Newer
Older
Import / web / www.xiaofrog.com / shell / webtty.js
/***********************************************************************
  This file is part of the webtty package. The webtty package was
  written by Martin Steen Nielsen
  Development is hosted by testape.com at www.testape.com/webtty
  webtty is free software; you can redistribute it and/or modify it 
  under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
***********************************************************************/

/***********************************************************************
   Resize terminal
 ***********************************************************************/

function resize(id)
{
    var elem = document.getElementById(id);
    elem.cols+=20;
    elem.rows+=6;
    if (elem.cols>20*4) {
        elem.cols-=(20*4);
        elem.rows-=(6*4);
    }
    elem.scrollTop=elem.scrollHeight;
    return false;
}


/***********************************************************************
   Toggles scrollbars on terminal
 ***********************************************************************/
function bars(id)
{
    var elem = document.getElementById(id);
    if (elem.style.overflow!='scroll')
        elem.style.overflow='scroll';
    else
        elem.style.overflow='hidden';
    return false;
}


/***********************************************************************
   closes terminal
 ***********************************************************************/
function wclose(id)
{
    var elem = document.getElementById(id);
    elem.style.display='none';
    return false;
}




function webtty()
{

    /***********************************************************************
      Creates XMLHTTPRequest object
     ***********************************************************************/
    this.create_transfer_object = function()
    {
        try { return new ActiveXObject('Msxml2.XMLHTTP');    } catch(e) {}
        try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
        try { return new XMLHttpRequest();                   } catch(e) {}
        return null;
    }

    /***********************************************************************
       callback function whenever html data is ready
     ***********************************************************************/
    this.get_html_state = function()
    {
        if (this.xhr_out.readyState == 4)
        {
            if ((this.xhr_out.status!=200)&&(this.xhr_out.status!=0))
                this.error();
            this.element.innerHTML = this.xhr_out.responseText;
            var tmp = document.getElementById('webtty_session_id');
            this.session_id = tmp.innerHTML;
            //document.getElementById('webtty_session_id').id='default';
            this.output_area = document.getElementById('webtty_output');
            this.output_area.id = 'default';
            var obj = this;
            this.output_area.onkeypress = function(e) { if (obj.running) return obj.key_event(e); }
            this.output_area.onkeydown  = function(e) { if (obj.running) return obj.special_key_event(e); }
            this.get_data();
        }
    }

    /***********************************************************************
      Request a new process and HTML data from server
     ***********************************************************************/
    this.get_html = function()
    {
        var obj = this;
        if (null == this.xhr_out)
            this.xhr_out     = this.create_transfer_object();
        this.xhr_out.onreadystatechange = function() {};
        this.xhr_out.open('GET', 'webtty.php?shell=virt', true);
        this.xhr_out.onreadystatechange = function() { if (obj.running) obj.get_html_state(); }
        this.xhr_out.send('');
    }


    /***********************************************************************
       callback function whenever process data is ready
     ***********************************************************************/
    this.get_data_state = function()
    {
        if (this.xhr_out.readyState == 4) {
            if ((this.xhr_out.status!=200)&&(this.xhr_out.status!=0))
                this.error();
            var data = this.xhr_out.responseText; 
            if (data == '_C_L_O_S_E_D_') {
                this.session_id=null;
                this.webtty_exit(0);
                this.cleanup();
            } else {
                if (data != '')
                    this.handle_data(data);
                this.get_data();
            }
        }
    }


    /***********************************************************************
       Request new data from the server
     ***********************************************************************/
    this.get_data = function()
    {
        if (null == this.session_id)
            return this.error();
        if (null == this.xhr_out)
            this.xhr_out     = this.create_transfer_object();
        var obj = this;
        this.xhr_out.onreadystatechange = function() { }
        this.xhr_out.open('GET', 'webtty.php?outp=1&id='+this.session_id, true);
        this.xhr_out.onreadystatechange = function() { return obj.get_data_state(); }
        this.xhr_out.send('');
    }


    /***********************************************************************
       exit page. Remove handles and terminate session
     ***********************************************************************/
    this.cleanup = function()
    {
        this.running     = false;
        if (this.xhr_kbd) {
            this.xhr_kbd.onreadystatechange = function() {};
            this.xhr_kbd.abort();
            delete this.xhr_kbd;
        }
        if (this.xhr_out) {
            this.xhr_out.onreadystatechange = function() {};
            this.xhr_out.abort();
            delete xhr_out;
        }
        if (null != this.session_id) {
            var exit_req = this.create_transfer_object();
            exit_req.open('GET', 'webtty.php?id='+this.session_id+'&shell=kill', true);
            exit_req.send('');
            delete exit_req;
            this.session_id=null;
        }
    }

    this.x = 0;
    this.y = 0;
    this.saveX = 0;
    this.saveY = 0;
    this.lines = new Array(25);
    for (i = 0; i < 25; i++ ) {
        this.lines[i] = new Array(80);
    }

    /***********************************************************************
       handles process data
     ***********************************************************************/
    this.handle_data = function(data)
    {
        var newLines = data.split("\n");
        
        for (j = 0; j < newLines.length; j++) {
            for (i = 0; i < newLines[j].length; i++) {
                if (newLines[j].charCodeAt(i) == 27) {
                    i++;
                    //i++;
                    var escaped = true;
                    while (escaped == true) { 
                        //i--;
                        if ( newLines[j].slice(i).match(/^\[K/) ) { // erase to EOL
                            for (z = this.x; z < 80; z++)
                                this.lines[this.y][z] = "";
                            i = i + 2;
                        } else if ( newLines[j].slice(i).match(/^\[1K/) ) { // erase to SOL
                            for (z = 0; z <= this.x; z++)
                                this.lines[this.y][z] = "";
                            i = i + 3;
                        } else if ( newLines[j].slice(i).match(/^\[2K/) ) { // erase line
                            this.lines[this.y] = new Array(80);
                            i = i + 3;
                        } else if ( newLines[j].slice(i).match(/^\[J/) ) { // erase down
                            for (z = this.y; z < 25; z++)
                                this.lines[z] = new Array(80);
                            i = i + 2;
                        } else if ( newLines[j].slice(i).match(/^\[1J/) ) { // erase up
                            for (z = 0; z <= this.y; z++)
                                this.lines[z] = new Array(80);
                            i = i + 3;
                        } else if ( newLines[j].slice(i).match(/^\[2J/) ) { // erase screen
                            for (z = 0; z < 25; z++ ) {
                                this.lines[z] = new Array(80);
                                this.x = 0;
                                this.y = 0;
                            }
                            i = i + 3;
                        } else if ( newLines[j].slice(i).match(/^\[r/) ) { // enable scrolling
                            i = i + 2; // XXX
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*\;[0-9]*r/) ) { // scroll
                            var m = newLines[j].slice(i).match(/^\[[0-9]*\;[0-9]*r/);
                            i += (m+"-").length - 1;   // XXX
                        } else if ( newLines[j].slice(i).match(/^D/) ) { // scroll down
                            var newLine = new Array(1);
                            newLine[0] = new Array(80);
                            this.lines = newLine.concat(this.lines).slice(0,25);
                            i++;
                        } else if ( newLines[j].slice(i).match(/^M/) ) { // scroll up
                            this.lines = this.lines.slice(1);
                            var newLine = new Array(1);
                            newLine[0] = new Array(80);
                            this.lines = this.lines.concat(newLine);
                            i++;
                        } else if ( newLines[j].slice(i).match(/^\[s/) ) { // save cursor pos
                            this.saveX = this.x;
                            this.saveY = this.y;
                            i = i + 2;
                        } else if ( newLines[j].slice(i).match(/^\[u/) ) { // unsave cursor pos
                            this.x = this.saveX;
                            this.y = this.saveY;
                            i = i + 2;
                        } else if ( newLines[j].slice(i).match(/^\[[Hf]/) ) { // move cursor home
                            i = i + 2;
                            this.x = 0;
                            this.y = 0;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*\;[0-9]*[Hf]/) ) { // move cursor
                            //alert( "text: -" + newLines[j].slice(i) + "-");
                            i++;
                            var x2 = 0;
                            var y2 = 0;
                            y2 = newLines[j].slice(i).match(/^[0-9]*/);
                            //if ( y2 ) {
                                i += (y2+"-").length;
                                x2 = newLines[j].slice(i).match(/^[0-9]*/);
                            //    if ( x2 ) {
                                    i += (x2+"-").length;
                                    //alert("Move To -" + x2 + "- -" + y2 + "-");
                                    this.x = x2;
                                    this.y = y2;
                            //    } else {
                            //        alert( "text: -" + newLines[j].slice(i) + "-");
                            //    }
                            //}
                        } else if ( newLines[j].slice(i).match(/^\[A/) ) {
                            i = i + 2;
                            this.y = this.y - 1;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*A/) ) { // cursor up
                            i++;
                            var y2 = 0;
                            y2 = newLines[j].slice(i).match(/^[0-9]*/);
                            i += (y2+"-").length;
                            this.y = this.y - y2;
                        } else if ( newLines[j].slice(i).match(/^\[B/) ) {
                            i = i + 2;
                            this.y = this.y + 1;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*B/) ) { // cursor up
                            i++;
                            var y2 = 0;
                            y2 = newLines[j].slice(i).match(/^[0-9]*/);
                            i += (y2+"-").length;
                            this.y = this.y + y2;
                        } else if ( newLines[j].slice(i).match(/^\[C/) ) {
                            i = i + 2;
                            this.x = this.x + 1;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*C/) ) { // cursor forward
                            i++;
                            var x2 = 0;
                            x2 = newLines[j].slice(i).match(/^[0-9]*/);
                            i += (x2+"-").length;
                            this.x = this.x + x2;
                        } else if ( newLines[j].slice(i).match(/^\[D/) ) {
                            i = i + 2;
                            this.x = this.x - 1;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*D/) ) { // cursor backward
                            i++;
                            var x2 = 0;
                            x2 = newLines[j].slice(i).match(/^[0-9]*/);
                            i += (x2+"-").length;
                            this.x = this.x - x2;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*m/) ) { // attrib
                            i++;
                            var x2 = 0;
                            x2 = newLines[j].slice(i).match(/^[0-9]*/);
                            i += (x2+"-").length; // XXX
                            /*
                            if ( x2 == 34 )
                                this.lines[this.y][this.x] = "<font color=#FF00FF />" + this.lines[this.y][this.x];
                            else if ( x2 == 35 )
                                this.lines[this.y][this.x] = "<font color=#FF00FF />" + this.lines[this.y][this.x];
                            else if ( x2 == 0 )
                                this.lines[this.y][this.x] = "</font>" + this.lines[this.y][this.x];
                            */
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*;[0-9]*m/) ) { // attrib
                            i++;
                            var x2 = 0;
                            x2 = newLines[j].slice(i).match(/^[0-9]*;[0-9]*/);
                            i += (x2+"-").length;
                        } else if ( newLines[j].slice(i).match(/^\[[0-9]*;[0-9]*;[0-9]*m/) ) { // attrib
                            i++;
                            var x2 = 0;
                            x2 = newLines[j].slice(i).match(/^[0-9]*;[0-9]*;[0-9]*/);
                            i += (x2+"-").length;
                        } else {
                            i--;
                            escaped = false;
                        }
                    }
                    
                    // alert("Escape vals -" + newLines[j][i] + newLines[j][i] + "-");
                } else {
                    this.lines[this.y][this.x] = newLines[j][i];
                    this.x++;
                    if ( this.x >= 80 ) {
                    //    this.x = 80;
                        this.x = 0;
                        this.y++;
                        if ( this.y >= 25 ) {
                            this.lines = this.lines.slice(1);
                            var newLine = new Array(1);
                            newLine[0] = new Array(80);
                            this.lines = this.lines.concat(newLine);
                            this.y--;
                        }
                    }
                }
            }
            if (j < (newLines.length - 1)) {
                this.y++;
                this.x = 0;
            }
            if ( this.y >= 25 ) {
                this.lines = this.lines.slice(1);
                var newLine = new Array(1);
                newLine[0] = new Array(80);
                this.lines = this.lines.concat(newLine);
                this.y--;
            }
        }
        
        //this.lines[this.lines.length-1] += outLines[0];
       
        //this.lines = this.lines.concat(outLines.slice(1));
        //this.lines = this.lines.slice(this.lines.length - 25);
        //this.ypos = this.lines.length; // this.ypos + 1;
   
        var output = "";
        for (i = 0; i < 25; i++) {
            output += this.lines[i].join("") + "\n";
        }
        
        this.output_area.value = output; // this.lines.join("\n");//data;//lines[0];//
        //this.output_area.value = this.handle_special_chars(this.output_area.value + data, 2*data.length);
        //this.output_area.value = this.handle_special_chars(this.output_area.value + data, 2*data.length);
        
        //this.output_area.scrollTop=this.output_area.scrollHeight;
    }

    /***********************************************************************
       Handle special characters. Simulates applying backspace, bell etc to 
       a string
     ***********************************************************************/
    this.handle_special_chars = function(streng, max_look_back)
    {
        var startpos=0;
        var write_pos=0; 
        var new_str='';
        var subs='';

        if (max_look_back>80)
            max_look_back=80;

        if (streng.length>max_look_back)
            startpos = streng.length-max_look_back;

        for (tl=startpos; tl<streng.length; tl++)
        {
            if (streng.charCodeAt(tl)==8) {
                write_pos--;
                subs = subs.substr(0,write_pos);
            } else if (streng.charCodeAt(tl)==27) {
                if (streng[tl+1]=="[") {
                    subs += "escaped";
                }
            } else if (streng.charCodeAt(tl)==7) {
                ;
            } else {
                write_pos++;
                subs += streng.charAt(tl);
            }
        }
        return streng.substr(0,startpos)+subs;
    }


    /***********************************************************************
       callback function whenever data has been transmitted
     ***********************************************************************/
    this.put_kbd_state = function()
    {
        if (this.xhr_kbd.readyState == 4)
        {
            delete this.xhr_kbd; 
            this.put_kbd();
        }
    }
	


    /***********************************************************************
       send data to server
     ***********************************************************************/
    this.put_kbd = function()
    {
        if ('' == this.kbd_buffer)
            return;

        this.xhr_kbd=this.create_transfer_object();
        this.xhr_kbd.open('POST', "webtty.php", true);
        var obj = this;
        this.xhr_kbd.onreadystatechange = function() { if (obj.running) return obj.put_kbd_state(); }
        this.xhr_kbd.setRequestHeader("Connection", "close")
        this.xhr_kbd.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        this.xhr_kbd.send("id="+this.session_id+"&inp="+this.kbd_buffer);
        this.kbd_buffer='';
    }


    /***********************************************************************
      buffer data and send to server
     ***********************************************************************/
    this.buffer_next_data = function(cmd)
    {
        this.kbd_buffer += cmd;
        if (undefined == this.xhr_kbd) 
            this.put_kbd();
        //this.xhr_kbd.send("id="+this.session_id+"&inp="+this.kbd_buffer);
    }



    /***********************************************************************
      handles normal keypresses
     ***********************************************************************/
    this.key_event = function(e)
    {
        if (!e) var e = window.event
            if (e.keyCode) key = e.keyCode;
            else if (e.which) key = e.which;

            if (!(window.event || e.charCode))
                return true;

            this.buffer_next_data(escape(String.fromCharCode(key)));
            return false; 
    }


    /***********************************************************************
       handles special keypresses
     ***********************************************************************/
    this.special_key_event = function (e)
    {
        if (!e) var e = window.event
            if (e.keyCode) key = e.keyCode;
            else if (e.which) key = e.which;

            if (key==9)
            { 
                this.buffer_next_data(escape("\t")); 
                return false; 
            }
            else if (key==8)
            { 
                this.buffer_next_data(escape("\010")); 
                return false; 
            }
            else if (key == 40)
            { 
                this.buffer_next_data(escape("\033[B")); 
                return false; 
            }
            else if (key == 38)
            { 
                this.buffer_next_data(escape("\033[A")); 
                return false; 
            }
            else if (key == 38)
            { 
                this.buffer_next_data(escape("\033[A")); 
                return false; 
            }
            else if (key == 13)
            { 
                this.buffer_next_data(escape("\n")); 
                return false; 
            }
    }

    /***********************************************************************
      handles errors
     ***********************************************************************/
    this.error = function()
    {
        this.webtty_exit(1);
        alert("Error");
        this.cleanup();
    }

    /***********************************************************************
      exit handler
     ***********************************************************************/
    this.webtty_exit = function(val)
    {
        alert("Refresh page to get a new virtual machine");
    }

    this.element = document.getElementById('dynamic');
    if (null == this.element)
        return this.error();
    this.running     = true;
    this.session_id  = null;
    this.output_area = null;
    this.kbd_buffer  = '';
    this.get_html();
}