Console button test source


The source used in the Console example. Links to these source files are: console_button_test.pl and test.js. Eventually test.js may be implemented using Prolog, but there are complications with having ProscriptLS run the HTML for an interpreter that evaluates ProscriptLS queries and keeping the two levels of ProscriptLS evaluation separated.

Javascript source for the 'terminal' is shown in the test.js file.

console_button_test.pl

/*
The Console Button Test uses console_button_test.html and console_button_test.pl to create a
web page for testing a dynamic Prolog console.
There is initially only a single button name 'Create Console' that when clicked renames that
button 'Remove Console' and creates a prolog 'console' DIV on that page (if it does not already exist)
in which there is a 'stdout' DIV in which the user can run Prolog queries.
Clicking the 'Remove Console' button renames the button 'Create Console' and removes the 'stdout' DIV.
*/

:- initialization(console_button_test).

console_button_test :-
    dom_element_attribute_value(E, id, simpletest),
    create_dom_element('BUTTON', Button),
    set_dom_element_attribute_value(Button, id, console_button),
    set_dom_element_attribute_value(Button, class, 'example-button'),
    create_dom_text_node("Create Console", NewContent),
    append_dom_node_child(ButtonNewContent),
    dom_object_method(Button, addEventListener(click, setup_div_and_console)),
    append_dom_node_child(EButton).

setup_div_and_console :-
  dom_element_attribute_value(Button, id, console_button),
  dom_object_property(_Button, innerText, Text),
  setup_div_and_console(TextButton).

setup_div_and_console("Create Console", Button:-
  set_dom_object_property(Button, innerText, "Remove Console"),
  setup_console_div,
  setup_console.

setup_div_and_console("Remove Console", Button:-
  set_dom_object_property(Button, innerText, "Create Console"),
  remove_console_div.

% <div style="border: 1px solid black; height: 50%; width: 100%; overflow: scroll;" id="stdout" onKeyPress="keypress(event)" onKeyDown="keydown(event)" tabindex="0"></div>
setup_console_div :-
  create_dom_element('DIV', Div),
  set_dom_element_attribute_value(Div, style, 'border: 1px solid black; height: 50%; width: 100%; overflow: scroll;'),
  set_dom_element_attribute_value(Div, id, stdout),
  set_dom_element_attribute_value(Div, onkeypress, 'keypress(event)'),
  set_dom_element_attribute_value(Div, onkeydown, 'keydown(event)'),
%  dom_element_add_event_listener(Div, keypress, eval_javascript("keypress(event)")),
%  dom_element_add_event_listener(Div, keydown, eval_javascript("keydown(event)")),
  set_dom_element_attribute_value(Div, tabindex, '0'),
  lookup_console_div(OuterDiv),
  append_dom_node_child(OuterDivDiv),
  dom_object_property(element, Body, tag, body),
  append_dom_node_child(BodyOuterDiv).

lookup_console_div(Div:-
  dom_element_attribute_value(Div, id, console)
    -> true
  ;
  create_dom_element('DIV', Div),
  set_dom_element_attribute_value(Div, id, console).

remove_console_div :-
  dom_element_attribute_value(Div, id, console),
  set_dom_object_property(Div, innerText, "").

setup_console :-
  dom_object_property(element, Body, tag, body),
  set_dom_element_attribute_value(Body, onkeydown, 'return preventBackspace(event);'),
%  dom_element_add_event_listener(Body, keydown, eval_javascript("preventBackspace(event);")),
  eval_javascript("onload();").

test.js

var x_history = [];
var h_ptr = -1;

var query = "";
debugging = false;
var output_console = null;
load_state();
call_directives();
initialize();
var can_backtrack = false;

function preventBackspace(e)
{
    if (e.keyCode === 8 && e.target === output_console)
    {
        e.preventDefault();
        e.stopPropagation();
        backspace();
        return false;
    }
    return true;
}

var stdout_buffer;

function predicate_flush_stdout()
{
    if (stdout_buffer && stdout_buffer.innerHTML !== "")
        stdout("\n");
    return true;
}

function stdout(msg)
{
    if(output_console) {
        output_console.removeChild(stdout_buffer);
        var lines = (stdout_buffer.innerHTML + msg).split('\n');
        for (var i = 0; i < lines.length - 1; i++) {
            debug(lines[i]);
        }
        stdout_buffer.innerHTML = lines[lines.length - 1];
        output_console.appendChild(stdout_buffer);
    } else {
        alert(msg);
    }
}

function onload(initialConsult)
{
    output_console = document.getElementById('stdout');    
    stdout_buffer = document.createElement('div');
    stdout_buffer.innerHTML = "";
    output_console.appendChild(stdout_buffer);
    if(initialConsult) {
        consult_for_test();
    }

    query_node = document.createElement('div');
    query_node.className = "query";
    query_node.innerHTML = "?-";
    output_console.appendChild(query_node);     
    scroll_to_bottom();
}

function debug(e) 
{
    if(output_console) {
        var newElement = document.createElement('div');
        newElement.innerHTML = '<div>' + e + '</div>';
        output_console.appendChild(newElement);
        scroll_to_bottom();
    }
    // else {
    //     alert(e);
    // }
}

function consult_for_test()
{
    let code_atom = document.getElementById('code').value;
    /* Reset the entire WAM */
    load_state();
    call_directives();
    initialize();
    let atom = lookup_atom(code_atom);
    let ftor = VAL(lookup_functor("wam_compiler:consult_atom", 1));
    allocate_first_frame();
    var pred = predicates[ftor];
    var pi = predicates[ftor].clause_keys[0];
    state.current_predicate = pred;
    code = pred.clauses[pi].code;
    state.P = 0;
    register[0] = atom;
    if (wam())
        debug("Buffer consulted");
    else
        debug("Failed to load buffer");
    // FIXME: This is not very good. At the very least setting state.B to 0 should reset state.H to 0 as well?
    state.B = 0;
    can_backtrack = false;
}

function backspace()
{
    if (query.length > 0)
    {
        query = query.substring(0, query.length - 1);
        query_node.innerHTML = "?-" + query;
        output_console.removeChild(query_node);
        output_console.appendChild(query_node);
        scroll_to_bottom();
    }
}

function keydown(e)
{
    if (e.keyCode === 38 && !can_backtrack)
    {
        e.preventDefault();
        e.stopPropagation();
        h_ptr++;
        if (h_ptr >= x_history.length)
        {
            h_ptr = x_history.length-1;
        }
        query = x_history[h_ptr];
        query_node.innerHTML = "?-" + query;
        output_console.removeChild(query_node);
        output_console.appendChild(query_node);
        scroll_to_bottom();
    }
    else if (e.keyCode === 40 && !can_backtrack)
    {
        e.preventDefault();
        e.stopPropagation();
        h_ptr--;
        if (h_ptr < 0)
        {
            h_ptr = -1;
            query = "";
        }
        else
            query = x_history[h_ptr];
        query_node.innerHTML = "?-" + query;
        output_console.removeChild(query_node);
        output_console.appendChild(query_node);
        scroll_to_bottom();        
    }
}

function keypress(e)
{
    var old_query;

    if (e.altKey || e.ctrlKey || e.metaKey)
        return;
    e.preventDefault();
    e.stopPropagation();
    if (e.keyCode === 8)
    {
        backspace();
    }
    else if (e.keyCode === 59 && can_backtrack)
    {
        old_query = document.createElement('div');
        old_query.innerHTML = ";";
        old_query.className = "old_query";
        output_console.appendChild(old_query);
        scroll_to_bottom();
        if (backtrack())
        {
            try_running();
        }
        else
        {
            stdout("false.\n");
            can_backtrack = false;
            query = "";
            query_node = document.createElement('div');
            query_node.className = "query";
            query_node.innerHTML = "?-";
            output_console.appendChild(query_node);  
            scroll_to_bottom();
        }
    }
    else if (e.keyCode === 13 && can_backtrack)
    {
        // Cut choicepoints (?)
        state.B = 0;
        can_backtrack = false;
        query = "";
        query_node = document.createElement('div');
        query_node.className = "query";
        query_node.innerHTML = "?-";
        output_console.appendChild(query_node);  
        scroll_to_bottom();
    }
    else if (e.keyCode === 13)
    {
        // call the toplevel handler
        // ARGH. MUST reset registers for new query, especially after failure!
        initialize();
        allocate_first_frame();        

        var ftor = VAL(lookup_functor("wam_compiler:repl", 1));
        var pred = predicates[ftor];
        var pi = predicates[ftor].clause_keys[0];
        state.current_predicate = pred;
        code = pred.clauses[pi].code;
        register[0] = lookup_atom(query);
        // Make the query a permanent part of the output        
        output_console.removeChild(query_node);
        old_query = document.createElement('div');
        old_query.innerHTML = "?-" + query;
        old_query.className = "old_query";
        output_console.appendChild(old_query);
        x_history.unshift(query);
        h_ptr = -1;
        try_running();
    }
    else
    {
        query += String.fromCharCode(e.keyCode);
        query_node.innerHTML = "?-" + query;
        output_console.removeChild(query_node);
        output_console.appendChild(query_node);
        scroll_to_bottom();
    }
}

function try_running()
{
    try
    {
        if (!wam())
        {
            stdout("false.\n");
        }
    }
    catch (anything)
    {
        console.log(anything);
        debug("Error. See javascript console");
    }
    if (state.B !== 0)
    {
        debug_msg("Can backtrack");
        can_backtrack = true;
    }
    else
    {
        debug_msg("No more solutions after this");
        can_backtrack = false;
        query = "";
        query_node = document.createElement('div');
        query_node.className = "query";
        query_node.innerHTML = "?-";
        output_console.appendChild(query_node);  
        scroll_to_bottom();
    }
}

function scroll_to_bottom()
{
    output_console.scrollTop = output_console.scrollHeight;    
}