<!doctype html>
<html lang="en-us">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>newLISP in a browser</title>
    <style type="text/css" media="screen">
    .emscripten {
        padding-right: 0; margin-left: auto; margin-right: auto; display: block;
        }
    .button {
        color: #000000;
        background-color: #B0B0B0;
        font-size: 1.0em;
        border: 0px;
        border-radius: 5px;
        padding-left: 6px;
        padding-right: 6px;
        margin-top: 3px;
        margin-bottom: 4px;
        }
    .blue-button {
        color: #0000AA;
        background-color: #B0B0B0;
        font-size: 1.0em;
        border: 0px;
        border-radius: 5px;
        padding-left: 6px;
        padding-right: 6px;
        margin-top: 3px;
        margin-bottom: 4px;
        }
    .red-button {
        color: #AA0000;
        background-color: #B0B0B0;
        font-size: 1.0em;
        border: 0px;
        border-radius: 5px;
        padding-left: 6px;
        padding-right: 6px;
        margin-top: 3px;
        margin-bottom: 4px;
        }
    .filesbutton {
        color: #000000;
        font-size: 0.1em;
        margin-bottom: 2px;
        opacity: 0.0;
        }
    .keyword {
        font-family: Helvetica, sans-serif;
        font-size: 1.0em;
        color: #000000;
        background-color: #E0E0E0;
        border: 2px solid #5080B0;
        border-radius: 5px;
        margin-right: 0px;
        }
    .dark-theme {
        color: #f0b060;
        background-color: #222223;
        cursor: cell;
        }
    .light-theme {
        color: #303060;
        background-color: #F8E8C8;
        }
    .white-theme {
        color: #000000;
        background-color: #FFFFFF;
        }
    .blue-theme {
        color: #DDDDDD;
        background-color: #102030;
        cursor: cell;
        }
    body, p {
        font-family: Helvetica, sans-serif;
        color: #C0C0C0;
        }
    body {
        overflow:hidden;
        background-color: #5080B0;
        margin: 0;
        padding: 0;
        }
    a {
        background-color: transparent;
        color: #c0c0c0;
        text-decoration: underline;
        }
    </style>
    <style title="layout">
    .left {
        height: 500px;
        }
    .left {
        margin-left: 1%;
        margin-bottom: 2px;
        float: left;
        }
    .right {
        height: 500px;
        }
    .right {
        margin-left: 1%;
        margin-bottom: 2px;
        float: left;
        }
    .upper {
        height: 240px;
        }
    .upper {
        margin-left: 1%;
        margin-bottom: 4px;
        }
    .lower {
        height: 240px;
        }
    .lower {
        margin-left: 1%;
        margin-bottom: 6px;
        }
    .menu {
        margin-top: 6px;
        margin-left: 1%;
        margin-bottom: 2px;
        }
    .lower-menu {
        margin-left: 1%;
        }
    textarea {
        font-family: Andale Mono, Monaco, Courier;
        font-size: 1.0em;
        border: 2px solid #C0C0C0;
        border-radius: 5px;
        width: 99%;
        height: 99%;
        resize: none;
        }
    </style>
    <script language="Javascript">
<!--
        // globalarray for width percentages editor and log in 3 layouts
        var LayoutWidth = [["48.5%", "48.5%"],["65%", "32%"], ["97%", "97%"], ["97%", "97%"]];
        var LayoutHeight = [[1.0, 1.0], [1.0, 1.0] ,[0.5, 0.5], [0.65, 0.35]];
        var LayoutClass = [["left", "right"],["left","right"], ["upper", "lower"], ["upper", "lower"]];
        var LayoutIdx = 0;

        // these two functions must be in all newLISP in a browser app
        function displayHTML(content) { 
              document.write(content); document.close(); 
              return content.length;
              }
        function displayHTMLnew(content) { 
              var newWindow = window.open('');
              newWindow.document.write(content); newWindow.document.close();
              return content.length;
              }

        // the following funcitons are specific to the editor IDE application
         var isMobile = {
            Android: function() { return navigator.userAgent.match(/Android/i);},
            iOS: function() { return navigator.userAgent.match(/iPhone|iPad|iPod/i);},
            Windows: function() { return navigator.userAgent.match(/IEMobile/i);},
            any: function() { return (isMobile.Android() || isMobile.iOS() || isMobile.Windows());}
            };
        
         function detectIPadOrientation () {
            resizeTextarea();
            window.scrollTo(0, 0);
            //if ( orientation == 0 ) { alert ('Portrait Mode, Home Button bottom');
            }

        function evaluateInput() {
            var result = newlispEvalStr(document.getElementById('input').value.trim());
            Module.print(result);
            }

        function evaluateInputSilent() {
            var result = newlispEvalStr('(silent)' + document.getElementById('input').value.trim());
            Module.print(result);
            }

        function evaluateInputJS() {
            var result = newlispEvalStr (
                '(eval-string-js {' + document.getElementById('input').value.trim() + '})' );
            Module.print(result);
            }

        function clearEditor() {
            document.getElementById('input').value = '';
            }

        function clearLog() {
            document.getElementById('output').value = ''
            }

        function editorSetFocus() {
            document.getElementById('input').focus();
            }

        function switchLayout() {
            LayoutIdx = (LayoutIdx += 1) % 4;
            resizeTextarea();
            if(!isMobile.any()) editor.focus();
            }

        function switchTheme() {
            var newTheme;

            switch(document.getElementById('input').className)
                {
                case "dark-theme":
                    newTheme = "light-theme"; break;
                case "light-theme":
                    newTheme = "white-theme"; break;
                case "white-theme":
                    newTheme = "blue-theme"; break;
                case "blue-theme":
                    newTheme = "dark-theme"; break;
                default:
                    newTheme = "white-theme";
                } 

            document.getElementById('input').className = newTheme;
            document.getElementById('output').className = newTheme;
            Module.print("# set theme: " + newTheme);
            }
 
        function flashBackground(duration) {
            document.body.style.background = "#8888CC";
            var myTimer = setInterval(function() {
                clearInterval(myTimer);
                document.body.style.background = "#5080B0";
                }, duration);
            }

        function restoreLayoutAndTheme() {
            var theme = localStorage.theme;

	        if(!isMobile.any()) {
                var allInputs = document.getElementsByTagName("input");
                for(var i = 0; i < allInputs.length; i++) {
                    allInputs[i].setAttribute('style', "font-size: 1.1em");
                    }
            	document.getElementById('keywordtext').setAttribute('size', '12');
		        }

            LayoutIdx = localStorage.layout;
            if(LayoutIdx != 0 && LayoutIdx != 1 && LayoutIdx  != 2 && LayoutIdx != 3)
                LayoutIdx = 0;
            resizeTextarea();

            if(!theme) { theme = "white-theme"; }
            document.getElementById('input').className = theme; 
            document.getElementById('output').className = theme;
            Module.print("# set theme: " + theme);

            var elem = document.getElementById("files");
            if(isMobile.any()) { elem.style.marginLeft = "-200px"; }
            else { elem.style.marginLeft = "0px"; }

            if(!isMobile.any()) editorSetFocus();
            }

        function resizeTextarea() {
            var styleSheet;
            var offset = 0;
            var vertical;

            // set width
            document.getElementById('editdiv').className = LayoutClass[LayoutIdx][0];
            document.getElementById('logdiv').className = LayoutClass[LayoutIdx][1];
            document.getElementById('editdiv').style.width = LayoutWidth[LayoutIdx][0];
            document.getElementById('logdiv').style.width = LayoutWidth[LayoutIdx][1];

            // set height
            for(var i = 0; i < document.styleSheets.length; i++) {
                var sheet = document.styleSheets[i];
                if(sheet.title == 'layout') { styleSheet = sheet; }
                }

    	    if(isMobile.any()) { offset = 16; }
            if(LayoutIdx < 2) { // left right layout
                vertical = window.innerHeight - 76 - offset;
                } else { // one column layout
                vertical = window.innerHeight - 84 - offset;
                } 

            var editHeight = vertical * LayoutHeight[LayoutIdx][0];
            var logHeight = vertical - editHeight;

            styleSheet.deleteRule(0); // left
            styleSheet.insertRule('.left { height: ' + editHeight + 'px;}' , 0);
            styleSheet.deleteRule(2); // right
            styleSheet.insertRule('.right { height: ' + editHeight + 'px;}' , 2);
            styleSheet.deleteRule(4); // upper
            styleSheet.insertRule('.upper { height: ' + editHeight + 'px;}' , 4);
            styleSheet.deleteRule(6); // lower
            styleSheet.insertRule('.lower { height: ' + logHeight + 'px;}' , 6);
            }

        function handleFileSelect(evt) {
            var files = evt.target.files; // FileList object
            var textFile = files[0];

            // file info:
            // textFile.name
            // textFile.type
            // textFile.size
            // textFile.lastModifiedDate[.toLocaleDateString()]

            //Only process text files.
            if(textFile.type.match('image.*')) {
                alert(textFile.type + 'is an invalid file type');
                return;
                }
 
            var reader = new FileReader();
 
            // read file asynchronous and fill editor box
            reader.onload = (function(theFile) {
                return function(e) {
                    var editBox = document.getElementById('input');
                    editBox.value = ''; // clear editbox
                    editBox.value = e.target.result;
                    };
                })(textFile);
 
            // Read in the text file
            reader.readAsText(textFile);
            Module.print('# loaded ' + textFile.name + ' -', textFile.size + ' bytes');
            editorSetFocus();
            }

        function GetSelectedText() {
            var selText = "";
            if (window.getSelection) {  // all browsers, except IE before version 9
                if (document.activeElement &&
                    document.activeElement.tagName.toLowerCase () == "textarea")
                    {
                    var text = document.activeElement.value;
                    selText = text.substring (document.activeElement.selectionStart,
                                              document.activeElement.selectionEnd);
                }
                else {
                    var selRange = window.getSelection ();
                    selText = selRange.toString ();
                    }
                }
                else {
                    if (document.selection.createRange) {       // Internet Explorer
                        var range = document.selection.createRange ();
                        selText = range.text;
                    }
                }
                if (selText !== "") {
                    document.getElementById('keywordtext').value = selText;
                    //alert(selText);
                }
        }
 
        function searchKeyPress(e) {
            // look for window.event in case event isn't passed in
            if(typeof e == 'undefined' && window.event) { e = window.event; }
            if(e.keyCode == 13) { document.getElementById('doc').click(); }
            }       

        function editorKeyDown(e) {
            var editor = document.getElementById('input');
            if(typeof e == 'undefined' && window.event) { e = window.event; }

            if(e.keyCode == 9) { // TAB key emits 4 spaces
                e.preventDefault();
                var oldCaret = getCaretPosition(editor);
                var newCaret = oldCaret + 4; 
                editor.value = editor.value.substring(0, oldCaret) + "    " + 
                               editor.value.substring(oldCaret, editor.value.length);
                editor.selectionStart = newCaret;
                editor.selectionEnd = newCaret;
                }
            }

        function editorKeyUp(e) {
            var editor = document.getElementById('input');
            if(typeof e == 'undefined' && window.event) { e = window.event; }
            //alert(e.shiftKey + ' ' + e.ctrlKey + ' ' + e.altKey + ' ' + e.keyCode + ' ' + e.charCode + ' ' + e.which);

            if(e.ctrlKey === true) {
                e.preventDefault();
                if(e.shiftKey === false && e.keyCode === 76) { // Ctrl-L  [x edit] 
                    document.getElementById('clearinput').click(); 
                    document.getElementById('keywordtext').value = '';
                    }
                if(e.shiftKey ===  true && e.keyCode === 76) { // Shift-Ctrl-L  [x edit] 
                    document.getElementById('clearoutput').click(); 
                    }
                if(e.altKey ===  true && e.keyCode === 76) { // Shift-Ctrl-L  [x edit] 
                    document.getElementById('clearinput').click(); 
                    document.getElementById('keywordtext').value = '';
                    document.getElementById('clearoutput').click(); 
                    }
                if(e.keyCode === 79) { // Ctrl-O [open]
                    document.getElementById('browse').click();
                    }
                if(e.keyCode === 83) { // Ctrl-S [save]
                    document.getElementById('save').click();
                    }
                }
            }

        function editorKeyPress(e) {
            var editor = document.getElementById('input');
            var closingPar = '';
            var openPar;
            //alert(e.shiftKey + ' ' + e.ctrlKey + ' ' + e.altKey + ' ' + e.keyCode + ' ' + e.charCode + ' ' + e.which);

            if(typeof e == 'undefined' && window.event) { e = window.event; }

            if(e.charCode == 41) 
                { closingPar = ')'; openPar = '('; }
            if(e.charCode == 125) 
                { closingPar = '}'; openPar = '{'; }

            // blink opening parenthesis on closing par
            if(closingPar == ')' || closingPar == '}') {
                var text = editor.value;
                var caret = getCaretPosition(editor);
                var balance = 1;
                for(var pos = caret - 1; pos >= 0; pos -= 1 ) {
                    if(text.charAt(pos) == openPar) balance -= 1;
                    if(text.charAt(pos) == closingPar) balance += 1;
                    if(balance === 0) {
                        editor.selectionStart = pos;
                        editor.selectionEnd = pos;
                        var myTimer = setTimeout(function() {
                            clearInterval(myTimer);
                            editor.value = text.substring(0, caret) + closingPar +
                                text.substring(caret, text.length);
                            editor.selectionStart = caret + 1;
                            editor.selectionEnd = caret + 1;
                            }, 300);
                        e.preventDefault();
                        break;
                        }
                    }
                }
            
            if(e.ctrlKey === true) {
                e.preventDefault();
                if(e.shiftKey === false && e.keyCode === 13) { // Ctrl-enter [eval]
                    document.getElementById('evalinput').click(); 
                    } 
                if(e.shiftKey === true && e.keyCode === 13) { // Shift-Ctrl-enter [silent]
                    document.getElementById('evalsilent').click();
                    }
                }
            }

        function sleep(ms){
            var startTime = new Date().getTime();
            while (new Date().getTime() < startTime + ms) {}
            }

        function getCaretPosition (field) {
            var caretPos = 0;
            // IE 9 and after
            if (document.selection) {
                field.focus ();
                var sel = document.selection.createRange ();
                sel.moveStart ('character', - field.value.length);
                caretPos = sel.text.length;
                }
            // Firefox, Safari, Chrome
            else if (field.selectionStart || field.selectionStart == '0') {
                caretPos = field.selectionStart; }
            return (caretPos);
            }

        function setCaretPosition(elem, caretPos) {
            if(elem !== null) {
                // IE 6 and after
                if(elem.createTextRange) {
                    var range = elem.createTextRange();
                    range.move('character', caretPos);
                    range.select();
                    }
                // Firefox, Safari, Chrome
                else {
                    if(elem.selectionStart) {
                        elem.focus();
                        elem.setSelectionRange(caretPos, caretPos);
                        }
                    else
                        elem.focus();
                    }
                }
            }

        function OpenDoc () {
            var selText = document.getElementById('keywordtext').value.trim();
             
            if(selText.charAt(selText.length - 1) == "?") {
                 selText = selText.substring(0, selText.length - 1) + 'p';
                }
            if(selText !== '') {
                //window.open('http://www.newlisp.org/downloads/newlisp_manual.html#'+selText,'MsgWindow');
                window.open('newlisp_manual.html#'+selText,'MsgWindow');
                }
            else {
                //window.open('http://www.newlisp.org/downloads/manual_frame.html','MsgWindow');
                window.open('manual_frame.html','MsgWindow');
                }
            }

        function saveEdit() {
            localStorage.userEdits = document.getElementById('input').value;
            localStorage.log = document.getElementById('output').value;
            localStorage.theme = document.getElementById('input').className;
            localStorage.layout = LayoutIdx;
            Module.print("# saved to local browser storage");
            if(!isMobile()) { editorSetFocus() };
            }

        function restoreEdit() {
            var log = document.getElementById('output');
            if(localStorage.log != null) {
                log.value = localStorage.log;
                }
            var content = document.getElementById('input');
            if(localStorage.userEdits != null) {
                content.value = localStorage.userEdits;
                if(content.value.length > 0)
                     { Module.print("# local browser storage recovered"); }
                else
                     { Module.print("# local browser storage is empty"); }
                }
            else { Module.print("# no local browser storage found"); }
            }

        function insertEdit(text) {
            document.getElementById('input').value = text;
            }
-->
    </script>
  </head>

  <body onmouseup="GetSelectedText();" onload="restoreEdit();">
    <!-- this hidden text area contains code which is preloaded into newLISP.
         The code is part of the tutorial application and is not required
         for a functioning editor IDE.
         Do not remove first and last out-commenting tags! -->
    <textarea id="code" style="display: none;">
; insert newLISP code for preload here
    (set (global 'display-html) (fn (x y)
        (replace {\} x {\\}) 
        (replace "\n" x "\\n") 
        (replace {'} x {\'})
        (replace {"} x {\"})
        (eval-string-js (if (true? y)
        (string {displayHTMLnew('} x {')})
        (string {displayHTML('} x {')})))))
; eof
    </textarea>

    <div class="emscripten" id="status">Downloading...</div>
    <div class="emscripten">
      <progress value="0" max="100" id="progress" hidden=1></progress> 
    </div>
    <div class="menu">
    <input type="button" value="eval" class="blue-button" id="evalinput" title="eval newLISP - Ctl-enter"
      onclick="evaluateInput();" />
    <input type="button" value="silent" class="blue-button" id="evalsilent" 
      title="eval silent - Shift-Ctrl-enter" onclick="evaluateInputSilent();" />
    <input type="button" value="JS" id="evalinputJS" class="blue-button" title="eval JavaScript"
      onclick="evaluateInputJS();" />
    <input type="button" value="html" id="evalinputHTML" class="blue-button" title="display HTML"
      onclick="displayHTMLnew(document.getElementById('input').value)" />
    <input type="button" value="x edit" id="clearinput" class="red-button" title="clear edit - Ctrl-L" 
      onclick="clearEditor();" />
    <input type="button" value="x log" class="red-button" id="clearoutput" title="clear log - Shift-Ctrl-L"
      onclick="clearLog();" />
    <input type="button" value="layout" id="switchlayout" class="button" title="change layout"
      onclick="switchLayout();" />
    <input type="button" value="theme" id="theme" class="button" title="change colors"
     onclick="switchTheme();" />
    <input type="button" value="info" id="info" class="button" title="about this program"
      onclick="window.open('README.html','MsgWindow');" />
    <input type="button" value="doc" id="doc" class="button" title="access documentation" 
      onclick="OpenDoc();"/>
    <input type="text" class="keyword" id="keywordtext" size="14" onkeypress="searchKeyPress(event);" />
    </div>
    <div class="two-column" id="editdiv">
    <textarea id="input" class="light-theme" spellcheck="false" onkeydown="editorKeyDown(event);"
        onkeypress="editorKeyPress(event);" onkeyup="editorKeyUp(event);"
         >; Welcome to newLISP running in a browser.
; based on newLISP v10.6.3 2015-05-14, Emscripten v1.29.0
; best used with FireFox browser
; Click [eval]

(println "Hello World")

(define (double x)
   (+ x x))

(println "=> " (double 123))

; [eval] shows all the return values from
; evaluated expressions plus the effect from
; 'println'. Now click [silent]. It only shows
; the effect from the 'println' expression.

; To find out more about how to use this 
; editor, click the [info] button.

; The best introduction to newLISP can be found
; here:
; en.wikibooks.org/wiki/Introduction_to_newLISP
;
; More documentation can be found here:
; www.newlisp.org/index.cgi?Documentation
; or by clicking the [doc] button.
</textarea>
    </div>

    <div class="two-column" id="logdiv">
    <textarea  id="output" class="light-theme" style="cursor:default;" readonly ></textarea>
    <center>
    <font size="-1">Copyright &copy; 2014-2015, <a href="http://newlisp.org">newlisp.org</a></font>
    </center>
    </div>
    <div class="lower-menu">
     <input type="button" value="save" id="save" class="button" title="save to browser storage - Ctrl-S"
      onclick="saveEdit();" />
     <input type="button" value="open" id="browse" class="red-button" title="open a disk file - Ctrl-O"
        onclick="document.getElementById('files').click();" />
     <input type="file" class="filesbutton" id="files" name="load" />
    </div>

    <script type='text/javascript'>
<!--
        // EMSCRIPTEN loading newlisp-js-lib.js showing progress of downloading
        // importing newlispEvalStr and output to the console
        var Module = {
          preRun: [],
          postRun: [(function() {
            newlispEvalStr = Module.cwrap('newlispEvalStr', 'string', ['string']); 
            // preload newLISP functions from code textarea id='code'
            newlispEvalStr(document.getElementById('code').value);
          })],
          print: (function() {
            var element = document.getElementById('output');
            element.value = ''; // clear browser cache
            return function(text) {
              text = Array.prototype.slice.call(arguments).join(' ');
              element.value += text + "\n";
              element.scrollTop = 99999; // focus on bottom
            };
          })(),
          printErr: function(text) {
            text = Array.prototype.slice.call(arguments).join(' ');
            console.log(text);
          },
          setStatus: function(text) {
            if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
            if (text === Module.setStatus.text) return;
            var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
            var now = Date.now();
            if (m && now - Date.now() < 30) return; // if progress update, skip it if too soon
            var statusElement = document.getElementById('status');
            var progressElement = document.getElementById('progress');
            if (m) {
              text = m[1];
              progressElement.value = parseInt(m[2])*100;
              progressElement.max = parseInt(m[4])*100;
              progressElement.hidden = false;
            } else {
              progressElement.value = null;
              progressElement.max = null;
              progressElement.hidden = true;

              // leave file selection button disabled on Android, IOS and mobile Windows
              if(isMobile.any()) {
                  document.getElementById('files').disabled = true;
                  document.getElementById('browse').disabled = true;
                  }

              document.getElementById('files').addEventListener('change', handleFileSelect, false);
              // restore editor layout settings
              restoreLayoutAndTheme();
              window.onresize = resizeTextarea;
              editorSetFocus();

              if(isMobile.any()) { 
                // settings for mobile discover orientation
                window.onorientationchange = detectIPadOrientation;
                // on mobile scroll page into position when keyboard closes
                document.addEventListener('focusout', function(e) {window.scrollTo(0, 0);});
                }

        // EMSCRIPTEN finish download status update code

            }
            statusElement.innerHTML = text;
          },
          totalDependencies: 0,
          monitorRunDependencies: function(left) {
            this.totalDependencies = Math.max(this.totalDependencies, left);
            Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) +
                          '/' + this.totalDependencies + ')' : 'All downloads complete.');
          }
        };
        Module.setStatus('Downloading...');
-->
      </script>

      <script async type="text/javascript" src="newlisp-js-lib.js"></script>
  </body>
</html>

<!-- eof -->