<!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 © 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 -->