Changeset 2038748
- Timestamp:
- 02/25/2019 11:02:46 AM (7 years ago)
- Location:
- easycoder/trunk
- Files:
-
- 2 added
- 11 edited
-
easycoder-min.js (modified) (1 diff)
-
easycoder.js (modified) (57 diffs)
-
easycoder.php (modified) (2 diffs)
-
ec-rest.txt (modified) (1 diff)
-
plugins-sample.js (modified) (2 diffs)
-
plugins/browser.js (modified) (38 diffs)
-
plugins/ckeditor.js (modified) (3 diffs)
-
plugins/gmap.js (added)
-
plugins/json.js (modified) (15 diffs)
-
plugins/showdown.js (added)
-
plugins/ui.js (modified) (3 diffs)
-
readme.txt (modified) (4 diffs)
-
rest.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
easycoder/trunk/easycoder-min.js
r2016526 r2038748 1 var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(d,h, g){d!=Array.prototype&&d!=Object.prototype&&(d[h]=g.value)};$jscomp.getGlobal=function(d){return"undefined"!=typeof window&&window===d?d:"undefined"!=typeof global&&null!=global?global:d};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_";1 var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(d,h,f){d!=Array.prototype&&d!=Object.prototype&&(d[h]=f.value)};$jscomp.getGlobal=function(d){return"undefined"!=typeof window&&window===d?d:"undefined"!=typeof global&&null!=global?global:d};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_"; 2 2 $jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var d=0;return function(h){return $jscomp.SYMBOL_PREFIX+(h||"")+d++}}(); 3 3 $jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var d=$jscomp.global.Symbol.iterator;d||(d=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[d]&&$jscomp.defineProperty(Array.prototype,d,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(d){var h=0;return $jscomp.iteratorPrototype(function(){return h<d.length?{done:!1,value:d[h++]}:{done:!0}})}; 4 4 $jscomp.iteratorPrototype=function(d){$jscomp.initSymbolIterator();d={next:d};d[$jscomp.global.Symbol.iterator]=function(){return this};return d};$jscomp.makeIterator=function(d){$jscomp.initSymbolIterator();var h=d[Symbol.iterator];return h?h.call(d):$jscomp.arrayIterator(d)};$jscomp.owns=function(d,h){return Object.prototype.hasOwnProperty.call(d,h)}; 5 $jscomp.assign="function"==typeof Object.assign?Object.assign:function(d,h){for(var g=1;g<arguments.length;g++){var e=arguments[g];if(e)for(var b in e)$jscomp.owns(e,b)&&(d[b]=e[b])}return d};$jscomp.polyfill=function(d,h,g,e){if(h){g=$jscomp.global;d=d.split(".");for(e=0;e<d.length-1;e++){var b=d[e];b in g||(g[b]={});g=g[b]}d=d[d.length-1];e=g[d];h=h(e);h!=e&&null!=h&&$jscomp.defineProperty(g,d,{configurable:!0,writable:!0,value:h})}};6 $jscomp.polyfill("Object.assign",function(d){return d||$jscomp.assign},"es6","es3");$jscomp.iteratorFromArray=function(d,h){$jscomp.initSymbolIterator();d instanceof String&&(d+="");var g=0,e={next:function(){if(g<d.length){var b=g++;return{value:h(b,d[b]),done:!1}}e.next=function(){return{done:!0,value:void 0}};return e.next()}};e[Symbol.iterator]=function(){return e};return e};7 $jscomp.polyfill("Array.prototype.keys",function(d){return d?d:function(){return $jscomp.iteratorFromArray(this,function(d){return d})}},"es6","es3");$jscomp.checkStringArgs=function(d,h, g){if(null==d)throw new TypeError("The 'this' value for String.prototype."+g+" must not be null or undefined");if(h instanceof RegExp)throw new TypeError("First argument to String.prototype."+g+" must not be a regular expression");return d+""};8 $jscomp.polyfill("String.prototype.endsWith",function(d){return d?d:function(d, g){var e=$jscomp.checkStringArgs(this,d,"endsWith");d+="";void 0===g&&(g=e.length);g=Math.max(0,Math.min(g|0,e.length));for(var b=d.length;0<b&&0<g;)if(e[--g]!=d[--b])return!1;return 0>=b}},"es6","es3");$jscomp.polyfill("Object.is",function(d){return d?d:function(d,g){return d===g?0!==d||1/d===1/g:d!==d&&g!==g}},"es6","es3");9 $jscomp.polyfill("Array.prototype.includes",function(d){return d?d:function(d, g){var e=this;e instanceof String&&(e=String(e));var b=e.length;g=g||0;for(0>g&&(g=Math.max(g+b,0));g<b;g++){var a=e[g];if(a===d||Object.is(a,d))return!0}return!1}},"es7","es3");$jscomp.polyfill("String.prototype.includes",function(d){return d?d:function(d,g){return-1!==$jscomp.checkStringArgs(this,d,"includes").indexOf(d,g||0)}},"es6","es3");5 $jscomp.assign="function"==typeof Object.assign?Object.assign:function(d,h){for(var f=1;f<arguments.length;f++){var e=arguments[f];if(e)for(var b in e)$jscomp.owns(e,b)&&(d[b]=e[b])}return d};$jscomp.polyfill=function(d,h,f,e){if(h){f=$jscomp.global;d=d.split(".");for(e=0;e<d.length-1;e++){var b=d[e];b in f||(f[b]={});f=f[b]}d=d[d.length-1];e=f[d];h=h(e);h!=e&&null!=h&&$jscomp.defineProperty(f,d,{configurable:!0,writable:!0,value:h})}}; 6 $jscomp.polyfill("Object.assign",function(d){return d||$jscomp.assign},"es6","es3");$jscomp.iteratorFromArray=function(d,h){$jscomp.initSymbolIterator();d instanceof String&&(d+="");var f=0,e={next:function(){if(f<d.length){var b=f++;return{value:h(b,d[b]),done:!1}}e.next=function(){return{done:!0,value:void 0}};return e.next()}};e[Symbol.iterator]=function(){return e};return e}; 7 $jscomp.polyfill("Array.prototype.keys",function(d){return d?d:function(){return $jscomp.iteratorFromArray(this,function(d){return d})}},"es6","es3");$jscomp.checkStringArgs=function(d,h,f){if(null==d)throw new TypeError("The 'this' value for String.prototype."+f+" must not be null or undefined");if(h instanceof RegExp)throw new TypeError("First argument to String.prototype."+f+" must not be a regular expression");return d+""}; 8 $jscomp.polyfill("String.prototype.endsWith",function(d){return d?d:function(d,f){var e=$jscomp.checkStringArgs(this,d,"endsWith");d+="";void 0===f&&(f=e.length);f=Math.max(0,Math.min(f|0,e.length));for(var b=d.length;0<b&&0<f;)if(e[--f]!=d[--b])return!1;return 0>=b}},"es6","es3");$jscomp.polyfill("Object.is",function(d){return d?d:function(d,f){return d===f?0!==d||1/d===1/f:d!==d&&f!==f}},"es6","es3"); 9 $jscomp.polyfill("Array.prototype.includes",function(d){return d?d:function(d,f){var e=this;e instanceof String&&(e=String(e));var b=e.length;f=f||0;for(0>f&&(f=Math.max(f+b,0));f<b;f++){var a=e[f];if(a===d||Object.is(a,d))return!0}return!1}},"es7","es3");$jscomp.polyfill("String.prototype.includes",function(d){return d?d:function(d,f){return-1!==$jscomp.checkStringArgs(this,d,"includes").indexOf(d,f||0)}},"es6","es3"); 10 10 $jscomp.polyfill("Math.trunc",function(d){return d?d:function(d){d=Number(d);if(isNaN(d)||Infinity===d||-Infinity===d||0===d)return d;var h=Math.floor(Math.abs(d));return 0>d?-h:h}},"es6","es3");$jscomp.polyfill("Number.isFinite",function(d){return d?d:function(d){return"number"!==typeof d?!1:!isNaN(d)&&Infinity!==d&&-Infinity!==d}},"es6","es3");$jscomp.polyfill("Number.isInteger",function(d){return d?d:function(d){return Number.isFinite(d)?d===Math.floor(d):!1}},"es6","es3"); 11 $jscomp.polyfill("String.prototype.repeat",function(d){return d?d:function(d){var g=$jscomp.checkStringArgs(this,null,"repeat");if(0>d||1342177279<d)throw new RangeError("Invalid count value");d|=0;for(var e="";d;)if(d&1&&(e+=g),d>>>=1)g+=g;return e}},"es6","es3");$jscomp.stringPadding=function(d,h){d=void 0!==d?String(d):" ";return 0<h&&d?d.repeat(Math.ceil(h/d.length)).substring(0,h):""}; 12 $jscomp.polyfill("String.prototype.padStart",function(d){return d?d:function(d,g){var e=$jscomp.checkStringArgs(this,null,"padStart");return $jscomp.stringPadding(g,d-e.length)+e}},"es8","es3"); 13 (function(){function d(h,g,e){function b(c,p){if(!g[c]){if(!h[c]){var l="function"==typeof require&&require;if(!p&&l)return l(c,!0);if(a)return a(c,!0);p=Error("Cannot find module '"+c+"'");throw p.code="MODULE_NOT_FOUND",p;}p=g[c]={exports:{}};h[c][0].call(p.exports,function(a){return b(h[c][1][a]||a)},p,p.exports,d,h,g,e)}return g[c].exports}for(var a="function"==typeof require&&require,c=0;c<e.length;c++)b(e[c]);return b}return d})()({1:[function(d,h,g){var e=d("./easycoder/Main");e.timestamp= 14 Date.now();console.log("EasyCoder loaded; waiting for page");window.onload=function(){console.log(Date.now()-e.timestamp+" ms: Page loaded; reset timer & start EasyCoder");e.timestamp=Date.now();e.scripts={};window.EasyCoder=e;var b=document.getElementById("easycoder-script");if(b){b.style.display="none";try{e.start(b.innerText)}catch(a){e.reportError(a)}}}},{"./easycoder/Main":6}],2:[function(d,h,g){h.exports=function(d,b,a){b=d.value.evaluate(d,b);d=d.value.evaluate(d,a);a=b.content;var c=d.content; 15 b.numeric&&!d.numeric&&(a=a.toString());!b.numeric&&d.numeric&&(c=c.toString());return a>c?1:a<c?-1:0}},{}],3:[function(d,h,g){var e=Object.assign||function(a){for(var c=1;c<arguments.length;c++){var b=arguments[c],d;for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(a[d]=b[d])}return a},b=this,a={getTokens:function(){return b.tokens},addWarning:function(a){b.warnings.push(a)},warning:function(c){a.addWarning(c)},unrecognisedSymbol:function(c){a.addWarning("Unrecognised symbol '"+c+"'")},getWarnings:function(){return b.warnings}, 16 getIndex:function(){return b.index},next:function(a){b.index+=void 0===a?1:a},peek:function(){return b.tokens[b.index+1].token},getToken:function(){return b.index>=b.tokens.length?null:b.tokens[b.index]?b.tokens[b.index].token:null},nextToken:function(){a.next();return a.getToken()},tokenIs:function(a){return b.index>=b.tokens.length?!1:a===b.tokens[b.index].token},nextTokenIs:function(c){a.next();return a.tokenIs(c)},skip:function(c){if(b.index>=b.tokens.length)return null;a.next();a.tokenIs(c)&& 17 a.next()},prev:function(){b.index--},getLino:function(){return b.index>=b.tokens.length?0:b.tokens[b.index].lino},getTarget:function(a){a=void 0===a?b.index:a;return b.tokens[a].token},getTargetPc:function(c){c=void 0===c?b.index:c;return b.symbols[a.getTarget(c)].pc},getCommandAt:function(a){return b.program[a]},isSymbol:function(c){c=void 0===c?!1:c;if(a.getTarget()in b.symbols)return!0;if(c)throw Error("Unknown symbol: '"+a.getTarget()+"'");return!1},nextIsSymbol:function(c){c=void 0===c?!1:c; 18 a.next();return a.isSymbol(c)},getSymbol:function(){return b.symbols[a.getToken()]},getSymbolPc:function(){return a.getSymbol().pc},getSymbolRecord:function(){return b.program[a.getSymbolPc()]},getSymbols:function(){return b.symbols},getProgram:function(){return b.program},getPc:function(){return b.program.length},getValue:function(){return a.value.compile(a)},getNextValue:function(){a.next();return a.getValue()},getCondition:function(){return a.condition.compile(a)},constant:function(c,b){return a.value.constant(c, 19 void 0===b?!1:b)},addCommand:function(a){b.program.push(e({pc:b.program.length},a))},addSymbol:function(a,l){b.symbols[a]={pc:l}},mark:function(){b.savedMark=b.index},rewind:function(){b.index=b.savedMark},completeHandler:function(){var c=a.getLino(),b=a.getPc();a.addCommand({domain:"core",keyword:"goto",lino:c,goto:0});a.compileOne();a.addCommand({domain:"core",keyword:"stop",lino:c,next:0});a.getCommandAt(b).goto=a.getPc();return!0},compileVariable:function(c,l,d,f){d=void 0===d?!1:d;f=void 0=== 20 f?null:f;a.next();var p=a.getLino(),m=a.getTokens()[a.getIndex()];if(b.symbols[m.token])throw Error("Duplicate variable name '"+m.token+"'");var n=a.getPc();a.next();a.addSymbol(m.token,n);c={domain:c,keyword:l,lino:p,isSymbol:!0,isValueHolder:d,name:m.token,elements:1,index:0,value:[{}],element:[],extra:f};"dom"===f&&(c.element=[]);a.addCommand(c);return c},compileToken:function(){var c=a.getToken();if(c){a.mark();for(var b=$jscomp.makeIterator(Object.keys(a.domain)),d=b.next();!d.done;d=b.next()){if((d= 21 a.domain[d.value])&&(d=d.getHandler(c))&&d.compile(a))return;a.rewind()}console.log("No handler found");throw Error("I don't understand '"+c+"...'");}},compileOne:function(){var c=a.getToken();if(c){b.warnings=[];var l=b.program.length;if(c.endsWith(":")){c=c.substring(0,c.length-1);if(b.symbols[c])throw Error("Duplicate symbol: '"+c+"'");b.symbols[c]={pc:l};b.index++}else a.compileToken()}},compileFromHere:function(c){for(;b.index<b.tokens.length;){var l=b.tokens[b.index].token;if("else"===l)return b.program; 22 a.compileOne();if(-1<c.indexOf(l))break}},compile:function(c){b.tokens=c;b.index=0;b.program=[];b.symbols={};b.warnings=[];a.compileFromHere([]);a.addCommand({domain:"core",keyword:"stop",lino:a.getLino(),next:0});return b.program}};h.exports=a},{}],4:[function(d,h,g){var e=Object.assign||function(b){for(var a=1;a<arguments.length;a++){var c=arguments[a],l;for(l in c)Object.prototype.hasOwnProperty.call(c,l)&&(b[l]=c[l])}return b};h.exports={compile:function(b){b.mark();for(var a=$jscomp.makeIterator(Object.keys(b.domain)), 23 c=a.next();!c.done;c=a.next()){if(c=b.domain[c.value].condition.compile(b))return e({domain:name},c);b.rewind()}},test:function(b,a){return b.domain[a.domain].condition.test(b,a)}}},{}],5:[function(d,h,g){var e=this,b={Add:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("to"))if(a.next(),a.isSymbol()){var d=a.getSymbol();if(a.getCommandAt(d.pc).isValueHolder){if("giving"===a.peek()){d=a.getValue();a.next();var f=a.getToken();a.next();a.addCommand({domain:"core",keyword:"add", 24 lino:c,value1:b,value2:d,target:f})}else d=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"add",lino:c,value1:b,target:d});return!0}a.warning("core "+e.name+": Expected value holder")}else{d=a.getValue();if(a.tokenIs("giving"))return a.next(),f=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"add",lino:c,value1:b,value2:d,target:f}),!0;a.warning("core "+e.name+': Expected "giving"')}return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,f=a.getSymbolRecord(c.target);if(f.isValueHolder){var e= 25 f.value[f.index];d?(a=a.getValue(d)+a.getValue(b),f.value[f.index]={type:"constant",numeric:!0,content:a}):(e.numeric||a.nonNumericValueError(c.lino),a=e.content+a.getValue(b),f.value[f.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,f.name);return c.pc+1}},Alias:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getToken();a.next();if(a.tokenIs("to")&&(a.next(),a.isSymbol())){var d=a.getToken();a.next();a.addCommand({domain:"core", 26 keyword:"alias",lino:c,alias:b,symbol:d});return!0}}return!1},run:function(a){var c=a[a.pc],b=a.symbols[c.alias].pc,d=a[b],f=a.getSymbolRecord(c.symbol);a[b]={pc:d.pc,domain:f.domain,keyword:f.keyword,lino:d.lino,name:d.name,alias:c.symbol};return c.pc+1}},Begin:{compile:function(a){a.next();a.compileFromHere(["end"]);return!0},run:function(a){return a[a.pc].pc+1}},Clear:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"clear", 27 lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol);b.isValueHolder?(a.domain[b.domain].value.put(b,{type:"boolean",content:!1}),c.numeric=!1):a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},Close:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var b=a.getSymbolRecord();if("module"===b.keyword)return a.next(),a.addCommand({domain:"core",keyword:"close",lino:c,module:b.name}),!0}return!1},run:function(a){var c=a[a.pc];a=a.getSymbolRecord(c.module).program; 28 a.run(a.onClose);return c.pc+1}},Debug:{compile:function(a){var c=a.getLino();if(a.nextTokenIs("program")){a.next();if(["item","pc"].includes(a.getToken())){var b=a.getNextValue();a.addCommand({domain:"core",keyword:"debug",lino:c,item:b});return!0}a.addCommand({domain:"core",keyword:"debug",lino:c,item:-1});return!0}return a.tokenIs("symbols")?(a.next(),a.addCommand({domain:"core",keyword:"debug",lino:c,item:"symbols"}),!0):a.tokenIs("symbol")?(b=a.nextToken(),a.next(),a.addCommand({domain:"core", 29 keyword:"debug",lino:c,item:"symbol",name:b}),!0):a.tokenIs("step")?(a.next(),a.addCommand({domain:"core",keyword:"debug",lino:c,item:"step"}),!0):!1},run:function(a){var c=a[a.pc],b=c.item;switch(b){case "symbols":console.log("Symbols: "+JSON.stringify(a.symbols,null,2));break;case "symbol":a=a.getSymbolRecord(c.name);b=a.exporter;delete a.exporter;console.log("Symbol: "+JSON.stringify(a,null,2));a.exporter=b;break;case "step":a.debugStep=!0;break;default:0<=b.content?console.log("Debug item "+b.content+ 30 ": "+JSON.stringify(a[b.content],null,2)):console.log("Debug program: "+JSON.stringify(a,null,2))}return c.pc+1}},Decode:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"decode",lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol);if(b.isValueHolder){var d=a.getValue(b.value[b.index]);b.value[b.index]={type:"constant",numeric:!1,content:a.decode(d)};c.numeric=!1}else a.VariableDoesNotHoldAValueError(c.lino, 31 b.name);return c.pc+1}},Divide:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var b=a.getSymbol();var d=a.getCommandAt(b.pc).name}b=a.getValue();a.tokenIs("by")&&a.next();var f=a.getValue();if(a.tokenIs("giving")){a.next();if(a.isSymbol())return d=a.getSymbol(),d=a.getCommandAt(d.pc).name,a.next(),a.addCommand({domain:"core",keyword:"divide",lino:c,value1:b,value2:f,target:d}),!0;a.warning("core "+e.name+": Expected value holder")}else return void 0===d&&a.warning("core "+e.name+": No target variable given"), 32 a.addCommand({domain:"core",keyword:"divide",lino:c,value2:f,target:d}),!0;return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,f=a.getSymbolRecord(c.target);if(f.isValueHolder){var e=f.value[f.index];b?(a=a.getValue(b)/a.getValue(d),f.value[f.index]={type:"constant",numeric:!0,content:Math.trunc(a)}):(e.numeric||a.nonNumericValueError(c,lino),a=e.content/a.getValue(d),f.value[f.index]={type:"constant",numeric:!0,content:Math.trunc(a)})}else a.VariableDoesNotHoldAValueError(c.lino,f.name); 33 return c.pc+1}},Encode:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"encode",lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol);if(b.isValueHolder){var d=a.getValue(b.value[b.index]);b.value[b.index]={type:"constant",numeric:!1,content:a.encode(d)};c.numeric=!1}else a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},End:{compile:function(a){a.next();return!0}, 34 run:function(){return 0}},Fork:{compile:function(a){var c=a.getLino();a.next();a.tokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"fork",lino:c,label:b});return!0},run:function(a){var c=a[a.pc];try{a.run(a.symbols[c.label].pc)}catch(l){console.log(l.message),alert(l.message)}return c.pc+1}},Go:{compile:function(a){var c=a.getLino();a.next();a.tokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"go",lino:c,label:b});return!0}, 35 run:function(a){var c=a[a.pc];if(c.label&&a.verifySymbol(c.label)){var b=a.symbols[c.label];if(b)return b.pc;a.runtimeError(c.lino,"Unknown symbol '"+c.label+"'");return 0}return c.goto}},Gosub:{compile:function(a){var c=a.getLino();a.next();a.tokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"gosub",lino:c,label:b});return!0},run:function(a){var c=a[a.pc];if(a.verifySymbol(c.label))return a.stack.push(a.pc+1),a.symbols[c.label].pc;a.runtimeError(c.lino,"Unknown symbol '"+ 36 c.label+"'");return 0}},If:{compile:function(a){var c=a.getLino();a.next();var b=a.condition.compile(a),d=a.getPc();a.addCommand({domain:"core",keyword:"if",lino:c,condition:b});a.compileOne();if(!a.getToken())return a.getCommandAt(d).else=a.getPc(),!0;a.tokenIs("else")?(c=a.getPc(),a.addCommand({domain:"core",keyword:"goto",goto:0}),a.getCommandAt(d).else=a.getPc(),a.next(),a.compileOne(!0),a.getCommandAt(c).goto=a.getPc()):a.getCommandAt(d).else=a.getPc();return!0},run:function(a){var c=a[a.pc]; 37 return a.condition.test(a,c.condition)?c.pc+1:c.else}},Import:{compile:function(a){var c=a.imports,b=a.getProgram();c=$jscomp.makeIterator(c);for(var d=c.next();!d.done;d=c.next()){d=d.value;var f=a.nextToken(),e=d.keyword;if(f===e){if(f=a.compileVariable(d.domain,e,!0),f=b[a.getSymbols()[f.name].pc],f.element=d.element,f.exporter=d.exporter,f.exportedName=d.name,f.extra=d.extra,f.imported=!0,!a.tokenIs("and"))break}else throw Error("Mismatched import variable type for '"+d.name+"'");}return!0},run:function(a){return a[a.pc].pc+ 38 1}},Index:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol(!0)){var b=a.getToken();if(a.nextTokenIs("to")){var d=a.getNextValue();a.addCommand({domain:"core",keyword:"index",lino:c,symbol:b,value:d});return!0}}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol),d=a.getValue(c.value);d>=b.elements&&a.runtimeError(c.lino,"Array index "+d+" is out of range for '"+b.name+"'");b.index=d;b.imported&&(b.exporter.getSymbolRecord(b.exportedName).index=d);return c.pc+1}},Load:{compile:function(a){var c= 39 a.getLino();a.next();if(a.tokenIs("plugin")){a.next();var b=a.getValue();a.addCommand({domain:"core",keyword:"load",lino:c,name:b});return!0}},run:function(a){var c=a[a.pc],b=a.getValue(c.name);if(a.checkPlugin(b))return c.pc+1;EasyCoder_Plugins.getLocalPlugin(a.getPluginsPath,b,a.getPlugin,a.addLocalPlugin,function(){a.run(c.pc+1)});return 0}},Module:{compile:function(a){a.compileVariable("core","module");return!0},run:function(a){a=a[a.pc];a.program=null;return a.pc+1}},Multiply:{compile:function(a){var c= 40 a.getLino();a.next();if(a.isSymbol()){var b=a.getSymbol();var d=a.getCommandAt(b.pc).name}b=a.getValue();a.tokenIs("by")&&a.next();var f=a.getValue();if(a.tokenIs("giving")){a.next();if(a.isSymbol())return d=a.getSymbol(),d=a.getCommandAt(d.pc).name,a.next(),a.addCommand({domain:"core",keyword:"multiply",lino:c,value1:b,value2:f,target:d}),!0;a.warning("core multiply: Expected value holder")}else return void 0===d&&a.warning("core multiply: No target variable given"),a.addCommand({domain:"core",keyword:"multiply", 41 lino:c,value2:f,target:d}),!0;return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,f=a.getSymbolRecord(c.target);if(f.isValueHolder){var e=f.value[f.index];b?(a=a.getValue(b)*a.getValue(d),f.value[f.index]={type:"constant",numeric:!0,content:a}):(e.numeric||a.nonNumericValueError(c,lino),a=e.content*a.getValue(d),f.value[f.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,f.name);return c.pc+1}},Negate:{compile:function(a){var c=a.getLino();a.next(); 42 if(a.isSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"negate",lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol);b.isValueHolder?b.value[b.index]={type:"constant",numeric:!0,content:-b.value[b.index].content}:a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},On:{compile:function(a){var c=a.getLino(),b=a.nextToken();a.next();switch(b){case "trigger":case "close":case "restore":return a.addCommand({domain:"core", 43 keyword:"on",lino:c,action:b}),a.completeHandler()}return!1},run:function(a){var c=a[a.pc];switch(c.action){case "trigger":a.onTrigger=c.pc+2;break;case "close":a.onClose=c.pc+2;break;case "restore":a.onRestore=c.pc+2;break;default:return a.runtimeError(c.lino,"Unknown action '"+c.action+"'"),0}return c.pc+1}},Print:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();a.addCommand({domain:"core",keyword:"print",lino:c,value:b});return!0},run:function(a){var c=a[a.pc];a=a.getFormattedValue(c.value); 44 console.log("-> "+a);return c.pc+1}},Put:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("into")){a.next();if(a.isSymbol()){var d=a.getToken();a.next();a.addCommand({domain:"core",keyword:"put",lino:c,value:b,target:d});return!0}a.warning("core:put: No such variable: '"+a.getToken()+"'")}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.target);b.isValueHolder||a.variableDoesNotHoldAValueError(c.lino,b.name);a=a.evaluate(c.value);b.value[b.index]=a;b.imported&& 45 (b=b.exporter.getSymbolRecord(b.exportedName),b.value[b.index]=a);return c.pc+1}},Replace:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("with")){a.next();var d=a.getValue();if(a.tokenIs("in")&&(a.next(),a.isSymbol())){var f=a.getSymbolRecord();a.next();if(f.isValueHolder)return a.addCommand({domain:"core",keyword:"replace",lino:c,original:b,replacement:d,target:f.name}),!0}}return!1},run:function(a){var c=a[a.pc],b=a.getValue(c.original).split("\\r").join("\r").split("\\n").join("\n"), 46 d=a.getValue(c.replacement),f=a.getSymbolRecord(c.target);a=a.getValue(f.value[f.index]).split(b).join(d);f.value[f.index]={type:"constant",numeric:!1,content:a};return c.pc+1}},Return:{compile:function(a){var c=a.getLino();a.next();a.addCommand({domain:"core",keyword:"return",lino:c});return!0},run:function(a){return a.stack.pop()}},Run:{compile:function(a){var c=a.getLino(),b=a.getNextValue(),d=[];if(a.tokenIs("with"))for(;;)if(a.nextIsSymbol(!0)){var f=a.getSymbolRecord();f.exporter=a.getProgram(); 47 d.push(f);a.next();if(!a.tokenIs("and"))break}if(a.tokenIs("as")&&a.nextIsSymbol(!0)){f=a.getSymbolRecord();a.next();if("module"!==f.keyword)throw Error("'"+f.name+"' is not a module");var e=f.name}a.addCommand({domain:"core",keyword:"run",lino:c,script:b,imports:d,module:e});return!0},run:function(a){a.nextPc=a.pc+1;a.runScript(a);return 0}},Script:{compile:function(a){var c=a.getLino(),b=a.nextToken();a.next();a.addCommand({domain:"core",keyword:"script",lino:c,name:b});return!0},run:function(a){var c= 48 a[a.pc];a.script=c.name;EasyCoder.scripts[c.name]=a;console.log(Date.now()-EasyCoder.timestamp+" ms: Script: "+c.name);return c.pc+1}},Set:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getSymbolRecord();if(!b.isValueHolder)return!1;a.next();if(a.tokenIs("to")){a.next();for(var d=[];;){a.mark();try{d.push(a.getValue())}catch(f){a.rewind();break}}a.addCommand({domain:"core",keyword:"set",lino:c,type:"setArray",target:b.name,value:d});return!0}a.addCommand({domain:"core",keyword:"set", 49 lino:c,type:"setBoolean",target:b.name});return!0}if(a.tokenIs("ready"))return a.next(),a.addCommand({domain:"core",keyword:"set",lino:c,type:"setReady"}),!0;a.tokenIs("the")&&a.next();if(a.tokenIs("elements")&&(a.next(),a.tokenIs("of"))){a.next();if(!a.isSymbol())throw Error("Unknown variable '"+a.getToken()+"'");b=a.getToken();a.next();if(a.tokenIs("to"))return a.next(),d=a.getValue(),a.addCommand({domain:"core",keyword:"set",lino:c,type:"setElements",symbol:b,value:d}),!0}if(a.tokenIs("encoding")){a.next(); 50 if(a.tokenIs("to"))return a.next(),b=a.getValue(),a.addCommand({domain:"core",keyword:"set",type:"encoding",lino:c,encoding:b}),!0;a.addWarning("Unknown encoding option")}return!1},run:function(a){var c=a[a.pc];switch(c.type){case "setBoolean":var b=a.getSymbolRecord(c.target);b.isValueHolder?(b.value[b.index]={type:"boolean",content:!0},c.numeric=!1):a.VariableDoesNotHoldAValueError(c.lino,b.name);break;case "setReady":a.parent.run(a.parent.nextPc);break;case "setElements":b=a.getSymbolRecord(c.symbol); 51 b.elements=a.getValue(c.value);b.index=0;b.value=[];for(a=0;a<b.elements;a++)b.value.push({}),b.element.push("");break;case "setArray":a=a.getSymbolRecord(c.target);a.elements=c.value.length;a.value=c.value;break;case "encoding":a.encoding=a.getValue(c.encoding)}return c.pc+1}},Stop:{compile:function(a){var c=a.getLino();a.next();a.addCommand({domain:"core",keyword:"stop",lino:c,next:0});return!0},run:function(){return 0}},Take:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("from"))if(a.next(), 52 a.isSymbol()){var d=a.getSymbol();if(a.getCommandAt(d.pc).isValueHolder){if("giving"===a.peek()){d=a.getValue();a.next();var f=a.getToken();a.next();a.addCommand({domain:"core",keyword:"take",lino:c,value1:b,value2:d,target:f})}else d=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"take",lino:c,value1:b,target:d});return!0}a.warning("core "+e.name+": Expected value holder")}else{d=a.getValue();if(a.tokenIs("giving"))return a.next(),f=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"take", 53 lino:c,value1:b,value2:d,target:f}),!0;a.warning("core "+e.name+': Expected "giving"')}return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,f=a.getSymbolRecord(c.target);if(f.isValueHolder){var e=f.value[f.index];d?(a=a.getValue(d)-a.getValue(b),f.value[f.index]={type:"constant",numeric:!0,content:a}):(e.numeric||a.nonNumericValueError(c,lino),a=a.getValue(e)-a.getValue(b),f.value[f.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,f.name);return c.pc+ 54 1}},Toggle:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getSymbolPc();a.next();a.addCommand({domain:"core",keyword:"toggle",lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a[c.symbol];if(b.isValueHolder){var d=a.domain[b.domain];a=d.value.get(a,b.value[b.index]).content;d.value.put(b,{type:"boolean",content:!a})}else a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},Trigger:{compile:function(a){var c=a.getLino();a.next();return a.tokenIs("parent")? 55 (a.next(),a.addCommand({domain:"core",keyword:"trigger",lino:c,module:"parent"}),!0):a.isSymbol()&&(moduleRecord=a.getSymbolRecord(),a.next(),"module"===moduleRecord.keyword)?(h=moduleRecord.name,a.addCommand({domain:"core",keyword:"trigger",lino:c,module:h}),!0):!1},run:function(a){var c=a[a.pc];if("parent"===c.module)a.parent&&a.parent.trigger(!0);else{var b=a.getSymbolRecord(c.module);if(b.exporter){exporterRecord=b.exporter.getSymbolRecord(c.module);var d=exporterRecord.program}else d=b.program; 56 d?d.trigger():a.runtimeError(c.lino,b.name+" has not been initialized")}return c.pc+1}},Variable:{compile:function(a){a.compileVariable("core","variable",!0);return!0},run:function(a){return a[a.pc].pc+1}},Wait:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue(a),d=1E3;switch(a.getToken()){case "milli":case "millis":a.next();d=1;break;case "tick":case "ticks":a.next();d=10;break;case "second":case "seconds":a.next();d=1E3;break;case "minute":case "minutes":a.next(),d=6E4}a.addCommand({domain:"core", 57 keyword:"wait",lino:c,value:b,multiplier:d});return!0},run:function(a){var c=a[a.pc],b=a.getValue(c.value);setTimeout(function(){a.run(c.pc+1)},b*c.multiplier);return 0}},While:{compile:function(a){var c=a.getLino();a.next();var b=a.getCondition(),d=a.getPc();a.addCommand({domain:"core",keyword:"while",lino:c,condition:b});c=a.getPc();a.addCommand({domain:"core",keyword:"goto",goto:0});a.compileOne();a.addCommand({domain:"core",keyword:"goto",goto:d});a.getCommandAt(c).goto=a.getPc();return!0},run:function(a){return a.condition.test(a, 58 a[a.pc].condition)?a.pc+2:a.pc+1}},getHandler:function(a){switch(a){case "add":return b.Add;case "alias":return b.Alias;case "begin":return b.Begin;case "clear":return b.Clear;case "close":return b.Close;case "debug":return b.Debug;case "decode":return b.Decode;case "divide":return b.Divide;case "encode":return b.Encode;case "end":return b.End;case "fork":return b.Fork;case "go":case "goto":return b.Go;case "gosub":return b.Gosub;case "if":return b.If;case "import":return b.Import;case "index":return b.Index; 59 case "load":return b.Load;case "module":return b.Module;case "multiply":return b.Multiply;case "negate":return b.Negate;case "on":return b.On;case "print":return b.Print;case "put":return b.Put;case "replace":return b.Replace;case "return":return b.Return;case "run":return b.Run;case "script":return b.Script;case "set":return b.Set;case "stop":return b.Stop;case "take":return b.Take;case "toggle":return b.Toggle;case "trigger":return b.Trigger;case "variable":return b.Variable;case "wait":return b.Wait; 60 case "while":return b.While;default:return!1}},run:function(a){var c=a[a.pc],d=b.getHandler(c.keyword);d||a.runtimeError(c.lino,"Unknown keyword '"+c.keyword+"' in 'core' package");return d.run(a)},isNegate:function(a){return"not"===a.getToken()?(a.next(),!0):!1},value:{compile:function(a){if(a.isSymbol()){var c=a.getToken();switch(a.getSymbolRecord().keyword){case "module":return a.next(),{domain:"core",type:"module",name:c};case "variable":return a.nextTokenIs("modulo")?(a=a.getNextValue(),{domain:"core", 61 type:"modulo",name:c,value:a}):{domain:"core",type:"symbol",name:c}}return null}c=a.getToken();if("true"===c)return a.next(),{domain:"core",type:"boolean",content:!0};if("false"===c)return a.next(),{domain:"core",type:"boolean",content:!1};if("random"===c)return a.next(),{domain:"core",type:"random",range:a.getValue()};if("cos"===c)return a.next(),c=a.getValue(),a.skip("radius"),a=a.getValue(),{domain:"core",type:"cos",angle_c:c,radius_c:a};if("sin"===c)return a.next(),c=a.getValue(),a.skip("radius"), 62 a=a.getValue(),{domain:"core",type:"sin",angle_s:c,radius_s:a};if("tan"===c)return a.next(),c=a.getValue(),a.skip("radius"),a=a.getValue(),{domain:"core",type:"tan",angle_t:c,radius_t:a};if("empty"===c)return a.next(),{domain:"core",type:"empty"};if("now"===c)return a.next(),{domain:"core",type:"now"};if("newline"===c)return a.next(),{domain:"core",type:"newline"};if("encode"===c)return a.next(),{domain:"core",type:"encode",value:a.getValue()};if("decode"===c)return a.next(),{domain:"core",type:"decode", 63 value:a.getValue()};if("lowercase"===c)return a.next(),{domain:"core",type:"lowercase",value:a.getValue()};if("element"===c){c=a.getNextValue();if(a.tokenIs("of")&&a.nextIsSymbol()){var b=a.getSymbolRecord();a.next();if("variable"===b.keyword)return{domain:"core",type:"element",element:c,symbol:b.name}}return null}if("property"===c)return c=a.getNextValue(),a.tokenIs("of")&&a.nextIsSymbol()&&(b=a.getSymbolRecord(),a.next(),"variable"===b.keyword)?{domain:"core",type:"property",property:c,symbol:b.name}: 64 null;a.tokenIs("the")&&a.next();switch(a.getToken()){case "index":a.next();if(a.tokenIs("of")&&(a.next(),a.isSymbol()))return c=a.getToken(),a.next(),{domain:"core",type:"index",name:c};break;case "value":a.next();if(a.tokenIs("of"))return a.next(),{domain:"core",type:"valueOf",value:a.getValue()};break;case "length":a.next();if(a.tokenIs("of"))return a.next(),{domain:"core",type:"lengthOf",value:a.getValue()};break;case "left":a.next();c=a.getValue();if(a.tokenIs("of"))return a.next(),a=a.getValue(), 65 {domain:"core",type:"left",count:c,value:a};break;case "right":a.next();c=a.getValue();if(a.tokenIs("of"))return a.next(),a=a.getValue(),{domain:"core",type:"right",count:c,value:a};break;case "position":if(a.next(),a.tokenIs("of")&&(a.next(),c=!1,a.tokenIs("the")&&(a.next(),a.tokenIs("last")&&(a.next(),c=!0)),b=a.getValue(),a.tokenIs("in")))return a.next(),a=a.getValue(),{domain:"core",type:"position",needle:b,haystack:a,last:c}}return null},get:function(a,c){switch(c.type){case "boolean":return{type:"boolean", 66 numeric:!1,content:c.content};case "index":return{type:"constant",numeric:!0,content:a.getSymbolRecord(c.name).index};case "random":return a=a.evaluate(c.range),{type:"constant",numeric:!0,content:Math.floor(Math.random()*a.content)};case "cos":var b=a.getValue(c.angle_c);a=a.getValue(c.radius_c);return{type:"constant",numeric:!0,content:parseInt(Math.cos(.01745329*parseFloat(b))*a,10)};case "sin":return b=a.getValue(c.angle_s),a=a.getValue(c.radius_s),{type:"constant",numeric:!0,content:parseInt(Math.sin(.01745329* 67 parseFloat(b))*a,10)};case "tan":return b=a.getValue(c.angle_t),a=a.getValue(c.radius_t),{type:"constant",numeric:!0,content:parseInt(Math.tan(.01745329*parseFloat(b))*a,10)};case "valueOf":return a=parseInt(a.getValue(c.value)),{type:"constant",numeric:!0,content:a?a:0};case "lengthOf":return{type:"constant",numeric:!0,content:a.getValue(c.value).length};case "left":return{type:"constant",numeric:!1,content:a.getValue(c.value).substr(0,a.getValue(c.count))};case "right":return b=a.getValue(c.value), 68 {type:"constant",numeric:!1,content:b.substr(b.length-a.getValue(c.count))};case "position":return b=a.getValue(c.needle),a=a.getValue(c.haystack),{type:"constant",numeric:!0,content:c.last?a.lastIndexOf(b):a.indexOf(b)};case "modulo":return b=a.getSymbolRecord(c.name),a=a.evaluate(c.value),{type:"constant",numeric:!0,content:b.value[b.index].content%a.content};case "empty":return{type:"constant",numeric:!1,content:""};case "now":return{type:"constant",numeric:!0,content:Date.now()/1E3};case "newline":return{type:"constant", 69 numeric:!1,content:"\n"};case "encode":return{type:"constant",numeric:!1,content:a.encode(a.getValue(c.value))};case "decode":return{type:"constant",numeric:!1,content:a.decode(a.getValue(c.value))};case "lowercase":return{type:"constant",numeric:!1,content:a.getValue(c.value).toLowerCase()};case "element":b=a.getValue(c.element);c=a.getSymbolRecord(c.symbol);var d="";try{d=JSON.parse(a.getValue(c.value[c.index]))[b]}catch(f){a.runtimeError(command.lino,"Can't parse JSON");break}return{type:"constant", 70 numeric:!1,content:"object"===typeof d?JSON.stringify(d):d};case "property":b=a.getValue(c.property);c=a.getSymbolRecord(c.symbol);a=a.getValue(c.value[c.index]);c="";if("object"===typeof a)c=a[b];else try{c=JSON.parse(a)[b]}catch(f){console.log("Can't parse '"+a+"': "+f.message),c=""}return{type:"constant",numeric:Number.isInteger(c),content:c};case "module":return{type:"boolean",numeric:!1,content:a.getSymbolRecord(c.name).program}}return null},put:function(a,c){a.value[a.index]=c}},condition:{compile:function(a){if(a.tokenIs("not"))return a.next(), 71 {domain:"core",type:"not",value:a.getValue()};try{var c=a.getValue();if("is"===a.getToken()){a.next();var d=b.isNegate(a);switch(a.getToken()){case "numeric":return a.next(),{domain:"core",type:"numeric",value1:c};case "even":return a.next(),{domain:"core",type:"even",value1:c};case "odd":return a.next(),{domain:"core",type:"odd",value1:c};case "greater":a.next();if(a.tokenIs("than")){a.next();var e=a.getValue();return{domain:"core",type:"greater",value1:c,value2:e,negate:d}}break;case "less":a.next(); 72 if(a.tokenIs("than")){a.next();var f=a.getValue();return{domain:"core",type:"less",value1:c,value2:f,negate:d}}break;default:var g=a.getValue();return{domain:"core",type:"is",value1:c,value2:g,negate:d}}}else if(c)return{domain:"core",type:"boolean",value:c}}catch(m){return program.runtimeError(command.lino,m.message),0}return null},test:function(a,c){switch(c.type){case "boolean":return a.getValue(c.value);case "numeric":return Number.isInteger(a.getValue(c.value1));case "even":return 0===a.getValue(c.value1)% 73 2;case "odd":return 1===a.getValue(c.value1)%2;case "is":return a=a.compare(a,c.value1,c.value2),c.negate?0!==a:0===a;case "greater":return a=a.compare(a,c.value1,c.value2),c.negate?0>=a:0<a;case "less":return a=a.compare(a,c.value1,c.value2),c.negate?0<=a:0>a;case "not":return!a.getValue(c.value)}return!1}}};h.exports=b},{}],6:[function(d,h,g){var e=this,b=d("./Tokenise"),a=d("./Compile"),c=d("./Run"),l=d("./Value"),p=d("./Condition"),f=d("./Compare"),k={domain:{core:d("./Core")},setupTracer:function(){var a= 74 document.getElementById("easycoder-tracer");a&&(a.innerHTML='<div><input id="easycoder-run-button" type="button" value="Run" /><input id="easycoder-step-button" type="button" value="Step" /><div id="easycoder-tracer-content" style="border:1px solid black;padding:4px";width:100%></div>',a.style.display="none")},runtimeError:function(a,c){this.lino=a;this.reportError({message:"Line "+(0<=a?a:"")+": "+c},k.program)},nonNumericValueError:function(a){this.runtimeError(a,"Non-numeric value")},variableDoesNotHoldAValueError:function(a, 75 c){this.runtimeError(a,"Variable '"+c+"' does not hold a value")},reportError:function(c,b,d){if(c.message)if(this.compiling||b){d=d?d:b.source;var e=d.tokens;d=d.scriptLines;e=this.compiling?e[a.getIndex()].lino:b[b.pc].lino;b=this.compiling?"Compile error":"Runtime error in '"+b.script+"'";b+=":\n";var f=e-5;for(f=0>f?0:f;f<e;f++){var m=(""+(f+1)).padStart(4," ");b+=m+" "+d[f].line.split("\\s").join(" ")+"\n"}b+=c.message+"\n";c=a.getWarnings();if(c.length)for(b+="Warnings:\n",c=$jscomp.makeIterator(c), 76 d=c.next();!d.done;d=c.next())b+=d.value+"\n";console.log(b);alert(b);k.aborted=!0}else c="Error: "+c.message,alert(c),console.log(c);else console.log("An error occurred - origin was "+c.path[0])},getSymbolRecord:function(a){a=this[this.symbols[a].pc];return a.alias?this.getSymbolRecord(a.alias):a.exporter&&a.exporter!=this?a.exporter.getSymbolRecord(a.exportedName):a},verifySymbol:function(a){return this.symbols.hasOwnProperty(a)},encode:function(a){return l.encode(a,this.encoding)},decode:function(a){return l.decode(a, 77 this.encoding)},evaluate:function(a){return l.evaluate(this,a)},getValue:function(a){return l.getValue(this,a)},getFormattedValue:function(a){a=l.getValue(this,a);return["[","{"].includes(a[0])?JSON.stringify(JSON.parse(a),null,2):a},getSimpleValue:function(a){return!0===a||!1===a?{type:"boolean",content:a}:{type:"constant",numeric:Number.isInteger(a),content:a}},run:function(a){k.program=this;c(this,a)},trigger:function(){this.onTrigger&&c(this,this.onTrigger)},addJS:function(a,c,b){var d=document.createElement("script"); 78 d.type="text/javascript";d.src=c+"/"+a+".js";d.onload=function(){b(a)};document.head.appendChild(d)},register:function(a){k.program=a},runScript:function(a){var c=a[a.pc],b=a.getValue(c.script),d=c.imports;a=c.module?a.getSymbolRecord(c.module):null;k.tokeniseScript(b.split("\n"),d,a,this)},close:function(){},compileScript:function(b,d,e,g){var h=b.tokens;this.compiling=!0;var m=Date.now();a.value=l;a.condition=p;a.domain=k.domain;a.imports=d;d=a.compile(h);var n=Date.now();console.log(n-k.timestamp+ 79 " ms: Compiled "+h.length+" tokens in "+(n-m+" ms"));this.compiling=!1;d.EasyCoder=k;d.value=l;d.condition=p;d.compare=f;d.source=b;d.run=this.run;d.runScript=this.runScript;d.evaluate=this.evaluate;d.getValue=this.getValue;d.getFormattedValue=this.getFormattedValue;d.getSimpleValue=this.getSimpleValue;d.encode=this.encode;d.decode=this.decode;d.domain=this.domain;d.trigger=this.trigger;d.addJS=this.addJS;d.checkPlugin=this.checkPlugin;d.getPlugin=this.getPlugin;d.addLocalPlugin=this.addLocalPlugin; 80 d.getPluginsPath=this.getPluginsPath;d.getSymbolRecord=this.getSymbolRecord;d.verifySymbol=this.verifySymbol;d.runtimeError=this.runtimeError;d.nonNumericValueError=this.nonNumericValueError;d.variableDoesNotHoldAValueError=this.variableDoesNotHoldAValueError;d.reportError=this.reportError;d.register=this.register;d.symbols=a.getSymbols();d.encoding="ec";d.popups=[];d.stack=[];d.queue=[0];d.module=e;d.parent=g;e&&(e.program=d);this.setupTracer();c(d,0)},tokeniseScript:function(a,c,d,e){var f=Date.now(); 81 a=b.tokenise(a);var g=Date.now();console.log(g-k.timestamp+" ms: Tokenised "+a.scriptLines.length+" lines in "+(g-f+" ms"));try{k.compileScript(a,c,d,e)}catch(q){"stop"!==q.message&&this.reportError(q,k.program,a)}},tokenize:function(a){var c=a.split("\n");if(!e.tokenising){try{k.tokeniseScript(c)}catch(r){k.reportError(r,null,a)}e.tokenising=!0}},setPluginCount:function(a){e.plugins=[];e.pluginCount=a},checkPlugin:function(a){return k.domain[a]},getPlugin:function(a,c,b){k.domain[a]?b():(a=document.createElement("script"), 82 a.type="text/javascript",a.src=c,a.onload=function(){console.log(Date.now()-k.timestamp+" ms: Plugin "+c+" loaded");b()},document.head.appendChild(a))},addGlobalPlugin:function(a,c){e.plugins.push({name:a,handler:c});e.plugins.length===e.pluginCount&&(e.plugins.forEach(function(a){k.domain[a.name]=a.handler}),k.tokenize(e.source))},addLocalPlugin:function(a,c,b){k.domain[a]=c;b()},getPluginsPath:function(){return k.pluginsPath},loadPluginJs:function(a){console.log(Date.now()-k.timestamp+" ms: Load plugins.js"); 83 var c=new XMLHttpRequest;c.open("GET",""+window.location.origin+a+"/easycoder/plugins.js",!0);c.onreadystatechange=function(){if(4===c.readyState)switch(c.status){case 200:var b=document.createElement("script");b.src=""+window.location.origin+a+"/easycoder/plugins.js";b.type="text/javascript";b.onload=function(){EasyCoder_Plugins.getGlobalPlugins(k.timestamp,a,k.setPluginCount,k.getPlugin,k.addGlobalPlugin)};document.head.appendChild(b);k.pluginsPath=a;break;case 404:a?k.loadPluginJs(a.slice(0,a.lastIndexOf("/"))): 84 k.reportError({message:"Can't load plugins.js"},k.program,this.source)}};c.send()},start:function(a){e.source=a;a=window.location.pathname;a.endsWith("/")&&(a=a.slice(0,-1));k.loadPluginJs(a)}};h.exports=k},{"./Compare":2,"./Compile":3,"./Condition":4,"./Core":5,"./Run":7,"./Tokenise":8,"./Value":9}],7:[function(d,h,g){var e=function(b,a){var c=[],d=function(a){var c=9999;a.forEach(function(a){a=a.line;for(var b=0;b<a.length&&" "===a[b];)b++;0<b&&b<c&&(c=b)});return 0};if(c.length)c.push(a);else for(b.register(b), 85 c.push(a);0<c.length&&!EasyCoder.aborted;)for(b.pc=c.shift(),b.watchdog=0,a={};;){if(1E6<b.watchdog){b.lino=b[b.pc].lino;b.reportError(Error("Program runaway intercepted.\nHave you forgotten to increment a loop counter?",b),b);break}b.watchdog++;var g=b[b.pc].domain;b.debugStep&&console.log(b.script+" "+b.pc+" "+g+":"+b[b.pc].keyword);var f=b.domain[g];if(!f){b.runtimeError(b[b.pc].lino,"Unknown domain '"+g+"'");break}b.pc=f.run(b);if(!b.pc)break;if(b.stop){b.tracing=!1;return}if(b.tracing){g=b[b.pc]; 86 f=b.source.scriptLines;var h=d(f),m=document.getElementById("easycoder-tracer");if(!m){b.runtimeError(g.lino,"Element 'easycoder-tracer' was not found");return}m.style.display="block";m.style.visibility="visible";var n="";if(b.tracer){if(m=document.getElementById("easycoder-tracer-content")){b.tracer.variables.forEach(function(a,c,d){var e=b.getSymbolRecord(a);if(1<e.elements)for(n+=a+": "+e.index+"/"+e.elements+": ",a=0;a<e.elements;a++){var f=e.value[a];n=f?n+(f.content+" "):n+"undefined "}else n= 87 (e=e.value[e.index])?n+(a+": "+e.content):n+(a+": undefined");switch(b.tracer.alignment){case "horizontal":c<d.length-1&&(n+=", ");break;case "vertical":n+="<br>"}});n+="<hr>";for(var r="",t=5;0<t;t--)g.lino&&(r+='<input type="text" name="'+t+'"'+('value="'+f[g.lino-t].line.substr(h)+'"')+'style="width:100%;border:none;enabled:false">'),r+="<br>";m.innerHTML=n+" "+r;m.style.display="block";a.run=document.getElementById("easycoder-run-button");a.step=document.getElementById("easycoder-step-button"); 88 a.run.onclick=function(a){return function(){a.run.blur();b.tracing=!1;document.getElementById("easycoder-tracer-content").style.display="none";try{e(b,b.resume)}catch(q){var c="Error in run handler: "+q.message;console.log(c);alert(c)}}}(a);a.step.onclick=function(a){return function(){console.log("step");a.step.blur();b.tracing=!0;document.getElementById("easycoder-tracer-content").style.display="block";try{e(b,b.resume)}catch(q){var c="Error in step handler: "+q.message;console.log(c);alert(c)}}}(a)}b.resume= 89 b.pc;b.pc=0}break}a={run:a.run,step:a.step}}};h.exports=e},{}],8:[function(d,h,g){var e={markComments:function(b){var a=b.list,c=void 0===b.index?0:b.index,d=void 0===b.inComment?!1:b.inComment,g=void 0===b.newList?[]:b.newList;if(c>=a.length)return g;var f=a[c];b=f.lino;f=f.token;var h={list:a,index:c+1,inComment:!1,newList:g.concat({lino:b,index:c,token:f})};g={list:a,index:c+1,inComment:!0,newList:g.concat({lino:b,index:c,comment:!0,token:f})};return d&&0<c&&b===a[c-1].lino?e.markComments(g):"!"=== 90 f.charAt(0)?e.markComments(g):e.markComments(h)},findStrings:function(b){var a=b.original,c=b.line,d=void 0===b.inComment?!1:b.inComment;b=void 0===b.inQuote?!1:b.inQuote;var g=c.charAt(0),f=b&&" "===g?"\\s":g;if(1===c.length)return f;c=c.substring(1);if("!"===g&&!b)return g+e.findStrings({original:a,line:c,inComment:!0,inQuote:!1});if("`"===g&&!d&&!b)return g+e.findStrings({original:a,line:c,inComment:d,inQuote:!0});if("`"===g&&b)return g+e.findStrings({original:a,line:c,inComment:d,inQuote:!1}); 91 if(!d&&!b&&!g.match(/[A-z0-9_\-+*/\- ]/)){if("'"==g||'"'==g)throw Error('Bad syntax in "'+a+'":\nStrings in EasyCoder must be enclosed in backticks.');throw Error('Unrecognised character "'+g+'" in "'+a+'".');}return f+e.findStrings({original:a,line:c,inComment:d,inQuote:b})},tokenise:function(b){b=b.map(function(a){return a.length?e.findStrings({original:a,line:a}):""}).map(function(a,b){return{lino:b+1,line:a}});var a=b.map(function(a){return a.line.trim().split(/\s+/).map(function(c,b){return{lino:a.lino, 92 index:b,token:c}})});a=[].concat.apply([],a).filter(function(a){return a.token}).map(function(a){return{lino:a.lino,index:a.index,token:a.token.split("\\s").join(" ")}});a=e.markComments({list:a}).filter(function(a){return!a.comment});return{scriptLines:b,tokens:a}}};h.exports=e},{}],9:[function(d,h,g){var e={getItem:function(b){var a=b.getToken();if(!a)return null;if("true"===a)return b.next(),{type:"boolean",content:!0};if("false"===a)return b.next(),{type:"boolean",content:!1};if("`"===a.charAt(0))return b.next(), 93 {type:"constant",numeric:!1,content:a.substring(1,a.length-1)};if(a[0].match(/[0-9\-]/)){var c=eval(a);if(Number.isInteger(c))return b.next(),{type:"constant",numeric:!0,content:c};throw Error("'"+a+"' is not an integer");}a=$jscomp.makeIterator(Object.keys(b.domain));for(c=a.next();!c.done;c=a.next())if(c=b.domain[c.value].value.compile(b))return c;return null},compile:function(b){var a=b.getToken(),c=b.value.getItem(b);if(!c)throw Error("Undefined value: '"+a+"'");if("cat"===b.getToken()){for(a= 94 {type:"cat",numeric:!1,parts:[c]};b.tokenIs("cat");)b.next(),a.parts.push(b.value.getItem(b));return a}return c},doValue:function(b,a){switch(a.type){case "cat":return{type:"constant",numeric:!1,content:a.parts.reduce(function(a,c){return a+e.doValue(b,c).content},"")};case "boolean":case "constant":return a;case "symbol":var c=b.getSymbolRecord(a.name);return c.isValueHolder?(a=c.value[c.index])?(null===a.content&&(a.content=a.numeric?0:""),a):null:b.domain[c.domain].value.get(b,a)}return b.domain[a.domain].value.get(b, 95 a)},constant:function(b,a){return{type:"constant",numeric:a,content:b}},evaluate:function(b,a){if(!a)return{type:"constant",numeric:!1,content:""};var c=e.doValue(b,a);if(c)return c;b.runtimeError(b[b.pc].lino,"Can't decode value: "+a)},getValue:function(b,a){return e.evaluate(b,a).content},encode:function(b,a){if(b)switch(a){case "ec":return b.replace(/'/g,"~sq~").replace(/"/g,"~dq~");case "url":return encodeURIComponent(b.replace(/\s/g,"+"))}return b},decode:function(b,a){if(b)switch(a){case "ec":return b.replace(/~dq~/g, 96 '"').replace(/~sq~/g,"'");case "url":return decodeURIComponent(b).replace(/\+/g," ")}return b}};h.exports=e},{}]},{},[1]); 11 $jscomp.polyfill("String.prototype.repeat",function(d){return d?d:function(d){var f=$jscomp.checkStringArgs(this,null,"repeat");if(0>d||1342177279<d)throw new RangeError("Invalid count value");d|=0;for(var e="";d;)if(d&1&&(e+=f),d>>>=1)f+=f;return e}},"es6","es3");$jscomp.stringPadding=function(d,h){d=void 0!==d?String(d):" ";return 0<h&&d?d.repeat(Math.ceil(h/d.length)).substring(0,h):""}; 12 $jscomp.polyfill("String.prototype.padStart",function(d){return d?d:function(d,f){var e=$jscomp.checkStringArgs(this,null,"padStart");return $jscomp.stringPadding(f,d-e.length)+e}},"es8","es3"); 13 (function(){function d(h,f,e){function b(c,m){if(!f[c]){if(!h[c]){var g="function"==typeof require&&require;if(!m&&g)return g(c,!0);if(a)return a(c,!0);m=Error("Cannot find module '"+c+"'");throw m.code="MODULE_NOT_FOUND",m;}m=f[c]={exports:{}};h[c][0].call(m.exports,function(a){return b(h[c][1][a]||a)},m,m.exports,d,h,f,e)}return f[c].exports}for(var a="function"==typeof require&&require,c=0;c<e.length;c++)b(e[c]);return b}return d})()({1:[function(d,h,f){var e=d("./easycoder/Main");e.timestamp= 14 Date.now();console.log("EasyCoder loaded; waiting for page");window.onload=function(){console.log(Date.now()-e.timestamp+" ms: Page loaded; reset timer & start EasyCoder");e.timestamp=Date.now();e.scripts={};window.EasyCoder=e;var b=document.getElementById("easycoder-script");if(b){b.style.display="none";try{e.start(b.innerText)}catch(a){e.reportError(a)}}}},{"./easycoder/Main":6}],2:[function(d,h,f){h.exports=function(d,b,a){b=d.value.evaluate(d,b);d=d.value.evaluate(d,a);a=b.content;var c=d.content; 15 b.numeric&&!d.numeric&&(a=a.toString());!b.numeric&&d.numeric&&(c=c.toString());b.numeric||"undefined"!==typeof a||(a="");d.numeric||"undefined"!==typeof c||(c="");return a>c?1:a<c?-1:0}},{}],3:[function(d,h,f){var e=Object.assign||function(a){for(var c=1;c<arguments.length;c++){var b=arguments[c],d;for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(a[d]=b[d])}return a},b=this,a={getTokens:function(){return b.tokens},addWarning:function(a){b.warnings.push(a)},warning:function(c){a.addWarning(c)}, 16 unrecognisedSymbol:function(c){a.addWarning("Unrecognised symbol '"+c+"'")},getWarnings:function(){return b.warnings},getIndex:function(){return b.index},next:function(a){b.index+=void 0===a?1:a},peek:function(){return b.tokens[b.index+1].token},getToken:function(){return b.index>=b.tokens.length?null:b.tokens[b.index]?b.tokens[b.index].token:null},nextToken:function(){a.next();return a.getToken()},tokenIs:function(a){return b.index>=b.tokens.length?!1:a===b.tokens[b.index].token},nextTokenIs:function(c){a.next(); 17 return a.tokenIs(c)},skip:function(c){if(b.index>=b.tokens.length)return null;a.next();a.tokenIs(c)&&a.next()},prev:function(){b.index--},getLino:function(){return b.index>=b.tokens.length?0:b.tokens[b.index].lino},getTarget:function(a){a=void 0===a?b.index:a;return b.tokens[a].token},getTargetPc:function(c){c=void 0===c?b.index:c;return b.symbols[a.getTarget(c)].pc},getCommandAt:function(a){return b.program[a]},isSymbol:function(c){c=void 0===c?!1:c;if(a.getTarget()in b.symbols)return!0;if(c)throw Error("Unknown symbol: '"+ 18 a.getTarget()+"'");return!1},nextIsSymbol:function(c){c=void 0===c?!1:c;a.next();return a.isSymbol(c)},getSymbol:function(){return b.symbols[a.getToken()]},getSymbolPc:function(){return a.getSymbol().pc},getSymbolRecord:function(){return b.program[a.getSymbolPc()]},getSymbols:function(){return b.symbols},getProgram:function(){return b.program},getPc:function(){return b.program.length},getValue:function(){return a.value.compile(a)},getNextValue:function(){a.next();return a.getValue()},getCondition:function(){return a.condition.compile(a)}, 19 constant:function(c,g){return a.value.constant(c,void 0===g?!1:g)},addCommand:function(a){b.program.push(e({pc:b.program.length},a))},addSymbol:function(a,g){b.symbols[a]={pc:g}},mark:function(){b.savedMark=b.index},rewind:function(){b.index=b.savedMark},rewindTo:function(a){b.index=a},completeHandler:function(){var c=a.getLino(),g=a.getPc();a.addCommand({domain:"core",keyword:"goto",lino:c,goto:0});a.compileOne();a.addCommand({domain:"core",keyword:"stop",lino:c,next:0});a.getCommandAt(g).goto=a.getPc(); 20 return!0},compileVariable:function(c,g,m,d){m=void 0===m?!1:m;d=void 0===d?null:d;a.next();var p=a.getLino(),l=a.getTokens()[a.getIndex()];if(b.symbols[l.token])throw Error("Duplicate variable name '"+l.token+"'");var n=a.getPc();a.next();a.addSymbol(l.token,n);c={domain:c,keyword:g,lino:p,isSymbol:!0,isValueHolder:m,name:l.token,elements:1,index:0,value:[{}],element:[],extra:d};"dom"===d&&(c.element=[]);a.addCommand(c);return c},compileToken:function(){var c=a.getToken();if(c){a.mark();for(var g= 21 $jscomp.makeIterator(Object.keys(a.domain)),b=g.next();!b.done;b=g.next()){if((b=a.domain[b.value])&&(b=b.getHandler(c))&&b.compile(a))return;a.rewind()}console.log("No handler found");throw Error("I don't understand '"+c+"...'");}},compileOne:function(){var c=a.getToken();if(c){b.warnings=[];var g=b.program.length;if(c.endsWith(":")){c=c.substring(0,c.length-1);if(b.symbols[c])throw Error("Duplicate symbol: '"+c+"'");b.symbols[c]={pc:g};b.index++}else a.compileToken()}},compileFromHere:function(c){for(;b.index< 22 b.tokens.length;){var g=b.tokens[b.index].token;if("else"===g)return b.program;a.compileOne();if(-1<c.indexOf(g))break}},compile:function(c){b.tokens=c;b.index=0;b.program=[];b.symbols={};b.warnings=[];a.compileFromHere([]);a.addCommand({domain:"core",keyword:"stop",lino:a.getLino(),next:0});return b.program}};h.exports=a},{}],4:[function(d,h,f){var e=Object.assign||function(b){for(var a=1;a<arguments.length;a++){var c=arguments[a],g;for(g in c)Object.prototype.hasOwnProperty.call(c,g)&&(b[g]=c[g])}return b}; 23 h.exports={compile:function(b){b.mark();for(var a=$jscomp.makeIterator(Object.keys(b.domain)),c=a.next();!c.done;c=a.next()){if(c=b.domain[c.value].condition.compile(b))return e({domain:name},c);b.rewind()}},test:function(b,a){return b.domain[a.domain].condition.test(b,a)}}},{}],5:[function(d,h,f){var e=this,b={Add:{compile:function(a){var c=a.getLino();a.next();var g=a.getValue();if(a.tokenIs("to"))if(a.next(),a.isSymbol()){var b=a.getSymbol();if(a.getCommandAt(b.pc).isValueHolder){if("giving"=== 24 a.peek()){b=a.getValue();a.next();var d=a.getToken();a.next();a.addCommand({domain:"core",keyword:"add",lino:c,value1:g,value2:b,target:d})}else b=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"add",lino:c,value1:g,target:b});return!0}a.warning("core "+e.name+": Expected value holder")}else{b=a.getValue();if(a.tokenIs("giving"))return a.next(),d=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"add",lino:c,value1:g,value2:b,target:d}),!0;a.warning("core "+e.name+': Expected "giving"')}return!1}, 25 run:function(a){var c=a[a.pc],g=c.value1,b=c.value2,d=a.getSymbolRecord(c.target);if(d.isValueHolder){var e=d.value[d.index];b?(a=a.getValue(b)+a.getValue(g),d.value[d.index]={type:"constant",numeric:!0,content:a}):(e.numeric||a.nonNumericValueError(c.lino),a=e.content+a.getValue(g),d.value[d.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,d.name);return c.pc+1}},Alias:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var g=a.getToken();a.next(); 26 if(a.tokenIs("to")&&(a.next(),a.isSymbol())){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"alias",lino:c,alias:g,symbol:b});return!0}}return!1},run:function(a){var c=a[a.pc],g=a.symbols[c.alias].pc,b=a[g],d=a.getSymbolRecord(c.symbol);a[g]={pc:b.pc,domain:d.domain,keyword:d.keyword,lino:b.lino,name:b.name,alias:c.symbol};return c.pc+1}},Begin:{compile:function(a){a.next();a.compileFromHere(["end"]);return!0},run:function(a){return a[a.pc].pc+1}},Callback:{compile:function(a){a.compileVariable("core", 27 "callback");return!0},run:function(a){return a[a.pc].pc+1}},Clear:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var g=a.getSymbolRecord();if(g.isValueHolder)return g=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"clear",lino:c,symbol:g}),!0;a.warning("'Variable '"+g.name+"' does not hold a value")}return!1},run:function(a){var c=a[a.pc],g=a.getSymbolRecord(c.symbol);g.isValueHolder?(a.domain[g.domain].value.put(g,{type:"boolean",content:!1}),c.numeric=!1):a.VariableDoesNotHoldAValueError(c.lino, 28 g.name);return c.pc+1}},Close:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var g=a.getSymbolRecord();if("module"===g.keyword)return a.next(),a.addCommand({domain:"core",keyword:"close",lino:c,module:g.name}),!0}return!1},run:function(a){var c=a[a.pc];a=a.getSymbolRecord(c.module).program;a.run(a.onClose);return c.pc+1}},Debug:{compile:function(a){var c=a.getLino();if(a.nextTokenIs("program")){a.next();if(["item","pc"].includes(a.getToken())){var g=a.getNextValue();a.addCommand({domain:"core", 29 keyword:"debug",lino:c,item:g});return!0}a.addCommand({domain:"core",keyword:"debug",lino:c,item:-1});return!0}return a.tokenIs("symbols")?(a.next(),a.addCommand({domain:"core",keyword:"debug",lino:c,item:"symbols"}),!0):a.tokenIs("symbol")?(g=a.nextToken(),a.next(),a.addCommand({domain:"core",keyword:"debug",lino:c,item:"symbol",name:g}),!0):a.tokenIs("step")?(a.next(),a.addCommand({domain:"core",keyword:"debug",lino:c,item:"step"}),!0):!1},run:function(a){var c=a[a.pc],g=c.item;switch(g){case "symbols":console.log("Symbols: "+ 30 JSON.stringify(a.symbols,null,2));break;case "symbol":a=a.getSymbolRecord(c.name);g=a.exporter;delete a.exporter;console.log("Symbol: "+JSON.stringify(a,null,2));a.exporter=g;break;case "step":a.debugStep=!0;break;default:0<=g.content?console.log("Debug item "+g.content+": "+JSON.stringify(a[g.content],null,2)):console.log("Debug program: "+JSON.stringify(a,null,2))}return c.pc+1}},Decode:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var g=a.getToken();a.next();a.addCommand({domain:"core", 31 keyword:"decode",lino:c,symbol:g});return!0}return!1},run:function(a){var c=a[a.pc],g=a.getSymbolRecord(c.symbol);if(g.isValueHolder){var b=a.getValue(g.value[g.index]);g.value[g.index]={type:"constant",numeric:!1,content:a.decode(b)};c.numeric=!1}else a.VariableDoesNotHoldAValueError(c.lino,g.name);return c.pc+1}},Divide:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var g=a.getSymbol();var b=a.getCommandAt(g.pc).name}g=a.getValue();a.tokenIs("by")&&a.next();var d=a.getValue();if(a.tokenIs("giving")){a.next(); 32 if(a.isSymbol())return b=a.getSymbol(),b=a.getCommandAt(b.pc).name,a.next(),a.addCommand({domain:"core",keyword:"divide",lino:c,value1:g,value2:d,target:b}),!0;a.warning("core "+e.name+": Expected value holder")}else return"undefined"===typeof b&&a.warning("core "+e.name+": No target variable given"),a.addCommand({domain:"core",keyword:"divide",lino:c,value2:d,target:b}),!0;return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,e=a.getSymbolRecord(c.target);if(e.isValueHolder){var f=e.value[e.index]; 33 b?(a=a.getValue(b)/a.getValue(d),e.value[e.index]={type:"constant",numeric:!0,content:Math.trunc(a)}):(f.numeric||a.nonNumericValueError(c,lino),a=f.content/a.getValue(d),e.value[e.index]={type:"constant",numeric:!0,content:Math.trunc(a)})}else a.VariableDoesNotHoldAValueError(c.lino,e.name);return c.pc+1}},Encode:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"encode",lino:c,symbol:b});return!0}return!1},run:function(a){var c= 34 a[a.pc],b=a.getSymbolRecord(c.symbol);if(b.isValueHolder){var d=a.getValue(b.value[b.index]);b.value[b.index]={type:"constant",numeric:!1,content:a.encode(d)};c.numeric=!1}else a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},End:{compile:function(a){a.next();return!0},run:function(){return 0}},Fork:{compile:function(a){var c=a.getLino();a.next();a.nextTokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"fork",lino:c,label:b});return!0},run:function(a){var c= 35 a[a.pc];try{a.run(a.symbols[c.label].pc)}catch(g){console.log(g.message),alert(g.message)}return c.pc+1}},Go:{compile:function(a){var c=a.getLino();a.nextTokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"go",lino:c,label:b});return!0},run:function(a){var c=a[a.pc];if(c.label){if(a.verifySymbol(c.label)){var b=a.symbols[c.label];if(b)return b.pc}a.runtimeError(c.lino,"Unknown symbol '"+c.label+"'");return 0}return c.goto}},Gosub:{compile:function(a){var c=a.getLino(); 36 a.nextTokenIs("to")&&a.next();var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"gosub",lino:c,label:b});return!0},run:function(a){var c=a[a.pc];if(a.verifySymbol(c.label))return a.stack.push(a.pc+1),a.symbols[c.label].pc;a.runtimeError(c.lino,"Unknown symbol '"+c.label+"'");return 0}},If:{compile:function(a){var c=a.getLino();a.next();var b=a.condition.compile(a),d=a.getPc();a.addCommand({domain:"core",keyword:"if",lino:c,condition:b});a.compileOne();if(!a.getToken())return a.getCommandAt(d).else= 37 a.getPc(),!0;a.tokenIs("else")?(c=a.getPc(),a.addCommand({domain:"core",keyword:"goto",goto:0}),a.getCommandAt(d).else=a.getPc(),a.next(),a.compileOne(!0),a.getCommandAt(c).goto=a.getPc()):a.getCommandAt(d).else=a.getPc();return!0},run:function(a){var c=a[a.pc];return a.condition.test(a,c.condition)?c.pc+1:c.else}},Import:{compile:function(a){var c=a.imports,b=a.getProgram();c=$jscomp.makeIterator(c);for(var d=c.next();!d.done;d=c.next()){d=d.value;var e=a.nextToken(),f=d.keyword;if(e===f){if(e=a.compileVariable(d.domain, 38 f,!0),e=b[a.getSymbols()[e.name].pc],e.element=d.element,e.exporter=d.exporter,e.exportedName=d.name,e.extra=d.extra,e.imported=!0,!a.tokenIs("and"))break}else throw Error("Mismatched import variable type for '"+d.name+"'");}if(a.tokenIs("and"))throw Error("Imports do not match exports");return!0},run:function(a){return a[a.pc].pc+1}},Index:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol(!0)){var b=a.getToken();if(a.nextTokenIs("to")){var d=a.getNextValue();a.addCommand({domain:"core",keyword:"index", 39 lino:c,symbol:b,value:d});return!0}}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol),d=a.getValue(c.value);d>=b.elements&&a.runtimeError(c.lino,"Array index "+d+" is out of range for '"+b.name+"'");b.index=d;b.imported&&(b.exporter.getSymbolRecord(b.exportedName).index=d);return c.pc+1}},Load:{compile:function(a){var c=a.getLino();switch(a.nextToken()){case "plugin":var b=a.getNextValue();a.addCommand({domain:"core",keyword:"load",lino:c,name:b});return!0}return!1},run:function(a){var c= 40 a[a.pc],b=a.getValue(c.name);switch(c.keyword){case "load":if(a.checkPlugin(b))return c.pc+1;EasyCoder_Plugins.getLocalPlugin(a.getPluginsPath,b,a.getPlugin,a.addLocalPlugin,function(){a.run(c.pc+1)});return 0}}},Module:{compile:function(a){a.compileVariable("core","module");return!0},run:function(a){a=a[a.pc];a.program=null;return a.pc+1}},Multiply:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getSymbol();var d=a.getCommandAt(b.pc).name}b=a.getValue();a.tokenIs("by")&& 41 a.next();var e=a.getValue();if(a.tokenIs("giving")){a.next();if(a.isSymbol())return d=a.getSymbol(),d=a.getCommandAt(d.pc).name,a.next(),a.addCommand({domain:"core",keyword:"multiply",lino:c,value1:b,value2:e,target:d}),!0;a.warning("core multiply: Expected value holder")}else return"undefined"===typeof d&&a.warning("core multiply: No target variable given"),a.addCommand({domain:"core",keyword:"multiply",lino:c,value2:e,target:d}),!0;return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,e= 42 a.getSymbolRecord(c.target);if(e.isValueHolder){var f=e.value[e.index];b?(a=a.getValue(b)*a.getValue(d),e.value[e.index]={type:"constant",numeric:!0,content:a}):(f.numeric||a.nonNumericValueError(c,lino),a=f.content*a.getValue(d),e.value[e.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,e.name);return c.pc+1}},Negate:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getToken();a.next();a.addCommand({domain:"core",keyword:"negate", 43 lino:c,symbol:b});return!0}return!1},run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.symbol);b.isValueHolder?b.value[b.index]={type:"constant",numeric:!0,content:-b.value[b.index].content}:a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},On:{compile:function(a){var c=a.getLino(),b=a.nextToken();switch(b){case "trigger":case "close":case "restore":case "message":return a.next(),a.addCommand({domain:"core",keyword:"on",lino:c,action:b}),a.completeHandler()}return a.isSymbol()&&(b= 44 a.getSymbolRecord(),"callback"===b.keyword)?(a.next(),a.addCommand({domain:"core",keyword:"on",lino:c,action:b.name}),a.completeHandler()):!1},run:function(a){var c=a[a.pc],b=c.pc+2;switch(c.action){case "trigger":a.onTrigger=b;break;case "close":a.onClose=b;break;case "restore":a.onRestore=b;break;case "message":a.onMessage=b;break;default:var d=a.getSymbolRecord(c.action);if(d)d.cb=b;else return a.runtimeError(c.lino,"Unknown action '"+c.action+"'"),0}return c.pc+1}},Print:{compile:function(a){var c= 45 a.getLino();a.next();var b=a.getValue();a.addCommand({domain:"core",keyword:"print",lino:c,value:b});return!0},run:function(a){var c=a[a.pc];a=a.getFormattedValue(c.value);console.log("-> "+a);return c.pc+1}},Put:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("into")){a.next();if(a.isSymbol()){var d=a.getToken();a.next();a.addCommand({domain:"core",keyword:"put",lino:c,value:b,target:d});return!0}a.warning("core:put: No such variable: '"+a.getToken()+"'")}return!1}, 46 run:function(a){var c=a[a.pc],b=a.getSymbolRecord(c.target);b.isValueHolder||a.variableDoesNotHoldAValueError(c.lino,b.name);a=a.evaluate(c.value);b.value[b.index]=a;b.imported&&(b=b.exporter.getSymbolRecord(b.exportedName),b.value[b.index]=a);return c.pc+1}},Replace:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("with")){a.next();var d=a.getValue();if(a.tokenIs("in")&&(a.next(),a.isSymbol())){var e=a.getSymbolRecord();a.next();if(e.isValueHolder)return a.addCommand({domain:"core", 47 keyword:"replace",lino:c,original:b,replacement:d,target:e.name}),!0}}return!1},run:function(a){var c=a[a.pc],b=a.getValue(c.original),d=a.getValue(c.replacement),e=a.getSymbolRecord(c.target);a=a.getValue(e.value[e.index]).split(b).join(d);e.value[e.index]={type:"constant",numeric:!1,content:a};return c.pc+1}},Return:{compile:function(a){var c=a.getLino();a.next();a.addCommand({domain:"core",keyword:"return",lino:c});return!0},run:function(a){return a.stack.pop()}},Run:{compile:function(a){var c= 48 a.getLino(),b=a.getNextValue(),d=[];if(a.tokenIs("with"))for(;;)if(a.nextIsSymbol(!0)){var e=a.getSymbolRecord();e.exporter=a.getProgram();d.push(e);a.next();if(!a.tokenIs("and"))break}if(a.tokenIs("as")&&a.nextIsSymbol(!0)){e=a.getSymbolRecord();a.next();if("module"!==e.keyword)throw Error("'"+e.name+"' is not a module");var f=e.name}a.addCommand({domain:"core",keyword:"run",lino:c,script:b,imports:d,module:f});return!0},run:function(a){a.nextPc=a.pc+1;a.runScript(a);return 0}},Script:{compile:function(a){var c= 49 a.getLino(),b=a.nextToken();a.next();a.addCommand({domain:"core",keyword:"script",lino:c,name:b});return!0},run:function(a){var c=a[a.pc];a.script=c.name;EasyCoder.scripts[c.name]=a;console.log(Date.now()-EasyCoder.timestamp+" ms: Script: "+c.name);return c.pc+1}},Send:{compile:function(a){var c=a.getLino(),b=a.getNextValue();if(a.tokenIs("to")){if(a.nextTokenIs("parent"))var d="parent";else if(a.isSymbol){d=a.getSymbolRecord();if("module"!==d.keyword)throw Error("'"+d.name+"' is not a module");d= 50 d.name}a.next();a.addCommand({domain:"core",keyword:"send",lino:c,message:b,recipient:d})}return!0},run:function(a){var c=a[a.pc],b=a.getValue(c.message);if("parent"===c.recipient){var d=a.parent;d&&a.parent.onMessage&&(d.message=b,d.run(d.onMessage))}else a=a.getSymbolRecord(c.recipient),a.program.message=b,a.program.run(a.program.onMessage);return c.pc+1}},Set:{compile:function(a){var c=a.getLino();if(a.nextIsSymbol()){var b=a.getSymbolRecord();if(!b.isValueHolder)return!1;if(a.nextTokenIs("to")){a.next(); 51 for(var d=[];;){a.mark();try{d.push(a.getValue())}catch(k){a.rewind();break}}a.addCommand({domain:"core",keyword:"set",lino:c,request:"setArray",target:b.name,value:d});return!0}a.addCommand({domain:"core",keyword:"set",lino:c,request:"setBoolean",target:b.name});return!0}if(a.tokenIs("ready"))return a.next(),a.addCommand({domain:"core",keyword:"set",lino:c,request:"setReady"}),!0;if(a.tokenIs("property")&&(b=a.getNextValue(),a.tokenIs("of")&&a.nextIsSymbol()&&(d=a.getSymbolRecord(),"variable"=== 52 d.keyword&&a.nextTokenIs("to")))){var e=a.getNextValue();a.addCommand({domain:"core",keyword:"set",lino:c,request:"setProperty",target:d.name,name:b,value:e});return!0}a.tokenIs("the")&&a.next();if(a.tokenIs("elements")&&(a.next(),a.tokenIs("of"))){a.next();if(!a.isSymbol())throw Error("Unknown variable '"+a.getToken()+"'");b=a.getToken();a.next();if(a.tokenIs("to"))return a.next(),d=a.getValue(),a.addCommand({domain:"core",keyword:"set",lino:c,request:"setElements",symbol:b,value:d}),!0}if(a.tokenIs("encoding")){if(a.nextTokenIs("to"))return b= 53 a.getNextValue(),a.addCommand({domain:"core",keyword:"set",request:"encoding",lino:c,encoding:b}),!0;a.addWarning("Unknown encoding option");return!1}return a.tokenIs("payload")&&a.nextTokenIs("of")&&a.nextIsSymbol()&&(b=a.getSymbolRecord(),"callback"===b.keyword&&a.nextTokenIs("to"))?(d=a.getNextValue(),a.addCommand({domain:"core",keyword:"set",request:"setPayload",lino:c,callback:b.name,payload:d}),!0):!1},run:function(a){var c=a[a.pc];switch(c.request){case "setBoolean":var b=a.getSymbolRecord(c.target); 54 b.isValueHolder?(b.value[b.index]={type:"boolean",content:!0},c.numeric=!1):a.VariableDoesNotHoldAValueError(c.lino,b.name);break;case "setReady":a.parent.run(a.parent.nextPc);break;case "setElements":b=a.getSymbolRecord(c.symbol);b.elements=a.getValue(c.value);b.index=0;b.value=[];b.element=[];for(a=0;a<b.elements;a++)b.value.push({}),b.element.push({});break;case "setArray":b=a.getSymbolRecord(c.target);b.elements=c.value.length;b.value=c.value;break;case "encoding":a.encoding=a.getValue(c.encoding); 55 break;case "setProperty":b=a.getSymbolRecord(c.target);var d=a.getValue(b.value[b.index]);d||(d="{}");var e="";try{e=JSON.parse(d)}catch(k){return a.runtimeError(c.lino,"Can't parse "+b.name),0}d=a.getValue(c.name);if(a=a.evaluate(c.value))e[d]="boolean"===a.type?a.content:a.numeric?a.content:'{"'===a.content.substr(0,2)?JSON.parse(a.content):a.content.split('"').join('\\"'),b.value[b.index]={type:"constant",numeric:!1,content:JSON.stringify(e)};break;case "setPayload":a.getSymbolRecord(c.callback).payload= 56 a.getValue(c.payload)}return c.pc+1}},Stop:{compile:function(a){var c=a.getLino();a.next();a.addCommand({domain:"core",keyword:"stop",lino:c,next:0});return!0},run:function(){return 0}},Take:{compile:function(a){var c=a.getLino();a.next();var b=a.getValue();if(a.tokenIs("from"))if(a.next(),a.isSymbol()){var d=a.getSymbol();if(a.getCommandAt(d.pc).isValueHolder){if("giving"===a.peek()){d=a.getValue();a.next();var f=a.getToken();a.next();a.addCommand({domain:"core",keyword:"take",lino:c,value1:b,value2:d, 57 target:f})}else d=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"take",lino:c,value1:b,target:d});return!0}a.warning("core "+e.name+": Expected value holder")}else{d=a.getValue();if(a.tokenIs("giving"))return a.next(),f=a.getToken(),a.next(),a.addCommand({domain:"core",keyword:"take",lino:c,value1:b,value2:d,target:f}),!0;a.warning("core "+e.name+': Expected "giving"')}return!1},run:function(a){var c=a[a.pc],b=c.value1,d=c.value2,e=a.getSymbolRecord(c.target);if(e.isValueHolder){var f= 58 e.value[e.index];d?(a=a.getValue(d)-a.getValue(b),e.value[e.index]={type:"constant",numeric:!0,content:a}):(f.numeric||a.nonNumericValueError(c,lino),a=a.getValue(f)-a.getValue(b),e.value[e.index]={type:"constant",numeric:!0,content:a})}else a.VariableDoesNotHoldAValueError(c.lino,e.name);return c.pc+1}},Toggle:{compile:function(a){var c=a.getLino();a.next();if(a.isSymbol()){var b=a.getSymbolPc();a.next();a.addCommand({domain:"core",keyword:"toggle",lino:c,symbol:b});return!0}return!1},run:function(a){var c= 59 a[a.pc],b=a[c.symbol];if(b.isValueHolder){var d=a.domain[b.domain];a=d.value.get(a,b.value[b.index]).content;d.value.put(b,{type:"boolean",content:!a})}else a.VariableDoesNotHoldAValueError(c.lino,b.name);return c.pc+1}},Trigger:{compile:function(a){var c=a.getLino();a.next();return a.tokenIs("parent")?(a.next(),a.addCommand({domain:"core",keyword:"trigger",lino:c,module:"parent"}),!0):a.isSymbol()&&(moduleRecord=a.getSymbolRecord(),a.next(),"module"===moduleRecord.keyword)?(h=moduleRecord.name,a.addCommand({domain:"core", 60 keyword:"trigger",lino:c,module:h}),!0):!1},run:function(a){var c=a[a.pc];if("parent"===c.module)a.parent&&a.parent.trigger(!0);else{var b=a.getSymbolRecord(c.module);if(b.exporter){exporterRecord=b.exporter.getSymbolRecord(c.module);var d=exporterRecord.program}else d=b.program;d?d.trigger():a.runtimeError(c.lino,b.name+" has not been initialized")}return c.pc+1}},Variable:{compile:function(a){a.compileVariable("core","variable",!0);return!0},run:function(a){return a[a.pc].pc+1}},Wait:{compile:function(a){var c= 61 a.getLino();a.next();var b=a.getValue(a),d=1E3;switch(a.getToken()){case "milli":case "millis":a.next();d=1;break;case "tick":case "ticks":a.next();d=10;break;case "second":case "seconds":a.next();d=1E3;break;case "minute":case "minutes":a.next(),d=6E4}a.addCommand({domain:"core",keyword:"wait",lino:c,value:b,multiplier:d});return!0},run:function(a){var c=a[a.pc],b=a.getValue(c.value);setTimeout(function(){a.run(c.pc+1)},b*c.multiplier);return 0}},While:{compile:function(a){var c=a.getLino();a.next(); 62 var b=a.getCondition(),d=a.getPc();a.addCommand({domain:"core",keyword:"while",lino:c,condition:b});c=a.getPc();a.addCommand({domain:"core",keyword:"goto",goto:0});a.compileOne();a.addCommand({domain:"core",keyword:"goto",goto:d});a.getCommandAt(c).goto=a.getPc();return!0},run:function(a){return a.condition.test(a,a[a.pc].condition)?a.pc+2:a.pc+1}},getHandler:function(a){switch(a){case "add":return b.Add;case "alias":return b.Alias;case "begin":return b.Begin;case "callback":return b.Callback;case "clear":return b.Clear; 63 case "close":return b.Close;case "debug":return b.Debug;case "decode":return b.Decode;case "divide":return b.Divide;case "encode":return b.Encode;case "end":return b.End;case "fork":return b.Fork;case "go":case "goto":return b.Go;case "gosub":return b.Gosub;case "if":return b.If;case "import":return b.Import;case "index":return b.Index;case "load":return b.Load;case "module":return b.Module;case "multiply":return b.Multiply;case "negate":return b.Negate;case "on":return b.On;case "print":return b.Print; 64 case "put":return b.Put;case "replace":return b.Replace;case "return":return b.Return;case "run":return b.Run;case "script":return b.Script;case "send":return b.Send;case "set":return b.Set;case "stop":return b.Stop;case "take":return b.Take;case "toggle":return b.Toggle;case "trigger":return b.Trigger;case "variable":return b.Variable;case "wait":return b.Wait;case "while":return b.While;default:return!1}},run:function(a){var c=a[a.pc],d=b.getHandler(c.keyword);d||a.runtimeError(c.lino,"Unknown keyword '"+ 65 c.keyword+"' in 'core' package");return d.run(a)},isNegate:function(a){return"not"===a.getToken()?(a.next(),!0):!1},value:{compile:function(a){if(a.isSymbol()){var c=a.getToken();switch(a.getSymbolRecord().keyword){case "module":return a.next(),{domain:"core",type:"module",name:c};case "variable":return a.nextTokenIs("modulo")?(a=a.getNextValue(),{domain:"core",type:"modulo",name:c,value:a}):{domain:"core",type:"symbol",name:c}}return null}c=a.getToken();if("true"===c)return a.next(),{domain:"core", 66 type:"boolean",content:!0};if("false"===c)return a.next(),{domain:"core",type:"boolean",content:!1};if("random"===c)return a.next(),{domain:"core",type:"random",range:a.getValue()};if("cos"===c)return a.next(),c=a.getValue(),a.skip("radius"),a=a.getValue(),{domain:"core",type:"cos",angle_c:c,radius_c:a};if("sin"===c)return a.next(),c=a.getValue(),a.skip("radius"),a=a.getValue(),{domain:"core",type:"sin",angle_s:c,radius_s:a};if("tan"===c)return a.next(),c=a.getValue(),a.skip("radius"),a=a.getValue(), 67 {domain:"core",type:"tan",angle_t:c,radius_t:a};if("empty"===c)return a.next(),{domain:"core",type:"empty"};if("now"===c)return a.next(),{domain:"core",type:"now"};if("newline"===c)return a.next(),{domain:"core",type:"newline"};if("break"===c)return a.next(),{domain:"core",type:"break"};if("encode"===c)return a.next(),{domain:"core",type:"encode",value:a.getValue()};if("decode"===c)return a.next(),{domain:"core",type:"decode",value:a.getValue()};if("lowercase"===c)return a.next(),{domain:"core",type:"lowercase", 68 value:a.getValue()};if("element"===c){c=a.getNextValue();if(a.tokenIs("of")&&a.nextIsSymbol()){var b=a.getSymbolRecord();a.next();if("variable"===b.keyword)return{domain:"core",type:"element",element:c,symbol:b.name}}return null}if("property"===c)return c=a.getNextValue(),a.tokenIs("of")&&a.nextIsSymbol()&&(b=a.getSymbolRecord(),a.next(),"variable"===b.keyword)?{domain:"core",type:"property",property:c,symbol:b.name}:null;a.tokenIs("the")&&a.next();c=a.getToken();switch(c){case "elements":case "index":if(a.nextTokenIs("of")&& 69 a.nextIsSymbol())return b=a.getToken(),a.next(),{domain:"core",type:c,name:b};break;case "value":if(a.nextTokenIs("of"))return a.next(),{domain:"core",type:"valueOf",value:a.getValue()};break;case "length":if(a.nextTokenIs("of"))return a.next(),{domain:"core",type:"lengthOf",value:a.getValue()};break;case "left":case "right":b=a.getNextValue();if(a.tokenIs("of"))return a=a.getNextValue(),{domain:"core",type:c,count:b,value:a};break;case "from":b=a.getNextValue();var d=a.tokenIs("to")?a.getNextValue(): 70 null;if(a.tokenIs("of"))return a=a.getNextValue(),{domain:"core",type:c,from:b,to:d,value:a};break;case "position":if(a.nextTokenIs("of")&&(c=!1,a.nextTokenIs("the")&&a.nextTokenIs("last")&&(a.next(),c=!0),b=a.getValue(),a.tokenIs("in")))return a=a.getNextValue(),{domain:"core",type:"position",needle:b,haystack:a,last:c};break;case "payload":if(a.nextTokenIs("of")&&a.nextIsSymbol()&&(c=a.getSymbolRecord(),"callback"===c.keyword))return a.next(),{domain:"core",type:"payload",callback:c.name};break; 71 case "message":case "error":return a.next(),{domain:"core",type:c}}return null},get:function(a,c){switch(c.type){case "boolean":return{type:"boolean",numeric:!1,content:c.content};case "elements":return{type:"constant",numeric:!0,content:a.getSymbolRecord(c.name).elements};case "index":return{type:"constant",numeric:!0,content:a.getSymbolRecord(c.name).index};case "random":return a=a.evaluate(c.range),{type:"constant",numeric:!0,content:Math.floor(Math.random()*a.content)};case "cos":var b=a.getValue(c.angle_c); 72 a=a.getValue(c.radius_c);return{type:"constant",numeric:!0,content:parseInt(Math.cos(.01745329*parseFloat(b))*a,10)};case "sin":return b=a.getValue(c.angle_s),a=a.getValue(c.radius_s),{type:"constant",numeric:!0,content:parseInt(Math.sin(.01745329*parseFloat(b))*a,10)};case "tan":return b=a.getValue(c.angle_t),a=a.getValue(c.radius_t),{type:"constant",numeric:!0,content:parseInt(Math.tan(.01745329*parseFloat(b))*a,10)};case "valueOf":return a=parseInt(a.getValue(c.value)),{type:"constant",numeric:!0, 73 content:a?a:0};case "lengthOf":return{type:"constant",numeric:!0,content:a.getValue(c.value).length};case "left":return{type:"constant",numeric:!1,content:a.getValue(c.value).substr(0,a.getValue(c.count))};case "right":return{type:"constant",numeric:!1,content:a.getValue(c.value).substr(rstr.length-a.getValue(c.count))};case "from":b=a.getValue(c.from);var d=c.to?a.getVaue(c.to):null;a=a.getValue(c.value);return{type:"constant",numeric:!1,content:d?a.substr(b,d):a.substr(b)};case "position":return b= 74 a.getValue(c.needle),a=a.getValue(c.haystack),{type:"constant",numeric:!0,content:c.last?a.lastIndexOf(b):a.indexOf(b)};case "payload":return{type:"constant",numeric:!1,content:a.getSymbolRecord(c.callback).payload};case "modulo":return b=a.getSymbolRecord(c.name),a=a.evaluate(c.value),{type:"constant",numeric:!0,content:b.value[b.index].content%a.content};case "empty":return{type:"constant",numeric:!1,content:""};case "now":return{type:"constant",numeric:!0,content:Date.now()/1E3};case "newline":return{type:"constant", 75 numeric:!1,content:"\n"};case "break":return{type:"constant",numeric:!1,content:"<br />"};case "encode":return{type:"constant",numeric:!1,content:a.encode(a.getValue(c.value))};case "decode":return{type:"constant",numeric:!1,content:a.decode(a.getValue(c.value))};case "lowercase":return{type:"constant",numeric:!1,content:a.getValue(c.value).toLowerCase()};case "element":b=a.getValue(c.element);c=a.getSymbolRecord(c.symbol);d="";try{d=JSON.parse(a.getValue(c.value[c.index]))[b]}catch(p){a.runtimeError(command.lino, 76 "Can't parse JSON");break}return{type:"constant",numeric:!1,content:"object"===typeof d?JSON.stringify(d):d};case "property":b=a.getValue(c.property);c=a.getSymbolRecord(c.symbol);a=a.getValue(c.value[c.index]);c="";if(b&&a)if("object"===typeof a)c=a[b];else if("{"===a.charAt(0))try{c=JSON.parse(a)[b]}catch(p){console.log("Can't parse '"+a+"': "+p.message)}return{type:"constant",numeric:Number.isInteger(c),content:"object"===typeof c?JSON.stringify(c):c};case "module":return{type:"boolean",numeric:!1, 77 content:a.getSymbolRecord(c.name).program};case "message":return c=a.message,{type:"constant",numeric:!1,content:c};case "error":return c=a.errorMessage,{type:"constant",numeric:!1,content:c}}return null},put:function(a,c){a.value[a.index]=c}},condition:{compile:function(a){if(a.tokenIs("not"))return a.next(),{domain:"core",type:"not",value:a.getValue()};try{var c=a.getValue();if("is"===a.getToken()){a.next();var d=b.isNegate(a);switch(a.getToken()){case "numeric":return a.next(),{domain:"core",type:"numeric", 78 value1:c};case "even":return a.next(),{domain:"core",type:"even",value1:c};case "odd":return a.next(),{domain:"core",type:"odd",value1:c};case "greater":a.next();if(a.tokenIs("than")){a.next();var e=a.getValue();return{domain:"core",type:"greater",value1:c,value2:e,negate:d}}break;case "less":a.next();if(a.tokenIs("than")){a.next();var f=a.getValue();return{domain:"core",type:"less",value1:c,value2:f,negate:d}}break;default:var h=a.getValue();return{domain:"core",type:"is",value1:c,value2:h,negate:d}}}else if(c)return{domain:"core", 79 type:"boolean",value:c}}catch(l){return a.warning("Can't get a value"),0}return null},test:function(a,c){switch(c.type){case "boolean":return a.getValue(c.value);case "numeric":return Number.isInteger(a.getValue(c.value1));case "even":return 0===a.getValue(c.value1)%2;case "odd":return 1===a.getValue(c.value1)%2;case "is":return a=a.compare(a,c.value1,c.value2),c.negate?0!==a:0===a;case "greater":return a=a.compare(a,c.value1,c.value2),c.negate?0>=a:0<a;case "less":return a=a.compare(a,c.value1,c.value2), 80 c.negate?0<=a:0>a;case "not":return!a.getValue(c.value)}return!1}}};h.exports=b},{}],6:[function(d,h,f){var e=this,b=d("./Tokenise"),a=d("./Compile"),c=d("./Run"),g=d("./Value"),m=d("./Condition"),p=d("./Compare"),k={domain:{core:d("./Core")},setupTracer:function(){var a=document.getElementById("easycoder-tracer");a&&(a.innerHTML='<div><input id="easycoder-run-button" type="button" value="Run" /><input id="easycoder-step-button" type="button" value="Step" /><div id="easycoder-tracer-content" style="border:1px solid black;padding:4px";width:100%></div>', 81 a.style.display="none")},runtimeError:function(a,c){this.lino=a;this.reportError({message:"Line "+(0<=a?a:"")+": "+c},k.program)},nonNumericValueError:function(a){this.runtimeError(a,"Non-numeric value")},variableDoesNotHoldAValueError:function(a,c){this.runtimeError(a,"Variable '"+c+"' does not hold a value")},reportError:function(c,b,d){if(c.message)if(this.compiling||b){d=d?d:b.source;var e=d.tokens;d=d.scriptLines;e=this.compiling?e[a.getIndex()].lino:b[b.pc].lino;b=this.compiling?"Compile error": 82 "Runtime error in '"+b.script+"'";b+=":\n";var g=e-5;for(g=0>g?0:g;g<e;g++){var l=(""+(g+1)).padStart(4," ");b+=l+" "+d[g].line.split("\\s").join(" ")+"\n"}b+=c.message+"\n";c=a.getWarnings();if(c.length)for(b+="Warnings:\n",c=$jscomp.makeIterator(c),d=c.next();!d.done;d=c.next())b+=d.value+"\n";console.log(b);alert(b);k.aborted=!0}else c="Error: "+c.message,alert(c),console.log(c);else console.log("An error occurred - origin was "+c.path[0])},getSymbolRecord:function(a){a=this[this.symbols[a].pc]; 83 return a.alias?this.getSymbolRecord(a.alias):a.exporter&&a.exporter!=this?a.exporter.getSymbolRecord(a.exportedName):a},verifySymbol:function(a){return this.symbols.hasOwnProperty(a)},encode:function(a){return g.encode(a,this.encoding)},decode:function(a){return g.decode(a,this.encoding)},evaluate:function(a){return g.evaluate(this,a)},getValue:function(a){return g.getValue(this,a)},getFormattedValue:function(a){a=g.evaluate(this,a);return a.numeric?a.content:'{"'===a.content.substr(0,2)||"["===a.content.charAt(0)? 84 JSON.stringify(JSON.parse(a.content),null,2):a.content},getSimpleValue:function(a){return!0===a||!1===a?{type:"boolean",content:a}:{type:"constant",numeric:Number.isInteger(a),content:a}},run:function(a){a&&(k.program=this,c(this,a))},trigger:function(){this.onTrigger&&c(this,this.onTrigger)},register:function(a){k.program=a},require:function(a,c,b){a=document.createElement("script");a.type="text/javascript";a.src=c;a.onload=function(){console.log(Date.now()-k.timestamp+" ms: Library "+c+" loaded"); 85 b()};document.head.appendChild(a)},isUndefined:function(a){return"undefined"===typeof a},runScript:function(a){var c=a[a.pc],b=a.getValue(c.script),d=c.imports;a=c.module?a.getSymbolRecord(c.module):null;k.tokeniseScript(b.split("\n"),d,a,this)},close:function(){},compileScript:function(c,b,d,e){var f=c.tokens;this.compiling=!0;var h=Date.now();a.value=g;a.condition=m;a.domain=k.domain;a.imports=b;b=a.compile(f);var l=Date.now();console.log(l-k.timestamp+" ms: Compiled "+f.length+" tokens in "+(l- 86 h+" ms"));this.compiling=!1;b.EasyCoder=k;b.value=g;b.condition=m;b.compare=p;b.source=c;b.run=this.run;b.runScript=this.runScript;b.evaluate=this.evaluate;b.getValue=this.getValue;b.getFormattedValue=this.getFormattedValue;b.getSimpleValue=this.getSimpleValue;b.encode=this.encode;b.decode=this.decode;b.domain=this.domain;b.trigger=this.trigger;b.require=this.require;b.isUndefined=this.isUndefined;b.checkPlugin=this.checkPlugin;b.getPlugin=this.getPlugin;b.addLocalPlugin=this.addLocalPlugin;b.getPluginsPath= 87 this.getPluginsPath;b.getSymbolRecord=this.getSymbolRecord;b.verifySymbol=this.verifySymbol;b.runtimeError=this.runtimeError;b.nonNumericValueError=this.nonNumericValueError;b.variableDoesNotHoldAValueError=this.variableDoesNotHoldAValueError;b.reportError=this.reportError;b.register=this.register;b.symbols=a.getSymbols();b.encoding="ec";b.popups=[];b.stack=[];b.queue=[0];b.module=d;b.parent=e;d&&(d.program=b);return b},tokeniseScript:function(a,d,e,g){var f=null,h=Date.now();a=b.tokenise(a);var l= 88 Date.now();console.log(l-k.timestamp+" ms: Tokenised "+a.scriptLines.length+" lines in "+(l-h+" ms"));try{f=k.compileScript(a,d,e,g)}catch(u){"stop"!==u.message&&this.reportError(u,k.program,a)}this.setupTracer();f&&c(f,0)},tokenize:function(a){var b=a.split("\n");if(!e.tokenising){try{k.tokeniseScript(b)}catch(q){k.reportError(q,null,a)}e.tokenising=!0}},setPluginCount:function(a){e.plugins=[];e.pluginCount=a},checkPlugin:function(a){return k.domain[a]},getPlugin:function(a,b,c){k.domain[a]?c(): 89 (a=document.createElement("script"),a.type="text/javascript",a.src=b,a.onload=function(){console.log(Date.now()-k.timestamp+" ms: Plugin "+b+" loaded");c()},document.head.appendChild(a))},addGlobalPlugin:function(a,b){e.plugins.push({name:a,handler:b});e.plugins.length===e.pluginCount&&(e.plugins.forEach(function(a){k.domain[a.name]=a.handler}),k.tokenize(e.source))},addLocalPlugin:function(a,b,c){k.domain[a]=b;c()},getPluginsPath:function(){return k.pluginsPath},loadPluginJs:function(a){console.log(Date.now()- 90 k.timestamp+" ms: Load "+a+"/easycoder/plugins.js");var b=document.createElement("script");b.src=""+window.location.origin+a+"/easycoder/plugins.js";b.type="text/javascript";b.onload=function(){EasyCoder_Plugins.getGlobalPlugins(k.timestamp,a,k.setPluginCount,k.getPlugin,k.addGlobalPlugin)};b.onerror=function(){a?k.loadPluginJs(a.slice(0,a.lastIndexOf("/"))):k.reportError({message:"Can't load plugins.js"},k.program,e.source)};document.head.appendChild(b);k.pluginsPath=a},start:function(a){e.source= 91 a;a=window.location.pathname;a.endsWith("/")&&(a=a.slice(0,-1));k.loadPluginJs(a)}};h.exports=k},{"./Compare":2,"./Compile":3,"./Condition":4,"./Core":5,"./Run":7,"./Tokenise":8,"./Value":9}],7:[function(d,h,f){var e=function(b,a){var c=[],d=function(a){var b=9999;a.forEach(function(a){a=a.line;for(var c=0;c<a.length&&" "===a[c];)c++;0<c&&c<b&&(b=c)});return 0};if(c.length)c.push(a);else for(b.register(b),c.push(a);0<c.length&&!EasyCoder.aborted;)for(b.pc=c.shift(),b.watchdog=0,a={};;){if(1E6<b.watchdog){b.lino= 92 b[b.pc].lino;b.reportError(Error("Program runaway intercepted.\nHave you forgotten to increment a loop counter?",b),b);break}b.watchdog++;var f=b[b.pc].domain;b.debugStep&&console.log(b.script+" "+b.pc+" "+f+":"+b[b.pc].keyword);var h=b.domain[f];if(!h){b.runtimeError(b[b.pc].lino,"Unknown domain '"+f+"'");break}b.pc=h.run(b);if(!b.pc)break;if(b.stop){b.tracing=!1;return}if(b.tracing){f=b[b.pc];h=b.source.scriptLines;var k=d(h),l=document.getElementById("easycoder-tracer");if(!l){b.runtimeError(f.lino, 93 "Element 'easycoder-tracer' was not found");return}l.style.display="block";l.style.visibility="visible";var n="";if(b.tracer){if(l=document.getElementById("easycoder-tracer-content")){b.tracer.variables.forEach(function(a,c,d){var e=b.getSymbolRecord(a);if(1<e.elements)for(n+=a+": "+e.index+"/"+e.elements+": ",a=0;a<e.elements;a++){var g=e.value[a];n=g?n+(g.content+" "):n+"undefined "}else n=(e=e.value[e.index])?n+(a+": "+e.content):n+(a+": undefined");switch(b.tracer.alignment){case "horizontal":c< 94 d.length-1&&(n+=", ");break;case "vertical":n+="<br>"}});n+="<hr>";for(var q="",r=5;0<r;r--)f.lino&&(q+='<input type="text" name="'+r+'"'+('value="'+h[f.lino-r].line.substr(k)+'"')+'style="width:100%;border:none;enabled:false">'),q+="<br>";l.innerHTML=n+" "+q;l.style.display="block";a.run=document.getElementById("easycoder-run-button");a.step=document.getElementById("easycoder-step-button");a.run.onclick=function(a){return function(){a.run.blur();b.tracing=!1;document.getElementById("easycoder-tracer-content").style.display= 95 "none";try{e(b,b.resume)}catch(t){var c="Error in run handler: "+t.message;console.log(c);alert(c)}}}(a);a.step.onclick=function(a){return function(){console.log("step");a.step.blur();b.tracing=!0;document.getElementById("easycoder-tracer-content").style.display="block";try{e(b,b.resume)}catch(t){var c="Error in step handler: "+t.message;console.log(c);alert(c)}}}(a)}b.resume=b.pc;b.pc=0}break}a={run:a.run,step:a.step}}};h.exports=e},{}],8:[function(d,h,f){var e={markComments:function(b){var a=b.list, 96 c=void 0===b.index?0:b.index,d=void 0===b.inComment?!1:b.inComment,f=void 0===b.newList?[]:b.newList;if(c>=a.length)return f;var h=a[c];b=h.lino;h=h.token;var k={list:a,index:c+1,inComment:!1,newList:f.concat({lino:b,index:c,token:h})};f={list:a,index:c+1,inComment:!0,newList:f.concat({lino:b,index:c,comment:!0,token:h})};return d&&0<c&&b===a[c-1].lino?e.markComments(f):"!"===h.charAt(0)?e.markComments(f):e.markComments(k)},findStrings:function(b){var a=b.original,c=b.line,d=void 0===b.inComment? 97 !1:b.inComment;b=void 0===b.inQuote?!1:b.inQuote;var f=c.charAt(0),h=b&&[" ","\\t"].includes(f)?"\\s":f;if(1===c.length)return h;c=c.substring(1);if("!"===f&&!b)return f+e.findStrings({original:a,line:c,inComment:!0,inQuote:!1});if("`"===f&&!d&&!b)return f+e.findStrings({original:a,line:c,inComment:d,inQuote:!0});if("`"===f&&b)return f+e.findStrings({original:a,line:c,inComment:d,inQuote:!1});if(!d&&!b&&!f.match(/[A-z0-9_\-+*/\- \t]/)){if(["'",'"'].includes(f))throw Error('Bad syntax in "'+a+'":\nStrings in EasyCoder must be enclosed in backticks.'); 98 throw Error('Unrecognised character "'+f+'" in "'+a+'".');}return h+e.findStrings({original:a,line:c,inComment:d,inQuote:b})},tokenise:function(b){b=b.map(function(a){var b=a.trim();return b.length?e.findStrings({original:a,line:b}):""}).map(function(a,b){return{lino:b+1,line:a}});var a=b.map(function(a){return a.line.trim().split(/\s+/).map(function(b,c){return{lino:a.lino,index:c,token:b}})});a=[].concat.apply([],a).filter(function(a){return a.token}).map(function(a){return{lino:a.lino,index:a.index, 99 token:a.token.split("\\s").join(" ")}});a=e.markComments({list:a}).filter(function(a){return!a.comment});return{scriptLines:b,tokens:a}}};h.exports=e},{}],9:[function(d,h,f){var e={getItem:function(b){var a=b.getToken();if(!a)return null;if("true"===a)return b.next(),{type:"boolean",content:!0};if("false"===a)return b.next(),{type:"boolean",content:!1};if("`"===a.charAt(0))return b.next(),{type:"constant",numeric:!1,content:a.substring(1,a.length-1)};if(a.charAt(0).match(/[0-9-]/)){var c=eval(a); 100 if(Number.isInteger(c))return b.next(),{type:"constant",numeric:!0,content:c};throw Error("'"+a+"' is not an integer");}a=b.getIndex();c=$jscomp.makeIterator(Object.keys(b.domain));for(var d=c.next();!d.done;d=c.next())if(d=d.value,b.rewindTo(a),d=b.domain[d].value.compile(b))return d;return null},compile:function(b){var a=b.getToken(),c=e.getItem(b);if(!c)throw Error("Undefined value: '"+a+"'");if("cat"===b.getToken()){for(a={type:"cat",numeric:!1,parts:[c]};b.tokenIs("cat");)b.next(),a.parts.push(b.value.getItem(b)); 101 return a}return c},doValue:function(b,a){switch(a.type){case "cat":return{type:"constant",numeric:!1,content:a.parts.reduce(function(a,c){return a+e.doValue(b,c).content},"")};case "boolean":case "constant":return a;case "symbol":var c=b.getSymbolRecord(a.name);if(c.isValueHolder){if(a=c.value[c.index]){c=a.content;if(null===c||"undefined"===typeof c)a.content=a.numeric?0:"";return a}return null}return b.domain[c.domain].value.get(b,a)}return b.domain[a.domain].value.get(b,a)},constant:function(b, 102 a){return{type:"constant",numeric:a,content:b}},evaluate:function(b,a){if(!a)return{type:"constant",numeric:!1,content:""};var c=e.doValue(b,a);if(c)return c;b.runtimeError(b[b.pc].lino,"Can't decode value: "+a)},getValue:function(b,a){return e.evaluate(b,a).content},encode:function(b,a){if(b)switch(a){case "ec":return b.replace(/'/g,"~sq~").replace(/"/g,"~dq~").replace(/\n/g,"%0a").replace(/\r/g,"%0d");case "url":return encodeURIComponent(b.replace(/\s/g,"+"));case "sanitize":return b.normalize("NFD").replace(/[\u0300-\u036f]/g, 103 "")}return b},decode:function(b,a){if(b)switch(a){case "ec":return b.replace(/~dq~/g,'"').replace(/~sq~/g,"'").replace(/%0a/g,"\n").replace(/%0d/g,"\r");case "url":return decodeURIComponent(b).replace(/\+/g," ")}return b}};h.exports=e},{}]},{},[1]); -
easycoder/trunk/easycoder.js
r2016526 r2038748 2 2 // EasyCoder 3 3 4 const EasyCoder = require( './easycoder/Main');4 const EasyCoder = require(`./easycoder/Main`); 5 5 6 6 EasyCoder.timestamp = Date.now(); … … 8 8 9 9 window.onload = function () { 10 console.log(`${Date.now() - EasyCoder.timestamp} ms: Page loaded; reset timer & start EasyCoder`);11 EasyCoder.timestamp = Date.now();12 EasyCoder.scripts = {};13 window.EasyCoder = EasyCoder;14 const script = document.getElementById('easycoder-script');15 if (script) {16 script.style.display = 'none';17 try {18 EasyCoder.start(script.innerText);19 } catch (err) {20 EasyCoder.reportError(err);21 }22 }10 console.log(`${Date.now() - EasyCoder.timestamp} ms: Page loaded; reset timer & start EasyCoder`); 11 EasyCoder.timestamp = Date.now(); 12 EasyCoder.scripts = {}; 13 window.EasyCoder = EasyCoder; 14 const script = document.getElementById(`easycoder-script`); 15 if (script) { 16 script.style.display = `none`; 17 try { 18 EasyCoder.start(script.innerText); 19 } catch (err) { 20 EasyCoder.reportError(err); 21 } 22 } 23 23 }; 24 24 },{"./easycoder/Main":6}],2:[function(require,module,exports){ … … 35 35 v2 = v2.toString(); 36 36 } 37 if (!val1.numeric && typeof v1 === `undefined`) { 38 v1 = ``; 39 } 40 if (!val2.numeric && typeof v2 === `undefined`) { 41 v2 = ``; 42 } 37 43 if (v1 > v2) { 38 44 return 1; … … 216 222 rewind: () => { 217 223 _this.index = _this.savedMark; 224 }, 225 226 rewindTo: index => { 227 _this.index = index; 218 228 }, 219 229 … … 301 311 return; 302 312 } 303 // console.log(`Compile keyword '${keyword}'`);313 // console.log(`Compile keyword '${keyword}'`); 304 314 _this.warnings = []; 305 315 const pc = _this.program.length; … … 544 554 }, 545 555 556 Callback: { 557 558 compile: compiler => { 559 compiler.compileVariable(`core`, `callback`); 560 return true; 561 }, 562 563 run: program => { 564 return program[program.pc].pc + 1; 565 } 566 }, 546 567 Clear: { 547 568 … … 550 571 compiler.next(); 551 572 if (compiler.isSymbol()) { 552 const symbol = compiler.getToken(); 553 compiler.next(); 554 compiler.addCommand({ 555 domain: `core`, 556 keyword: `clear`, 557 lino, 558 symbol 559 }); 560 return true; 573 const symbolRecord = compiler.getSymbolRecord(); 574 if (symbolRecord.isValueHolder) { 575 const symbol = compiler.getToken(); 576 compiler.next(); 577 compiler.addCommand({ 578 domain: `core`, 579 keyword: `clear`, 580 lino, 581 symbol 582 }); 583 return true; 584 } 585 compiler.warning(`'Variable '${symbolRecord.name}' does not hold a value`); 561 586 } 562 587 return false; … … 768 793 } else { 769 794 // Here we should already have the target. 770 if (t arget === undefined) {795 if (typeof target === `undefined`) { 771 796 compiler.warning(`core ${_this.name}: No target variable given`); 772 797 } … … 869 894 const lino = compiler.getLino(); 870 895 compiler.next(); 871 if (compiler. tokenIs(`to`)) {896 if (compiler.nextTokenIs(`to`)) { 872 897 compiler.next(); 873 898 } … … 899 924 compile: compiler => { 900 925 const lino = compiler.getLino(); 901 compiler.next(); 902 if (compiler.tokenIs(`to`)) { 926 if (compiler.nextTokenIs(`to`)) { 903 927 compiler.next(); 904 928 } … … 922 946 return pc.pc; 923 947 } 924 program.runtimeError(command.lino, `Unknown symbol '${command.label}'`);925 return 0;926 }948 } 949 program.runtimeError(command.lino, `Unknown symbol '${command.label}'`); 950 return 0; 927 951 } 928 952 return command.goto; … … 934 958 compile: compiler => { 935 959 const lino = compiler.getLino(); 936 compiler.next(); 937 if (compiler.tokenIs(`to`)) { 960 if (compiler.nextTokenIs(`to`)) { 938 961 compiler.next(); 939 962 } … … 1036 1059 } 1037 1060 } 1061 if (compiler.tokenIs(`and`)) { 1062 throw new Error(`Imports do not match exports`); 1063 } 1038 1064 return true; 1039 1065 }, … … 1088 1114 compile: compiler => { 1089 1115 const lino = compiler.getLino(); 1090 compiler.next(); 1091 if (compiler.tokenIs(`plugin`)) { 1092 compiler.next(); 1093 const name = compiler.getValue(); 1094 compiler.addCommand({ 1095 domain: `core`, 1096 keyword: `load`, 1097 lino, 1098 name 1099 }); 1100 return true; 1101 } 1116 const type = compiler.nextToken(); 1117 switch (type) { 1118 case `plugin`: 1119 const name = compiler.getNextValue(); 1120 compiler.addCommand({ 1121 domain: `core`, 1122 keyword: `load`, 1123 lino, 1124 name 1125 }); 1126 return true; 1127 } 1128 return false; 1102 1129 }, 1103 1130 … … 1105 1132 const command = program[program.pc]; 1106 1133 const name = program.getValue(command.name); 1107 if (program.checkPlugin(name)) { 1108 return command.pc + 1; 1109 } 1110 EasyCoder_Plugins.getLocalPlugin(program.getPluginsPath, name, program.getPlugin, program.addLocalPlugin, function () { 1111 program.run(command.pc + 1); 1112 }); 1113 return 0; 1134 switch (command.keyword) { 1135 case `load`: 1136 if (program.checkPlugin(name)) { 1137 return command.pc + 1; 1138 } 1139 EasyCoder_Plugins.getLocalPlugin(program.getPluginsPath, name, program.getPlugin, program.addLocalPlugin, function () { 1140 program.run(command.pc + 1); 1141 }); 1142 return 0; 1143 } 1114 1144 } 1115 1145 }, … … 1168 1198 } else { 1169 1199 // Here we should already have the target. 1170 if (t arget === undefined) {1200 if (typeof target === `undefined`) { 1171 1201 compiler.warning(`core multiply: No target variable given`); 1172 1202 } … … 1255 1285 const lino = compiler.getLino(); 1256 1286 const action = compiler.nextToken(); 1257 compiler.next();1258 1287 switch (action) { 1259 1288 case `trigger`: 1260 1289 case `close`: 1261 1290 case `restore`: 1291 case `message`: 1292 compiler.next(); 1262 1293 compiler.addCommand({ 1263 1294 domain: `core`, … … 1268 1299 return compiler.completeHandler(); 1269 1300 } 1301 if (compiler.isSymbol()) { 1302 const symbolRecord = compiler.getSymbolRecord(); 1303 if (symbolRecord.keyword === `callback`) { 1304 compiler.next(); 1305 compiler.addCommand({ 1306 domain: `core`, 1307 keyword: `on`, 1308 lino, 1309 action: symbolRecord.name 1310 }); 1311 return compiler.completeHandler(); 1312 } 1313 } 1270 1314 return false; 1271 1315 }, … … 1273 1317 run: program => { 1274 1318 const command = program[program.pc]; 1319 const cb = command.pc + 2; 1275 1320 switch (command.action) { 1276 1321 case `trigger`: 1277 program.onTrigger = c ommand.pc + 2;1322 program.onTrigger = cb; 1278 1323 break; 1279 1324 case `close`: 1280 program.onClose = c ommand.pc + 2;1325 program.onClose = cb; 1281 1326 break; 1282 1327 case `restore`: 1283 program.onRestore = command.pc + 2; 1328 program.onRestore = cb; 1329 break; 1330 case `message`: 1331 program.onMessage = cb; 1284 1332 break; 1285 1333 default: 1286 program.runtimeError(command.lino, `Unknown action '${command.action}'`); 1287 return 0; 1334 const callbacklRecord = program.getSymbolRecord(command.action); 1335 if (callbacklRecord) { 1336 callbacklRecord.cb = cb; 1337 } else { 1338 program.runtimeError(command.lino, `Unknown action '${command.action}'`); 1339 return 0; 1340 } 1288 1341 } 1289 1342 return command.pc + 1; … … 1394 1447 const command = program[program.pc]; 1395 1448 const original = program.getValue(command.original); 1396 const org = original.split(`\\r`).join(`\r`).split(`\\n`).join(`\n`);1449 // const org = original.split(`\\r`).join(`\r`).split(`\\n`).join(`\n`); 1397 1450 const replacement = program.getValue(command.replacement); 1398 1451 const target = program.getSymbolRecord(command.target); 1399 1452 const value = program.getValue(target.value[target.index]); 1400 const content = value.split(or g).join(replacement);1453 const content = value.split(original).join(replacement); 1401 1454 target.value[target.index] = { 1402 1455 type: `constant`, … … 1502 1555 }, 1503 1556 1557 Send: { 1558 1559 compile: compiler => { 1560 const lino = compiler.getLino(); 1561 const message = compiler.getNextValue(); 1562 var recipient; 1563 if (compiler.tokenIs(`to`)) { 1564 if (compiler.nextTokenIs(`parent`)) { 1565 recipient = `parent`; 1566 } else if (compiler.isSymbol) { 1567 const moduleRecord = compiler.getSymbolRecord(); 1568 if (moduleRecord.keyword !== `module`) { 1569 throw new Error(`'${moduleRecord.name}' is not a module`); 1570 } 1571 recipient = moduleRecord.name; 1572 } 1573 compiler.next(); 1574 compiler.addCommand({ 1575 domain: `core`, 1576 keyword: `send`, 1577 lino, 1578 message, 1579 recipient 1580 }); 1581 } 1582 return true; 1583 }, 1584 1585 run: program => { 1586 const command = program[program.pc]; 1587 const message = program.getValue(command.message); 1588 if (command.recipient === `parent`) { 1589 const parent = program.parent; 1590 if (parent) { 1591 const onMessage = program.parent.onMessage; 1592 if (onMessage) { 1593 parent.message = message; 1594 parent.run(parent.onMessage); 1595 } 1596 } 1597 } else { 1598 const recipient = program.getSymbolRecord(command.recipient); 1599 recipient.program.message = message; 1600 recipient.program.run(recipient.program.onMessage); 1601 } 1602 return command.pc + 1; 1603 } 1604 }, 1605 1504 1606 Set: { 1505 1607 1506 1608 compile: compiler => { 1507 1609 const lino = compiler.getLino(); 1508 compiler.next(); 1509 if (compiler.isSymbol()) { 1610 if (compiler.nextIsSymbol()) { 1510 1611 const targetRecord = compiler.getSymbolRecord(); 1511 1612 if (!targetRecord.isValueHolder) { 1512 1613 return false; 1513 1614 } 1514 compiler.next(); 1515 if (compiler.tokenIs(`to`)) { 1615 if (compiler.nextTokenIs(`to`)) { 1516 1616 compiler.next(); 1517 1617 const value = []; … … 1529 1629 keyword: `set`, 1530 1630 lino, 1531 type: `setArray`,1631 request: `setArray`, 1532 1632 target: targetRecord.name, 1533 1633 value … … 1539 1639 keyword: `set`, 1540 1640 lino, 1541 type: `setBoolean`,1641 request: `setBoolean`, 1542 1642 target: targetRecord.name 1543 1643 }); … … 1550 1650 keyword: `set`, 1551 1651 lino, 1552 type: `setReady`1652 request: `setReady` 1553 1653 }); 1554 1654 return true; 1655 } 1656 if (compiler.tokenIs(`property`)) { 1657 const name = compiler.getNextValue(); 1658 if (compiler.tokenIs(`of`)) { 1659 if (compiler.nextIsSymbol()) { 1660 const targetRecord = compiler.getSymbolRecord(); 1661 if (targetRecord.keyword === `variable`) { 1662 if (compiler.nextTokenIs(`to`)) { 1663 const value = compiler.getNextValue(); 1664 compiler.addCommand({ 1665 domain: `core`, 1666 keyword: `set`, 1667 lino, 1668 request: `setProperty`, 1669 target: targetRecord.name, 1670 name, 1671 value 1672 }); 1673 return true; 1674 } 1675 } 1676 } 1677 } 1555 1678 } 1556 1679 if (compiler.tokenIs(`the`)) { … … 1574 1697 keyword: `set`, 1575 1698 lino, 1576 type: `setElements`,1699 request: `setElements`, 1577 1700 symbol, 1578 1701 value … … 1583 1706 } 1584 1707 if (compiler.tokenIs(`encoding`)) { 1585 compiler.next(); 1586 if (compiler.tokenIs(`to`)) { 1587 compiler.next(); 1588 const encoding = compiler.getValue(); 1708 if (compiler.nextTokenIs(`to`)) { 1709 const encoding = compiler.getNextValue(); 1589 1710 compiler.addCommand({ 1590 1711 domain: `core`, 1591 1712 keyword: `set`, 1592 type: `encoding`,1713 request: `encoding`, 1593 1714 lino, 1594 1715 encoding … … 1599 1720 return false; 1600 1721 } 1722 if (compiler.tokenIs(`payload`)) { 1723 if (compiler.nextTokenIs(`of`)) { 1724 if (compiler.nextIsSymbol()) { 1725 const callbackRecord = compiler.getSymbolRecord(); 1726 if (callbackRecord.keyword === `callback`) { 1727 if (compiler.nextTokenIs(`to`)) { 1728 const payload = compiler.getNextValue(); 1729 compiler.addCommand({ 1730 domain: `core`, 1731 keyword: `set`, 1732 request: `setPayload`, 1733 lino, 1734 callback: callbackRecord.name, 1735 payload 1736 }); 1737 return true; 1738 } 1739 } 1740 } 1741 } 1742 } 1601 1743 return false; 1602 1744 }, 1603 1745 1604 1746 run: program => { 1747 let targetRecord; 1605 1748 const command = program[program.pc]; 1606 switch (command. type) {1749 switch (command.request) { 1607 1750 case `setBoolean`: 1608 1751 const target = program.getSymbolRecord(command.target); … … 1625 1768 symbol.index = 0; 1626 1769 symbol.value = []; 1770 symbol.element = []; 1627 1771 for (var n = 0; n < symbol.elements; n++) { 1628 1772 symbol.value.push({}); 1629 symbol.element.push( ``);1773 symbol.element.push({}); 1630 1774 } 1631 1775 break; 1632 1776 case `setArray`: 1633 consttargetRecord = program.getSymbolRecord(command.target);1777 targetRecord = program.getSymbolRecord(command.target); 1634 1778 targetRecord.elements = command.value.length; 1635 1779 targetRecord.value = command.value; … … 1637 1781 case `encoding`: 1638 1782 program.encoding = program.getValue(command.encoding); 1783 break; 1784 case `setProperty`: 1785 targetRecord = program.getSymbolRecord(command.target); 1786 let targetValue = program.getValue(targetRecord.value[targetRecord.index]); 1787 if (!targetValue) { 1788 targetValue = `{}`; 1789 } 1790 let targetJSON = ``; 1791 try { 1792 targetJSON = JSON.parse(targetValue); 1793 } catch (err) { 1794 program.runtimeError(command.lino, `Can't parse ${targetRecord.name}`); 1795 return 0; 1796 } 1797 const itemName = program.getValue(command.name); 1798 const itemValue = program.evaluate(command.value); 1799 if (itemValue) { 1800 if (itemValue.type === `boolean`) { 1801 targetJSON[itemName] = itemValue.content; 1802 } else { 1803 targetJSON[itemName] = itemValue.numeric ? itemValue.content : itemValue.content.substr(0, 2) === `{"` ? JSON.parse(itemValue.content) : itemValue.content.split(`"`).join(`\\"`); 1804 } 1805 targetRecord.value[targetRecord.index] = { 1806 type: `constant`, 1807 numeric: false, 1808 content: JSON.stringify(targetJSON) 1809 }; 1810 } 1811 break; 1812 case `setPayload`: 1813 program.getSymbolRecord(command.callback).payload = program.getValue(command.payload); 1639 1814 break; 1640 1815 default: … … 1972 2147 case `begin`: 1973 2148 return EasyCoder_Core.Begin; 2149 case `callback`: 2150 return EasyCoder_Core.Callback; 1974 2151 case `clear`: 1975 2152 return EasyCoder_Core.Clear; … … 2021 2198 case `script`: 2022 2199 return EasyCoder_Core.Script; 2200 case `send`: 2201 return EasyCoder_Core.Send; 2023 2202 case `set`: 2024 2203 return EasyCoder_Core.Set; … … 2178 2357 }; 2179 2358 } 2359 if (token === `break`) { 2360 compiler.next(); 2361 return { 2362 domain: `core`, 2363 type: `break` 2364 }; 2365 } 2180 2366 if (token === `encode`) { 2181 2367 compiler.next(); … … 2244 2430 compiler.next(); 2245 2431 } 2246 switch (compiler.getToken()) { 2432 const type = compiler.getToken(); 2433 switch (type) { 2434 case `elements`: 2247 2435 case `index`: 2248 compiler.next(); 2249 if (compiler.tokenIs(`of`)) { 2250 compiler.next(); 2251 if (compiler.isSymbol()) { 2436 if (compiler.nextTokenIs(`of`)) { 2437 if (compiler.nextIsSymbol()) { 2252 2438 const name = compiler.getToken(); 2253 2439 compiler.next(); 2254 2440 return { 2255 2441 domain: `core`, 2256 type : `index`,2442 type, 2257 2443 name 2258 2444 }; … … 2261 2447 break; 2262 2448 case `value`: 2263 compiler.next(); 2264 if (compiler.tokenIs(`of`)) { 2449 if (compiler.nextTokenIs(`of`)) { 2265 2450 compiler.next(); 2266 2451 const value = compiler.getValue(); … … 2273 2458 break; 2274 2459 case `length`: 2275 compiler.next(); 2276 if (compiler.tokenIs(`of`)) { 2460 if (compiler.nextTokenIs(`of`)) { 2277 2461 compiler.next(); 2278 2462 const value = compiler.getValue(); … … 2285 2469 break; 2286 2470 case `left`: 2287 compiler.next();2288 const leftCount = compiler.getValue();2471 case `right`: 2472 const count = compiler.getNextValue(); 2289 2473 if (compiler.tokenIs(`of`)) { 2290 compiler.next(); 2291 const leftValue = compiler.getValue(); 2474 const value = compiler.getNextValue(); 2292 2475 return { 2293 2476 domain: `core`, 2294 type : `left`,2295 count : leftCount,2296 value : leftValue2477 type, 2478 count, 2479 value 2297 2480 }; 2298 2481 } 2299 2482 break; 2300 case ` right`:2301 co mpiler.next();2302 const rightCount = compiler.getValue();2483 case `from`: 2484 const from = compiler.getNextValue(); 2485 const to = compiler.tokenIs(`to`) ? compiler.getNextValue() : null; 2303 2486 if (compiler.tokenIs(`of`)) { 2304 compiler.next(); 2305 const rightValue = compiler.getValue(); 2487 const value = compiler.getNextValue(); 2306 2488 return { 2307 2489 domain: `core`, 2308 type: `right`, 2309 count: rightCount, 2310 value: rightValue 2490 type, 2491 from, 2492 to, 2493 value 2311 2494 }; 2312 2495 } 2313 2496 break; 2314 2497 case `position`: 2315 compiler.next(); 2316 if (compiler.tokenIs(`of`)) { 2317 compiler.next(); 2498 if (compiler.nextTokenIs(`of`)) { 2318 2499 var last = false; 2319 if (compiler.tokenIs(`the`)) { 2320 compiler.next(); 2321 if (compiler.tokenIs(`last`)) { 2500 if (compiler.nextTokenIs(`the`)) { 2501 if (compiler.nextTokenIs(`last`)) { 2322 2502 compiler.next(); 2323 2503 last = true; … … 2326 2506 const needle = compiler.getValue(); 2327 2507 if (compiler.tokenIs(`in`)) { 2328 compiler.next(); 2329 const haystack = compiler.getValue(); 2508 const haystack = compiler.getNextValue(); 2330 2509 return { 2331 2510 domain: `core`, … … 2338 2517 } 2339 2518 break; 2519 case `payload`: 2520 if (compiler.nextTokenIs(`of`)) { 2521 if (compiler.nextIsSymbol()) { 2522 const callbackRecord = compiler.getSymbolRecord(); 2523 if (callbackRecord.keyword === `callback`) { 2524 compiler.next(); 2525 return { 2526 domain: `core`, 2527 type: `payload`, 2528 callback: callbackRecord.name 2529 }; 2530 } 2531 } 2532 } 2533 break; 2534 case `message`: 2535 case `error`: 2536 compiler.next(); 2537 return { 2538 domain: `core`, 2539 type 2540 }; 2340 2541 } 2341 2542 return null; … … 2349 2550 numeric: false, 2350 2551 content: value.content 2552 }; 2553 case `elements`: 2554 return { 2555 type: `constant`, 2556 numeric: true, 2557 content: program.getSymbolRecord(value.name).elements 2351 2558 }; 2352 2559 case `index`: … … 2407 2614 }; 2408 2615 case `right`: 2409 const rstr = program.getValue(value.value);2410 2616 return { 2411 2617 type: `constant`, 2412 2618 numeric: false, 2413 content: rstr.substr(rstr.length - program.getValue(value.count)) 2619 content: program.getValue(value.value).substr(rstr.length - program.getValue(value.count)) 2620 }; 2621 case `from`: 2622 const from = program.getValue(value.from); 2623 const to = value.to ? program.getVaue(value.to) : null; 2624 const fstr = program.getValue(value.value); 2625 return { 2626 type: `constant`, 2627 numeric: false, 2628 content: to ? fstr.substr(from, to) : fstr.substr(from) 2414 2629 }; 2415 2630 case `position`: … … 2420 2635 numeric: true, 2421 2636 content: value.last ? haystack.lastIndexOf(needle) : haystack.indexOf(needle) 2637 }; 2638 case `payload`: 2639 return { 2640 type: `constant`, 2641 numeric: false, 2642 content: program.getSymbolRecord(value.callback).payload 2422 2643 }; 2423 2644 case `modulo`: … … 2446 2667 numeric: false, 2447 2668 content: `\n` 2669 }; 2670 case `break`: 2671 return { 2672 type: `constant`, 2673 numeric: false, 2674 content: `<br />` 2448 2675 }; 2449 2676 case `encode`: … … 2485 2712 const propertyContent = program.getValue(propertyRecord.value[propertyRecord.index]); 2486 2713 var content = ``; 2487 if (typeof propertyContent === `object`) { 2488 content = propertyContent[property]; 2489 } else { 2490 try { 2491 content = JSON.parse(propertyContent)[property]; 2492 } catch (err) { 2493 console.log(`Can't parse '${propertyContent}': ${err.message}`); 2494 content = ``; 2714 if (property && propertyContent) { 2715 if (typeof propertyContent === `object`) { 2716 content = propertyContent[property]; 2717 } else if (propertyContent.charAt(0) === `{`) { 2718 try { 2719 content = JSON.parse(propertyContent)[property]; 2720 } catch (err) { 2721 console.log(`Can't parse '${propertyContent}': ${err.message}`); 2722 } 2495 2723 } 2496 2724 } … … 2498 2726 type: `constant`, 2499 2727 numeric: Number.isInteger(content), 2500 content 2728 content: typeof content === `object` ? JSON.stringify(content) : content 2501 2729 }; 2502 2730 case `module`: … … 2506 2734 numeric: false, 2507 2735 content: module.program 2736 }; 2737 case `message`: 2738 content = program.message; 2739 return { 2740 type: `constant`, 2741 numeric: false, 2742 content 2743 }; 2744 case `error`: 2745 content = program.errorMessage; 2746 return { 2747 type: `constant`, 2748 numeric: false, 2749 content 2508 2750 }; 2509 2751 } … … 2603 2845 } 2604 2846 } catch (err) { 2605 program.runtimeError(command.lino, err.message);2847 compiler.warning(`Can't get a value`); 2606 2848 return 0; 2607 2849 } … … 2748 2990 2749 2991 getFormattedValue: function (value) { 2750 const v = EasyCoder_Value.getValue(this, value); 2751 if ([`[`, `{`].includes(v[0])) { 2752 return JSON.stringify(JSON.parse(v), null, 2); 2753 } 2754 return v; 2992 const v = EasyCoder_Value.evaluate(this, value); 2993 if (v.numeric) { 2994 return v.content; 2995 } 2996 if (v.content.substr(0, 2) === `{"` || v.content.charAt(0) === `[`) { 2997 return JSON.stringify(JSON.parse(v.content), null, 2); 2998 } 2999 return v.content; 2755 3000 }, 2756 3001 … … 2770 3015 2771 3016 run: function (pc) { 2772 EasyCoder.program = this; 2773 EasyCoder_Run(this, pc); 3017 if (pc) { 3018 EasyCoder.program = this; 3019 EasyCoder_Run(this, pc); 3020 } 2774 3021 }, 2775 3022 … … 2780 3027 }, 2781 3028 2782 addJS: function (name, path, loaded) { 3029 register: program => { 3030 EasyCoder.program = program; 3031 }, 3032 3033 require: (program, src, cb) => { 2783 3034 const script = document.createElement(`script`); 2784 3035 script.type = `text/javascript`; 2785 script.src = `${path}/${name}.js`;3036 script.src = src; 2786 3037 script.onload = function () { 2787 loaded(name); 3038 console.log(`${Date.now() - EasyCoder.timestamp} ms: Library ${src} loaded`); 3039 cb(); 2788 3040 }; 2789 3041 document.head.appendChild(script); 2790 3042 }, 2791 3043 2792 register: program => {2793 EasyCoder.program = program;3044 isUndefined: item => { 3045 return typeof item === `undefined`; 2794 3046 }, 2795 3047 … … 2836 3088 program.domain = this.domain; 2837 3089 program.trigger = this.trigger; 2838 program.addJS = this.addJS; 3090 program.require = this.require; 3091 program.isUndefined = this.isUndefined; 2839 3092 program.checkPlugin = this.checkPlugin; 2840 3093 program.getPlugin = this.getPlugin; … … 2858 3111 module.program = program; 2859 3112 } 2860 this.setupTracer(); 2861 // console.log('Run the script'); 2862 EasyCoder_Run(program, 0); 2863 // console.log('Program: '+ JSON.stringify(program, null, 2)); 3113 return program; 2864 3114 }, 2865 3115 2866 3116 tokeniseScript: function (file, imports, module, parent) { 2867 3117 // console.log('Tokenise script: '); 3118 let program = null; 2868 3119 const startTokenise = Date.now(); 2869 3120 const source = EasyCoder_Tokenise.tokenise(file); … … 2872 3123 // console.log('Source: ' + JSON.stringify(source, null, 2)); 2873 3124 try { 2874 EasyCoder.compileScript(source, imports, module, parent);3125 program = EasyCoder.compileScript(source, imports, module, parent); 2875 3126 } catch (err) { 2876 3127 if (err.message !== `stop`) { 2877 3128 this.reportError(err, EasyCoder.program, source); 2878 3129 } 3130 } 3131 this.setupTracer(); 3132 // console.log('Run the script'); 3133 if (program) { 3134 // console.log('Program: '+ JSON.stringify(program, null, 2)); 3135 EasyCoder_Run(program, 0); 2879 3136 } 2880 3137 }, … … 2938 3195 }, 2939 3196 2940 loadPluginJs: pathname => { 2941 console.log(`${Date.now() - EasyCoder.timestamp} ms: Load plugins.js`); 2942 const fullPath = `${window.location.origin}${pathname}/easycoder/plugins.js`; 2943 const ajax = new XMLHttpRequest(); 2944 ajax.open(`GET`, fullPath, true); 2945 ajax.onreadystatechange = function () { 2946 if (ajax.readyState === 4) { 2947 switch (ajax.status) { 2948 case 200: 2949 const script = document.createElement(`script`); 2950 script.src = `${window.location.origin}${pathname}/easycoder/plugins.js`; 2951 script.type = `text/javascript`; 2952 script.onload = function () { 2953 EasyCoder_Plugins.getGlobalPlugins(EasyCoder.timestamp, pathname, EasyCoder.setPluginCount, EasyCoder.getPlugin, EasyCoder.addGlobalPlugin); 2954 }; 2955 document.head.appendChild(script); 2956 EasyCoder.pluginsPath = pathname; 2957 break; 2958 case 404: 2959 if (pathname) { 2960 EasyCoder.loadPluginJs(pathname.slice(0, pathname.lastIndexOf(`/`))); 2961 } else { 2962 EasyCoder.reportError({ 2963 message: `Can't load plugins.js` 2964 }, EasyCoder.program, this.source); 2965 } 2966 break; 2967 } 2968 } 3197 loadPluginJs: path => { 3198 console.log(`${Date.now() - EasyCoder.timestamp} ms: Load ${path}/easycoder/plugins.js`); 3199 const script = document.createElement(`script`); 3200 script.src = `${window.location.origin}${path}/easycoder/plugins.js`; 3201 script.type = `text/javascript`; 3202 script.onload = function () { 3203 EasyCoder_Plugins.getGlobalPlugins(EasyCoder.timestamp, path, EasyCoder.setPluginCount, EasyCoder.getPlugin, EasyCoder.addGlobalPlugin); 2969 3204 }; 2970 ajax.send(); 3205 script.onerror = () => { 3206 if (path) { 3207 EasyCoder.loadPluginJs(path.slice(0, path.lastIndexOf(`/`))); 3208 } else { 3209 EasyCoder.reportError({ 3210 message: `Can't load plugins.js` 3211 }, EasyCoder.program, _this.source); 3212 } 3213 }; 3214 document.head.appendChild(script); 3215 EasyCoder.pluginsPath = path; 2971 3216 }, 2972 3217 … … 3189 3434 }) => { 3190 3435 const c = line.charAt(0); 3191 const ch = inQuote && c === ` `? `\\s` : c;3436 const ch = inQuote && [` `, `\\t`].includes(c) ? `\\s` : c; 3192 3437 if (line.length === 1) { 3193 3438 return ch; … … 3203 3448 return c + EasyCoder_Tokenise.findStrings({ original, line: tail, inComment, inQuote: false }); 3204 3449 } 3205 if (!inComment && !inQuote && !c.match(/[A-z0-9_\-+*/\- ]/)) {3206 if ( c == `'` || c == `"`) {3450 if (!inComment && !inQuote && !c.match(/[A-z0-9_\-+*/\- \t]/)) { 3451 if ([`'`, `"`].includes(c)) { 3207 3452 throw new Error(`Bad syntax in "${original}":\nStrings in EasyCoder must be enclosed in backticks.`); 3208 3453 } else { … … 3220 3465 3221 3466 // Convert quoted spaces to \\s 3222 const markedSpaces = file.map(line => { 3467 const markedSpaces = file.map(original => { 3468 const line = original.trim(); 3223 3469 if (line.length) { 3224 return EasyCoder_Tokenise.findStrings({ original : line, line });3470 return EasyCoder_Tokenise.findStrings({ original, line }); 3225 3471 } 3226 3472 return ``; … … 3270 3516 const EasyCoder_Value = { 3271 3517 3272 getItem: compiler => { 3273 const token = compiler.getToken(); 3274 if (!token) { 3275 return null; 3276 } 3277 3278 // Check for a boolean 3279 if (token === 'true') { 3280 compiler.next(); 3281 return { 3282 type: 'boolean', 3283 content: true 3284 }; 3285 } 3286 3287 if (token === 'false') { 3288 compiler.next(); 3289 return { 3290 type: 'boolean', 3291 content: false 3292 }; 3293 } 3294 3295 // Check for a string constant 3296 if (token.charAt(0) === '`') { 3297 compiler.next(); 3298 const value = { 3299 type: 'constant', 3300 numeric: false, 3301 content: token.substring(1, token.length - 1) 3302 }; 3303 return value; 3304 } 3305 3306 // Check for a numeric constant 3307 if (token[0].match(/[0-9\-]/)) { 3308 const val = eval(token); 3309 if (Number.isInteger(val)) { 3310 compiler.next(); 3311 const value = { 3312 type: 'constant', 3313 numeric: true, 3314 content: val 3315 }; 3316 return value; 3317 } else { 3318 throw new Error(`'${token}' is not an integer`); 3319 } 3320 } 3321 3322 // See if any of the domains can handle it 3323 for (const name of Object.keys(compiler.domain)) { 3324 const handler = compiler.domain[name]; 3325 const code = handler.value.compile(compiler); 3326 if (code) { 3327 return code; 3328 } 3329 } 3330 return null; 3331 }, 3332 3333 compile: compiler => { 3334 const token = compiler.getToken(); 3335 const item = compiler.value.getItem(compiler); 3336 if (!item) { 3337 throw new Error(`Undefined value: '${token}'`); 3338 } 3339 3340 if (compiler.getToken() === 'cat') { 3341 const value = { 3342 type: 'cat', 3343 numeric: false, 3344 parts: [item] 3345 }; 3346 while (compiler.tokenIs('cat')) { 3347 compiler.next(); 3348 value.parts.push(compiler.value.getItem(compiler)); 3349 } 3350 return value; 3351 } 3352 3353 return item; 3354 }, 3355 3356 // runtime 3357 3358 doValue: (program, value) => { 3359 // console.log('Value:doValue:value: '+JSON.stringify(value,null,2)); 3360 // See if it's a constant string, a variable or something else 3361 const type = value.type; 3362 switch (type) { 3363 case 'cat': 3364 return { 3365 type: 'constant', 3366 numeric: false, 3367 content: value.parts.reduce(function (acc, part) { 3368 return acc + EasyCoder_Value.doValue(program, part).content; 3369 }, '') 3370 }; 3371 case 'boolean': 3372 case 'constant': 3373 return value; 3374 case 'symbol': 3375 const symbol = program.getSymbolRecord(value.name); 3376 if (symbol.isValueHolder) { 3377 const symbolValue = symbol.value[symbol.index]; 3378 if (symbolValue) { 3379 const v = symbolValue.content; 3380 if (v === null) { 3381 symbolValue.content = symbolValue.numeric ? 0 : ''; 3382 } 3383 return symbolValue; 3384 } else { 3385 return null; 3386 } 3387 } else { 3388 const handler = program.domain[symbol.domain].value; 3389 return handler.get(program, value); 3390 } 3391 break; 3392 default: 3393 break; 3394 } 3395 // Call the given domain to handle a value 3396 const handler = program.domain[value.domain].value; 3397 return handler.get(program, value); 3398 }, 3399 3400 constant: (content, numeric) => { 3401 return { 3402 type: 'constant', 3403 numeric, 3404 content 3405 }; 3406 }, 3407 3408 // runtime 3409 3410 evaluate: (program, value) => { 3411 if (!value) { 3412 return { 3413 type: 'constant', 3414 numeric: false, 3415 content: '' 3416 }; 3417 } 3418 const result = EasyCoder_Value.doValue(program, value); 3419 if (result) { 3420 return result; 3421 } 3422 program.runtimeError(program[program.pc].lino, 'Can\'t decode value: ' + value); 3423 }, 3424 3425 getValue: (program, value) => { 3426 return EasyCoder_Value.evaluate(program, value).content; 3427 }, 3428 3429 // tools 3430 3431 encode: (value, encoding) => { 3432 if (value) { 3433 switch (encoding) { 3434 case 'ec': 3435 return value.replace(/\'/g, "~sq~").replace(/\"/g, '~dq~'); 3436 case 'url': 3437 return encodeURIComponent(value.replace(/\s/g, '+')); 3438 } 3439 } 3440 return value; 3441 }, 3442 3443 decode: (value, encoding) => { 3444 if (value) { 3445 switch (encoding) { 3446 case 'ec': 3447 return value.replace(/~dq~/g, '"').replace(/~sq~/g, "'"); 3448 case 'url': 3449 const decoded = decodeURIComponent(value); 3450 return decoded.replace(/\+/g, ' '); 3451 } 3452 } 3453 return value; 3454 } 3518 getItem: compiler => { 3519 const token = compiler.getToken(); 3520 if (!token) { 3521 return null; 3522 } 3523 3524 // Check for a boolean 3525 if (token === `true`) { 3526 compiler.next(); 3527 return { 3528 type: `boolean`, 3529 content: true 3530 }; 3531 } 3532 3533 if (token === `false`) { 3534 compiler.next(); 3535 return { 3536 type: `boolean`, 3537 content: false 3538 }; 3539 } 3540 3541 // Check for a string constant 3542 if (token.charAt(0) === `\``) { 3543 compiler.next(); 3544 const value = { 3545 type: `constant`, 3546 numeric: false, 3547 content: token.substring(1, token.length - 1) 3548 }; 3549 return value; 3550 } 3551 3552 // Check for a numeric constant 3553 if (token.charAt(0).match(/[0-9-]/)) { 3554 const val = eval(token); 3555 if (Number.isInteger(val)) { 3556 compiler.next(); 3557 const value = { 3558 type: `constant`, 3559 numeric: true, 3560 content: val 3561 }; 3562 return value; 3563 } else { 3564 throw new Error(`'${token}' is not an integer`); 3565 } 3566 } 3567 3568 // See if any of the domains can handle it 3569 const index = compiler.getIndex(); 3570 for (const name of Object.keys(compiler.domain)) { 3571 compiler.rewindTo(index); 3572 const handler = compiler.domain[name]; 3573 const code = handler.value.compile(compiler); 3574 if (code) { 3575 return code; 3576 } 3577 } 3578 return null; 3579 }, 3580 3581 compile: compiler => { 3582 const token = compiler.getToken(); 3583 const item = EasyCoder_Value.getItem(compiler); 3584 if (!item) { 3585 throw new Error(`Undefined value: '${token}'`); 3586 } 3587 3588 if (compiler.getToken() === `cat`) { 3589 const value = { 3590 type: `cat`, 3591 numeric: false, 3592 parts: [item] 3593 }; 3594 while (compiler.tokenIs(`cat`)) { 3595 compiler.next(); 3596 value.parts.push(compiler.value.getItem(compiler)); 3597 } 3598 return value; 3599 } 3600 3601 return item; 3602 }, 3603 3604 // runtime 3605 3606 doValue: (program, value) => { 3607 // console.log('Value:doValue:value: '+JSON.stringify(value,null,2)); 3608 // See if it's a constant string, a variable or something else 3609 const type = value.type; 3610 switch (type) { 3611 case `cat`: 3612 return { 3613 type: `constant`, 3614 numeric: false, 3615 content: value.parts.reduce(function (acc, part) { 3616 return acc + EasyCoder_Value.doValue(program, part).content; 3617 }, ``) 3618 }; 3619 case `boolean`: 3620 case `constant`: 3621 return value; 3622 case `symbol`: 3623 const symbol = program.getSymbolRecord(value.name); 3624 if (symbol.isValueHolder) { 3625 const symbolValue = symbol.value[symbol.index]; 3626 if (symbolValue) { 3627 const v = symbolValue.content; 3628 if (v === null || typeof v === `undefined`) { 3629 symbolValue.content = symbolValue.numeric ? 0 : ``; 3630 } 3631 return symbolValue; 3632 } else { 3633 return null; 3634 } 3635 } else { 3636 const handler = program.domain[symbol.domain].value; 3637 return handler.get(program, value); 3638 } 3639 default: 3640 break; 3641 } 3642 // Call the given domain to handle a value 3643 const handler = program.domain[value.domain].value; 3644 return handler.get(program, value); 3645 }, 3646 3647 constant: (content, numeric) => { 3648 return { 3649 type: `constant`, 3650 numeric, 3651 content 3652 }; 3653 }, 3654 3655 // runtime 3656 3657 evaluate: (program, value) => { 3658 if (!value) { 3659 return { 3660 type: `constant`, 3661 numeric: false, 3662 content: `` 3663 }; 3664 } 3665 const result = EasyCoder_Value.doValue(program, value); 3666 if (result) { 3667 return result; 3668 } 3669 program.runtimeError(program[program.pc].lino, `Can't decode value: ` + value); 3670 }, 3671 3672 getValue: (program, value) => { 3673 return EasyCoder_Value.evaluate(program, value).content; 3674 }, 3675 3676 // tools 3677 3678 encode: (value, encoding) => { 3679 if (value) { 3680 switch (encoding) { 3681 case `ec`: 3682 return value.replace(/'/g, `~sq~`).replace(/"/g, `~dq~`).replace(/\n/g, `%0a`).replace(/\r/g, `%0d`); 3683 case `url`: 3684 return encodeURIComponent(value.replace(/\s/g, `+`)); 3685 case `sanitize`: 3686 return value.normalize(`NFD`).replace(/[\u0300-\u036f]/g, ``); 3687 } 3688 } 3689 return value; 3690 }, 3691 3692 decode: (value, encoding) => { 3693 if (value) { 3694 switch (encoding) { 3695 case `ec`: 3696 return value.replace(/~dq~/g, `"`).replace(/~sq~/g, `'`).replace(/%0a/g, `\n`).replace(/%0d/g, `\r`); 3697 case `url`: 3698 const decoded = decodeURIComponent(value); 3699 return decoded.replace(/\+/g, ` `); 3700 } 3701 } 3702 return value; 3703 } 3455 3704 }; 3456 3705 -
easycoder/trunk/easycoder.php
r2016526 r2038748 4 4 * Plugin URI: https://easycoder.software 5 5 * Description: Control the appearance and behavior of your posts and pages by embedding simple English-like scripts, without the need to learn JavaScript. 6 * Version: 2. 1.96 * Version: 2.2.0 7 7 * Author: EasyCoder Software 8 8 * Author URI: https://easycoder.software … … 17 17 function easycoder_enqueue_script() { 18 18 wp_enqueue_script('easycoder_script', plugin_dir_url( __FILE__ ) 19 . 'easycoder-min.js', array(), '2. 1.9');19 . 'easycoder-min.js', array(), '2.2.0'); 20 20 } 21 21 -
easycoder/trunk/ec-rest.txt
r1990067 r2038748 22 22 # You can create a hash value using this URL: 23 23 # {Your website}/wp-content/plugins/easycoder/rest.php/_hash/{password} 24 password= $2y$10$VtzWvsaY4qRfZHKnbkvuyO9xmZ9SgK68FrTCWe9HUW6hrX3kXKpPO24 password={encrypted password} -
easycoder/trunk/plugins-sample.js
r2016526 r2038748 43 43 * This lets you add a plugin before launching a script, using the 'plugin' command. 44 44 * You must provide a case for every plugin you will be adding; 45 * use 'ckeditor'as the pattern to follow.45 * use any one of them as the pattern to follow. 46 46 */ 47 47 … … 54 54 }); 55 55 break; 56 57 case `ui`: 58 getPlugin(name, 59 `${window.location.origin}${path()}/wp-content/plugins/easycoder/plugins/ui.js`, 60 function() { 61 addPlugin(name, EasyCoder_UI, callback); 62 }); 63 break; 64 65 case `gmap`: 66 getPlugin(name, 67 `${window.location.origin}${path()}/wp-content/plugins/easycoder/plugins/gmap.js`, 68 function() { 69 addPlugin(name, EasyCoder_GMap, callback); 70 }); 71 break; 72 73 case `showdown`: 74 getPlugin(name, 75 `${window.location.origin}${path()}/wp-content/plugins/easycoder/plugins/showdown.js`, 76 function() { 77 addPlugin(name, EasyCoder_Showdown, callback); 78 }); 79 break; 56 80 } 57 81 } -
easycoder/trunk/plugins/browser.js
r2016526 r2038748 142 142 }, 143 143 144 Clear: { 145 146 compile: (compiler) => { 147 const lino = compiler.getLino(); 148 if (compiler.nextIsSymbol()) { 149 const symbolRecord = compiler.getSymbolRecord(); 150 if (symbolRecord.extra === `dom`) { 151 compiler.next(); 152 compiler.addCommand({ 153 domain: `browser`, 154 keyword: `clear`, 155 lino, 156 name: symbolRecord.name 157 }); 158 return true; 159 } 160 } 161 return false; 162 }, 163 164 run: (program) => { 165 const command = program[program.pc]; 166 const targetRecord = program.getSymbolRecord(command.name); 167 const target = targetRecord.element[targetRecord.index]; 168 target.innerHTML = ``; 169 return command.pc + 1; 170 } 171 }, 172 144 173 Create: { 145 174 … … 227 256 break; 228 257 default: 229 varparent;258 let parent; 230 259 if (command.parent === `body`) { 231 260 parent = document.body; … … 233 262 const parentRecord = program.getSymbolRecord(command.parent); 234 263 if (!parentRecord.element[parentRecord.index]) { 235 parentRecord.element[parentRecord.index] = 236 document.getElementById(parentRecord.value[parentRecord.index].content); 264 program.runtimeError(command.pc, `Element ${parentRecord.name} does not exist.`); 237 265 } 238 266 parent = parentRecord.element[parentRecord.index]; … … 407 435 break; 408 436 case `getStorage`: 409 const value = window.localStorage.getItem(program.getValue(command.key)); 437 let value = window.localStorage.getItem(program.getValue(command.key)); 438 if (typeof value === `undefined`) { 439 value = null; 440 } 410 441 targetRecord.value[targetRecord.index] = { 411 442 type: `constant`, … … 501 532 case `replace`: 502 533 compiler.next(); 503 varurl = ``;504 varstate = ``;534 let url = ``; 535 let state = ``; 505 536 while (true) { 506 537 const token = compiler.getToken(); … … 542 573 } 543 574 const command = program[program.pc]; 544 varstate = program.getValue(command.state);575 let state = program.getValue(command.state); 545 576 if (!state) { 546 state = `{}`; 547 } 548 state = JSON.parse(state); 549 state.script = program.script; 550 state = JSON.stringify(state); 577 state = `{"script":"${program.script}"}`; 578 } 551 579 const url = program.getValue(command.url); 552 580 switch (command.type) { … … 638 666 compile: (compiler) => { 639 667 const lino = compiler.getLino(); 640 varnewWindow = false;668 let newWindow = false; 641 669 if (compiler.nextTokenIs(`new`)) { 642 670 newWindow = true; … … 671 699 if (compiler.nextTokenIs(`to`)) { 672 700 const to = compiler.getNextValue(); 673 varsubject = ``;674 varbody = ``;701 let subject = ``; 702 let body = ``; 675 703 if (compiler.tokenIs(`subject`)) { 676 704 subject = compiler.getNextValue(); … … 697 725 if (command.subject) { 698 726 window.location.href = `mailto:${program.getValue(command.to)}` + 699 `?subject=${program.getValue(command.subject)}&body=${ program.getValue(command.body)}`;727 `?subject=${program.getValue(command.subject)}&body=${encodeURIComponent(program.getValue(command.body))}`; 700 728 } else { 701 729 window.location.href = `mailto:${program.getValue(command.to)}`; … … 801 829 const changeItem = program.getSymbolRecord(command.symbol); 802 830 if (changeItem.keyword === `select`) { 803 const cssId = changeItem.value[changeItem.index].content; 804 if (cssId) { 805 const target = document.getElementById(cssId); 806 target.targetPc = command.pc + 2; 807 target.addEventListener(`change`, function () { 808 try { 809 program.run(target.targetPc); 810 } catch (err) { 811 console.log(err.message); 812 alert(err.message); 813 } 814 return false; 815 }); 816 } 831 const target = changeItem.element[changeItem.index]; 832 target.targetPc = command.pc + 2; 833 target.addEventListener(`change`, function () { 834 try { 835 program.run(target.targetPc); 836 } catch (err) { 837 console.log(err.message); 838 alert(err.message); 839 } 840 return false; 841 }); 817 842 } 818 843 break; 819 844 case `click`: 820 845 const targetRecord = program.getSymbolRecord(command.symbol); 821 targetRecord.element.forEach(function (element, index) { 822 var target = targetRecord.element[index]; 846 targetRecord.element.forEach(function (target, index) { 823 847 target.targetRecord = targetRecord; 824 848 target.targetIndex = index; … … 842 866 const interceptClickEvent = (e) => { 843 867 EasyCoder.timestamp = Date.now(); 844 vartarget = e.target || e.srcElement;845 varhref = ``;868 let target = e.target || e.srcElement; 869 let href = ``; 846 870 while (target.parentNode) { 847 871 if (target.tagName === `A`) { … … 854 878 while (target.parentNode) { 855 879 if (target.id.indexOf(`ec-`) === 0) { 856 varid = target.id.slice(3);857 varpos = id.indexOf(`-`);880 let id = target.id.slice(3); 881 let pos = id.indexOf(`-`); 858 882 program.varName = id.slice(0, pos); 859 883 id = id.slice(pos + 1); … … 876 900 break; 877 901 case `swipe`: 878 varxDown;902 let xDown; 879 903 const getTouches = (evt) => { 880 904 return evt.touches || // browser API … … 913 937 break; 914 938 case `key`: 939 if (typeof document.onKeyListeners === `undefined`) { 940 document.onKeyListeners = []; 941 } 942 if (!document.onKeyListeners.includes(program)) { 943 document.onKeyListeners.push(program); 944 } 915 945 program.onKeyPc = command.pc + 2; 916 946 document.onkeypress = function (event) { 917 program.key = event.key; 918 try { 919 setTimeout(function () { 920 program.run(program.onKeyPc); 921 }, 1); 922 } catch (err) { 923 console.log(`Error: ${err.message}`); 947 for (const program of document.onKeyListeners) { 948 program.key = event.key; 949 try { 950 setTimeout(function () { 951 program.run(program.onKeyPc); 952 }, 1); 953 } catch (err) { 954 console.log(`Error: ${err.message}`); 955 } 924 956 } 925 957 return true; … … 1208 1240 } 1209 1241 } else { 1210 vartoken = compiler.getToken();1242 let token = compiler.getToken(); 1211 1243 if (token === `the`) { 1212 1244 token = compiler.nextToken(); 1213 1245 } 1214 1246 if (token === `title`) { 1215 compiler.skip(`to`); 1216 const value = compiler.getValue(); 1217 compiler.addCommand({ 1218 domain: `browser`, 1219 keyword: `set`, 1220 lino, 1221 type: `setTitle`, 1222 value 1223 }); 1224 return true; 1247 if (compiler.nextTokenIs(`to`)) { 1248 const value = compiler.getNextValue(); 1249 compiler.addCommand({ 1250 domain: `browser`, 1251 keyword: `set`, 1252 lino, 1253 type: `setTitle`, 1254 value 1255 }); 1256 return true; 1257 } 1225 1258 } else if (token === `content`) { 1226 1259 if (compiler.nextTokenIs(`of`)) { … … 1331 1364 if (compiler.nextIsSymbol(true)) { 1332 1365 const symbolRecord = compiler.getSymbolRecord(); 1333 // if (symbolRecord.domain !== 'browser') {1334 // return false;1335 // }1336 1366 const symbolName = symbolRecord.name; 1337 1367 compiler.next(); 1338 varattributeValue = {1368 let attributeValue = { 1339 1369 type: `boolean`, 1340 1370 content: true … … 1359 1389 if (compiler.nextIsSymbol()) { 1360 1390 const symbolRecord = compiler.getSymbolRecord(); 1361 symbolName = symbolRecord.name;1391 const symbolName = symbolRecord.name; 1362 1392 if (symbolRecord.extra !== `dom`) { 1363 1393 compiler.warning(`'${symbolName}' is not a DOM type`); … … 1383 1413 } 1384 1414 const styleName = compiler.getValue(); 1385 vartype = `setStyle`;1386 varsymbolName = ``;1415 let type = `setStyle`; 1416 let symbolName = ``; 1387 1417 if (compiler.tokenIs(`of`)) { 1388 1418 if (compiler.nextToken() === `body`) { … … 1413 1443 } 1414 1444 } 1445 } else if (token === `default`) { 1446 if (compiler.nextTokenIs(`of`)) { 1447 if (compiler.nextIsSymbol()) { 1448 const symbolRecord = compiler.getSymbolRecord(); 1449 if (symbolRecord.keyword === `select`) { 1450 if (compiler.nextTokenIs(`to`)) { 1451 const value = compiler.getNextValue(); 1452 compiler.addCommand({ 1453 domain: `browser`, 1454 keyword: `set`, 1455 lino, 1456 type: `setDefault`, 1457 name: symbolRecord.name, 1458 value 1459 }); 1460 return true; 1461 } 1462 } 1463 } 1464 } 1415 1465 } 1416 1466 } … … 1421 1471 run: (program) => { 1422 1472 const command = program[program.pc]; 1423 varsymbol;1424 varvalue;1425 vartargetVar;1426 vartarget;1427 vartargetId;1428 varcssId;1473 let symbol; 1474 let value; 1475 let targetVar; 1476 let target; 1477 let targetId; 1478 let cssId; 1429 1479 switch (command.type) { 1430 1480 case `setContentVar`: … … 1557 1607 document.title = program.getValue(command.value); 1558 1608 break; 1609 case `setDefault`: 1610 const selectRecord = program.getSymbolRecord(command.name); 1611 value = program.getValue(command.value); 1612 const element = selectRecord.element[selectRecord.index]; 1613 for (let n = 0; n < element.options.length; n++) { 1614 if (element.options[n].value === value) { 1615 element.selectedIndex = n; 1616 break; 1617 } 1618 } 1619 break; 1559 1620 default: 1560 1621 break; … … 1610 1671 compiler.next(); 1611 1672 } 1612 varalignment = `horizontal`;1673 let alignment = `horizontal`; 1613 1674 if (compiler.tokenIs(`horizontal`) || compiler.tokenIs(`vertical`)) { 1614 1675 alignment = compiler.getToken(); … … 1737 1798 const percent = Math.round((event.loaded / event.total) * 100); 1738 1799 setProgress(percent); 1739 setStatus(`${Math.round(percent)}% uploaded...please wait`);1800 setStatus(`${Math.round(percent)}%...`); 1740 1801 }, false); 1741 1802 ajax.addEventListener(`load`, function (event) { … … 1763 1824 break; 1764 1825 default: 1765 varerror = ``;1826 let error = ``; 1766 1827 try { 1767 1828 error = JSON.parse(this.responseText ? this.responseText : `{}`); … … 1799 1860 case `button`: 1800 1861 return EasyCoder_Browser.BUTTON; 1862 case `clear`: 1863 return EasyCoder_Browser.Clear; 1801 1864 case `create`: 1802 1865 return EasyCoder_Browser.Create; … … 1896 1959 if (compiler.isSymbol()) { 1897 1960 const symbolRecord = compiler.getSymbolRecord(); 1961 if (compiler.nextTokenIs(`is`)) { 1962 if (compiler.nextTokenIs(`defined`)) { 1963 if (symbolRecord.extra === `dom`) { 1964 compiler.next(); 1965 return { 1966 domain: `browser`, 1967 type: `defined`, 1968 value: symbolRecord.name 1969 }; 1970 } 1971 return null; 1972 } else { 1973 compiler.prev(); 1974 } 1975 } 1898 1976 switch (symbolRecord.keyword) { 1899 1977 case `file`: 1900 1978 case `input`: 1901 1979 case `select`: 1902 compiler.next();1980 case `textarea`: 1903 1981 return { 1904 1982 domain: `browser`, … … 1910 1988 } 1911 1989 1912 vartoken = compiler.getToken();1990 let token = compiler.getToken(); 1913 1991 if (token === `mobile`) { 1914 1992 compiler.next(); … … 1935 2013 } 1936 2014 if (token === `attribute`) { 1937 compiler.next(); 1938 const attribute = compiler.getValue(); 2015 const attribute = compiler.getNextValue(); 1939 2016 if (compiler.tokenIs(`of`)) { 1940 2017 compiler.next(); … … 1955 2032 } 1956 2033 if (token === `confirm`) { 1957 compiler.next(); 1958 const text = compiler.getValue(); 2034 const text = compiler.getNextValue(); 1959 2035 return { 1960 2036 domain: `browser`, 1961 2037 type: `confirm`, 1962 text, 1963 pre 2038 text 1964 2039 }; 1965 2040 } 1966 2041 if (token === `prompt`) { 1967 compiler.next(); 1968 const text = compiler.getValue(); 2042 const text = compiler.getNextValue(); 1969 2043 if (compiler.tokenIs(`with`)) { 1970 2044 const pre = compiler.getNextValue(); … … 2016 2090 } 2017 2091 if ([`width`, `height`].includes(token)) { 2018 compiler.next(); 2019 if (compiler.tokenIs(`of`)) { 2020 compiler.next(); 2021 if (compiler.isSymbol()) { 2092 if (compiler.nextTokenIs(`of`)) { 2093 if (compiler.nextIsSymbol()) { 2022 2094 const symbol = compiler.getSymbolRecord(); 2023 2095 compiler.next(); … … 2089 2161 2090 2162 get: (program, value) => { 2091 var symbolRecord; 2092 var element; 2093 var target; 2163 let symbolRecord; 2164 let element; 2165 let target; 2166 let content; 2094 2167 switch (value.type) { 2095 2168 case `file`: 2096 2169 case `input`: 2097 2170 case `select`: 2171 case `textarea`: 2098 2172 symbolRecord = program.getSymbolRecord(value.value); 2099 2173 target = symbolRecord.element[symbolRecord.index]; … … 2102 2176 numeric: false, 2103 2177 content: target.value 2178 }; 2179 case `defined`: 2180 symbolRecord = program.getSymbolRecord(value.value); 2181 return { 2182 domain: `browser`, 2183 type: `boolean`, 2184 content: typeof symbolRecord.element[symbolRecord.index] !== `undefined` 2104 2185 }; 2105 2186 case `mobile`: … … 2137 2218 symbolRecord = program.getSymbolRecord(value.symbol); 2138 2219 target = symbolRecord.element[symbolRecord.index]; 2139 var content;2140 2220 switch (symbolRecord.keyword) { 2141 2221 case `input`: 2142 2222 case `textarea`: 2143 2223 content = target.value; 2224 break; 2225 case `pre`: 2226 content = target.innerHTML; 2144 2227 break; 2145 2228 default: … … 2234 2317 2235 2318 compile: (compiler) => { 2236 console.log(`Browser:compile`);2237 2319 if (compiler.tokenIs(`confirm`)) { 2238 2320 compiler.next(); … … 2243 2325 value 2244 2326 }; 2327 } else if (compiler.tokenIs(`element`)) { 2328 if (compiler.nextIsSymbol()) { 2329 const symbolRecord = compiler.getSymbolRecord(); 2330 if (symbolRecord.extra === `dom`) { 2331 if (compiler.nextTokenIs(`has`)) { 2332 if (compiler.nextTokenIs(`the`)) { 2333 compiler.next(); 2334 } 2335 if (compiler.tokenIs(`focus`)) { 2336 compiler.next(); 2337 return { 2338 domain: `browser`, 2339 type: `focus`, 2340 element: symbolRecord.name 2341 }; 2342 } 2343 } 2344 } 2345 } 2245 2346 } 2246 2347 return null; … … 2251 2352 case `confirm`: 2252 2353 return confirm(program.getValue(condition.value)); 2354 case `focus`: 2355 const elementRecord = program.getSymbolRecord(condition.element); 2356 return elementRecord.element[elementRecord.index] === document.activeElement; 2253 2357 } 2254 2358 } … … 2265 2369 }; 2266 2370 2267 varscrollPosition = 0;2371 let scrollPosition = 0; 2268 2372 2269 2373 window.addEventListener(`scroll`, function () { … … 2275 2379 const state = JSON.parse(event.state); 2276 2380 const program = window.EasyCoder.scripts[state.script]; 2277 if (program.onRestore) { 2278 program.run(program.onRestore); 2381 if (program) { 2382 if (program.onRestore) { 2383 program.run(program.onRestore); 2384 } 2385 } else { 2386 console.log(`No script property in window state object`); 2279 2387 } 2280 2388 }; -
easycoder/trunk/plugins/ckeditor.js
r2016526 r2038748 98 98 editor = program.getSymbolRecord(command.editor); 99 99 editor.editor = 100 CKEDITOR.appendTo(editor.element[editor.index].id, {101 height: 400102 });100 CKEDITOR.appendTo(editor.element[editor.index].id, { 101 height: 400 102 }); 103 103 break; 104 104 case `close`: … … 127 127 return 0; 128 128 case `reset`: 129 for ( name in CKEDITOR.instances) {129 for (const name in CKEDITOR.instances) { 130 130 CKEDITOR.instances[name].destroy(); 131 131 } … … 157 157 value: { 158 158 159 compile: ( compiler) => {159 compile: () => { 160 160 return null; 161 161 }, 162 get: ( program, value) => {}162 get: () => {} 163 163 }, 164 164 165 165 condition: { 166 166 167 compile: ( compiler) => {},168 test: ( program, condition) => {}167 compile: () => {}, 168 test: () => {} 169 169 } 170 170 }; -
easycoder/trunk/plugins/json.js
r2016526 r2038748 65 65 case `set`: 66 66 compiler.next(); 67 if (compiler.tokenIs(`property`)) {68 const name = compiler.getNextValue();69 if (compiler.tokenIs(`of`)) {70 if (compiler.nextIsSymbol()) {71 const targetRecord = compiler.getSymbolRecord();72 if (targetRecord.keyword === `variable`) {73 if (compiler.nextTokenIs(`to`)) {74 const value = compiler.getNextValue();75 compiler.addCommand({76 domain: `json`,77 keyword: `json`,78 lino,79 request: `setProperty`,80 target: targetRecord.name,81 name,82 value83 });84 return true;85 }86 }87 }88 }89 return null;90 }91 67 if (compiler.isSymbol()) { 92 68 const targetRecord = compiler.getSymbolRecord(); … … 193 169 } 194 170 const item = program.getValue(command.item); 195 if ( itemData[item] === `undefined`) {171 if (typeof itemData[item] === `undefined`) { 196 172 program.runtimeError(command.lino, `No such property '${item}'`); 197 173 } … … 230 206 numeric: false, 231 207 content 232 };233 break;234 case `setProperty`:235 targetRecord = program.getSymbolRecord(command.target);236 const targetValue = targetRecord.value[targetRecord.index];237 var targetItem = ``;238 try {239 targetItem = JSON.parse(program.getValue(targetValue));240 } catch (err) {241 program.runtimeError(command.lino, `Can't parse JSON`);242 return 0;243 }244 const itemName = program.getValue(command.name);245 const itemValue = program.evaluate(command.value);246 //targetItem[itemName] = itemValue.content;247 if (itemValue.type === `boolean`) {248 targetItem[itemName] = itemValue.content;249 } else {250 targetItem[itemName] = itemValue.numeric ? itemValue.content :251 itemValue.content.split(`"`).join(`\\"`);252 }253 targetRecord.value[targetRecord.index] = {254 type: `constant`,255 numeric: false,256 content: JSON.stringify(targetItem)257 208 }; 258 209 break; … … 270 221 // The target is assumed to be a SELECT 271 222 targetRecord = program.getSymbolRecord(command.target); 272 const target = document.getElementById(targetRecord.value[targetRecord.index].content);223 const target = targetRecord.element[targetRecord.index]; 273 224 target.options.length = 0; 274 225 // Get the name of the display field … … 353 304 if (compiler.nextTokenIs(`from`)) { 354 305 const url = compiler.getNextValue(); 306 var onError = null; 307 if (compiler.tokenIs(`or`)) { 308 compiler.next(); 309 onError = compiler.getPc() + 1; 310 compiler.completeHandler(); 311 } 355 312 compiler.addCommand({ 356 313 domain: `json`, … … 359 316 request: `get`, 360 317 target: targetRecord.name, 361 url 318 url, 319 onError 362 320 }); 363 321 return true; … … 379 337 } 380 338 const url = compiler.getValue(); 339 onError = null; 340 if (compiler.tokenIs(`or`)) { 341 compiler.next(); 342 onError = compiler.getPc() + 1; 343 compiler.completeHandler(); 344 } 381 345 compiler.addCommand({ 382 346 domain: `json`, … … 385 349 request: `post`, 386 350 value, 387 url 351 url, 352 onError 388 353 }); 389 354 return true; … … 408 373 if (command.request === `get`) { 409 374 var content = this.responseText; 410 if (![`[`, `{`].includes(content [0])) {375 if (![`[`, `{`].includes(content.charAt(0))) { 411 376 content = program.decode(content); 412 377 } … … 423 388 break; 424 389 default: 425 const error = this.responseText; 426 try { 390 if (command.onError) { 391 program.errorMessage = this.responseText; 392 program.run(command.onError); 393 } else { 394 const error = this.responseText; 427 395 program.runtimeError(command.lino, error); 428 } catch (err) {429 program.reportError(err, program);430 396 } 431 397 break; … … 443 409 case `post`: 444 410 const value = program.getValue(command.value); 445 console.log(`POST ${value}to ${path}`);411 console.log(`POST to ${path}`); 446 412 ajax.open(`POST`, path); 447 if (value [0] === `{`) {413 if (value.charAt(0) === `{` || !isNaN(value)) { 448 414 ajax.setRequestHeader(`Content-Type`, `application/json; charset=UTF-8`); 449 415 // console.log(`value=${value}`); 450 ajax.send(value );416 ajax.send(value.charAt(0) === `{` ? value : value.toString()); 451 417 } else { 452 418 ajax.setRequestHeader(`Content-Type`, `application/text; charset=UTF-8`); 453 console.log(`value=${program.encode(value)}`);419 // console.log(`value=${program.encode(value)}`); 454 420 ajax.send(program.encode(value)); 455 421 } … … 487 453 } 488 454 if (compiler.tokenIs(`json`)) { 489 if ([`size`, `count`].includes(compiler.nextToken())) { 455 const type = compiler.nextToken(); 456 if ([`size`, `count`, `keys`].includes(type)) { 490 457 compiler.skip(`of`); 491 458 if (compiler.isSymbol()) { … … 495 462 return { 496 463 domain: `json`, 497 type : `size`,464 type, 498 465 name: target.name 499 466 }; 500 467 } 501 468 } 469 } else if (type === `index`) { 470 if (compiler.nextTokenIs(`of`)) { 471 const item = compiler.getNextValue(); 472 if (compiler.tokenIs(`in`)) { 473 const list = compiler.getNextValue(); 474 return { 475 domain: `json`, 476 type, 477 item, 478 list 479 }; 480 } 481 } 502 482 } 503 483 } … … 506 486 507 487 get: (program, value) => { 488 let symbolRecord; 489 let data; 490 let content; 508 491 switch (value.type) { 509 492 case `size`: 510 const symbolRecord = program.getSymbolRecord(value.name); 511 const data = program.getValue(symbolRecord.value[symbolRecord.index]); 512 var array = []; 493 case `count`: 494 symbolRecord = program.getSymbolRecord(value.name); 495 data = program.getValue(symbolRecord.value[symbolRecord.index]); 496 let array; 513 497 try { 514 498 array = JSON.parse(data); 515 499 } catch (err) { 516 program.runtimeError(0, `Can't parse JSON`); 517 return 0; 500 array = []; 518 501 } 519 502 return { … … 521 504 numeric: true, 522 505 content: array.length 506 }; 507 case `keys`: 508 symbolRecord = program.getSymbolRecord(value.name); 509 data = program.getValue(symbolRecord.value[symbolRecord.index]); 510 content = data ? JSON.stringify(Object.keys(JSON.parse(data)).sort()) : `[]`; 511 return { 512 type: `constant`, 513 numeric: false, 514 content 515 }; 516 case `index`: 517 const item = program.getValue(value.item); 518 const list = JSON.parse(program.getValue(value.list)); 519 content = list.findIndex(function(value) { 520 return value === item; 521 }); 522 return { 523 type: `constant`, 524 numeric: true, 525 content 523 526 }; 524 527 } -
easycoder/trunk/plugins/ui.js
r2016526 r2038748 136 136 } 137 137 } 138 } else if (type === `navigator`) {139 if (compiler.nextTokenIs(`in`)) {140 if (compiler.nextIsSymbol()) {141 const symbolRecord = compiler.getSymbolRecord();142 compiler.next();143 if (symbolRecord.keyword === `div`) {144 compiler.addCommand({145 domain: `ui`,146 keyword: `create`,147 lino,148 type,149 parent: symbolRecord.name150 });151 return true;152 }153 }154 }155 138 } 156 139 } … … 223 206 EasyCoder_UI.renderDate(this.dateRecord); 224 207 }; 225 break;226 case `navigator`:227 208 break; 228 209 } … … 288 269 const dateRecord = program.getSymbolRecord(command.date); 289 270 dateRecord.timestamp = program.getValue(command.timestamp) * 1000; 290 EasyCoder_UI.renderDate( this.dateRecord);271 EasyCoder_UI.renderDate(dateRecord); 291 272 break; 292 273 } -
easycoder/trunk/readme.txt
r2016526 r2038748 6 6 Requires at least: 4.4 7 7 Requires PHP: 5.2 8 Tested up to: 5. 08 Tested up to: 5.1 9 9 Stable tag: trunk 10 10 License: GPLv2 or later … … 24 24 * Retrieve content from web services using REST and JSON 25 25 * Draw and animate simple graphics 26 * Create a Google Map, put markers on it and interct with the map and the markers 27 * Include the CKEditor rich text editor in your pages 28 * Include the Showdown markdown converter in your pages 26 29 27 30 *EasyCoder* provides a simple syntax to do all these things, at about the same level of complexity and readability as SQL or Excel macros. It's a full programming language, though, capable of making complex logical decisions. … … 29 32 *EasyCoder* scripts are embedded in your page or post, inside a special "preformatted" tag. When the page loads, *EasyCoder* looks for this element then compiles and runs the script it contains. When it interacts with HTML elements it attaches their IDs to its own variables, so your HTML and its controlling script are in the same file. 30 33 31 The *EasyCoder* core module is currently about 47k bytes in its minimised form and it downloads its own plugin modules from a growing library. Its performance is good because it precompiles scripts - a process that takes only tens of milliseconds - and the compiled code for each command is only a thin wrapper around the corresponding JavaScript functionality.34 The *EasyCoder* core module is currently about 50k bytes in its minimised form and it downloads its own plugin modules from a growing library. Its performance is good because it precompiles scripts - a process that takes only tens of milliseconds - and the compiled code for each command is only a thin wrapper around the corresponding JavaScript functionality. 32 35 33 36 When *EasyCoder* detects an error, either in compilation or at runtime, it opens a popup window with a friendly error message that tries to tell you what went wrong and where in the script it happened. … … 50 53 51 54 == Changelog == 55 56 = 2.2.0 25-feb-2019 = 57 * New plugins: gmap and showdown. 52 58 53 59 = 2.1.9 21-jan-2019 = -
easycoder/trunk/rest.php
r2016526 r2038748 1 1 <?php 2 // REST server 3 4 // This small REST server gives you the ability to manage tables 5 // in your site database. WordPress provides a full set of API endpoints 6 // for its own functions but if you need extra tables or extra features 7 // you have to write your own. This server provides you with access to 8 // your own tables as long as they fit a specified format. It also lets you 9 // add an extension of your own to handle special needs. 10 // 11 // Don't modify this file; it will be overwritten on the next upgrade 12 // of EasyCoder. Instead, use the extension script, a skeleton for which 13 // was set up when you first installed EasyCoder, at 14 // {root of your WordPress installation}/easycoder/rest-local.php. 15 16 date_default_timezone_set('Europe/London'); 17 logger(substr($_SERVER['PATH_INFO'], 1)); 18 $request = explode("/", substr($_SERVER['PATH_INFO'], 1)); 19 $table = array_shift($request); 20 $method = $_SERVER['REQUEST_METHOD']; 21 22 // The properties file is best kept above the site root to prevent access by browsers. 23 // For WordPress, that's 4 levels up from our current location, the 'plugins/easycoder' folder. 24 $props = array(); 25 $filename = '../../../../' . $_SERVER['HTTP_HOST'] . '.txt'; 26 if (file_exists($filename)) { 27 $file = fopen($filename, 'r'); 28 while (!feof($file)) { 29 $ss = trim(fgets($file)); 30 if (!$ss || substr($ss, 0, 1) == '#') { 31 continue; 32 } 33 $ss = explode('=', $ss, 2); 34 if (count($ss) > 1) { 35 $props[$ss[0]] = $ss[1]; 36 } 37 } 38 fclose($file); 39 } 40 41 // First, the commands that don't require a database connection. 42 switch ($method) { 43 case 'GET': 44 switch ($table) { 45 case '_list': 46 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_list 47 // List the contents of a directory 48 if (count($request)) { 49 $path = '/' . str_replace('~', '/', $request[0]); 50 } 51 // Start at the resources folder 52 $path = "../../../resources$path"; 53 $files = scandir($path); 54 print '['; 55 // First list all the directories 56 $flag = false; 57 foreach ($files as $file) { 58 if (strpos($file, '.') !== 0) { 59 if (is_dir("$path/$file")) { 60 if ($flag) { 61 print ','; 62 } else { 63 $flag = true; 64 } 65 print "{\"name\":\"$file\",\"type\":\"dir\"}"; 66 } 67 } 68 } 69 // Now do the ordinary files 70 foreach ($files as $file) { 71 if (strpos($file, '.') !== 0) { 72 if (!is_dir("$path/$file")) { 73 if ($flag) { 74 print ','; 75 } else { 76 $flag = true; 77 } 78 $ext = substr($file, strrpos($file, '.') + 1); 79 $type = $ext; 80 switch (strtolower($ext)) { 81 case 'jpg': 82 case 'png': 83 case 'gif': 84 $type = 'img'; 85 break; 86 } 87 print "{\"name\":\"$file\",\"type\":\"$type\"}"; 88 } 89 } 90 } 91 print ']'; 92 exit; 93 case '_hash': 94 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_hash/{value-to-hash} 95 print password_hash($request[0], PASSWORD_DEFAULT); 96 exit; 97 case '_verify': 98 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_verify/{value-to-verify} 99 print password_verify($request[0], $props['password']) ? 'yes' : 'no'; 100 exit; 101 } 102 break; 103 case 'POST': 104 switch ($table) { 105 case '_mkdir': 106 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_mkdir 107 header("Content-Type: application/text"); 108 $path = stripslashes(file_get_contents("php://input")); 109 mkdir($path); 110 exit; 111 case '_upload': 112 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_upload 113 // Upload a file (an image) to the current directory 114 $path = $_POST['path']; 115 $fileName = $_FILES['source']['name']; 116 $tempName = $_FILES['source']['tmp_name']; 117 $fileType = $_FILES['source']['type']; 118 $fileSize = $_FILES['source']['size']; 119 $fileError = $_FILES['source']['error']; 120 if (!move_uploaded_file($tempName, "$path/$fileName")) { 121 unlink($tempName); 122 http_response_code(400); 123 print "Failed to upload $fileName to $path.<br />tempName: $tempName<br />fileType: $fileType<br />fileSize:$fileSize<br />fileError: $fileError"; 124 } else { 125 print "File $fileName uploaded successfully to $path/$filenName"; 126 } 127 exit; 128 case '_delete': 129 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_delete 130 header("Content-Type: application/text"); 131 $path = stripslashes(file_get_contents("php://input")); 132 if (is_dir($path)) { 133 rmdir($path); 134 } else { 135 unlink($path); 136 } 137 exit; 138 } 139 break; 140 } 141 142 // Most further commands require use of the database. 143 $conn = mysqli_connect($props['sqlhost'], $props['sqluser'], 144 $props['sqlpassword'], $props['sqldatabase']); 145 if (!$conn) 146 { 147 http_response_code(404); 148 die("Failed to connect to MySQL: " . mysqli_connect_error()); 149 } 150 mysqli_set_charset($conn,'utf8'); 151 152 if (!count($request)) { 153 http_response_code(400); 154 print "{\"message\":\"Incomplete REST query: ".substr($_SERVER['PATH_INFO'], 1).".\"}"; 155 exit; 156 } 157 158 // You can have a custom extension that deals with special requests. 159 // These all have '_' as the table name. 160 switch ($method) { 161 162 case 'GET': 163 if ($table == '_') { 164 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_/{request-and-arguments} 165 include '../../../easycoder/rest-local.php'; 166 get_local($conn, $request); 167 return; 168 } else { 169 // Use the handler below 170 get($conn, $table, $request); 171 } 172 break; 173 174 case 'POST': 175 if ($table == '_') { 176 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_/{request-and-arguments} 177 include '../../../easycoder/rest-local.php'; 178 post_local($conn, $request); 179 return; 180 } else { 181 // Use the handler below 182 post($conn, $table, $request); 183 } 184 break; 185 186 default: 187 http_response_code(400); 188 break; 189 } 190 mysqli_close(); 191 exit; 192 193 ///////////////////////////////////////////////////////////////////////// 194 // All the other commands deal with tables having a specific format, with the following fields: 195 // 196 // id int(11) 197 // name varchar(40) 198 // value text 199 // ts int(11) 200 // 201 // GET 202 function get($conn, $table, $request) { 203 $action = $request[0]; 204 switch ($action) { 205 case 'count': 206 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/count 207 // Return the number of items in the table 208 $result = $conn->query("SELECT id from $table"); 209 //print "{\"count\":".mysqli_num_rows($result)."}"; 210 print mysqli_num_rows($result); 211 break; 212 case 'list': 213 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/list/{offset}/{count} 214 // List items by ID, with optional offset & count, defaulting to 0 & 10 215 switch (count($request)) { 216 case 2: 217 $offset = 0; 218 $count = $request[1]; 219 break; 220 case 3: 221 $offset = $request[1]; 222 $count = $request[2]; 223 break; 224 default: 225 $offset = 0; 226 $count = 10; 227 break; 228 } 229 $result = $conn->query("SELECT id FROM $table LIMIT $offset, $count"); 230 $response = '['; 231 while ($row = mysqli_fetch_object($result)) { 232 if ($response != '[') { 233 $response .= ','; 234 } 235 $response .= $row->id; 236 } 237 $response .= ']'; 238 print $response; 239 break; 240 case 'names': 241 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/names/{offset}/{count} 242 // List items by name in ascending alphabetical order, 243 // with optional offset & count, defaulting to 0 & 10 244 switch (count($request)) { 245 case 2: 246 $offset = 0; 247 $count = $request[1]; 248 break; 249 case 3: 250 $offset = $request[1]; 251 $count = $request[2]; 252 break; 253 default: 254 $offset = 0; 255 $count = 10; 256 break; 257 } 258 $result = $conn->query("SELECT name FROM $table ORDER BY name LIMIT $offset, $count"); 259 $response = '['; 260 while ($row = mysqli_fetch_object($result)) { 261 if ($response != '[') { 262 $response .= ','; 263 } 264 $response .= "\"$row->name\""; 265 } 266 $response .= ']'; 267 print $response; 268 break; 269 case 'id': 270 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/id/{id} 271 // Get a record given its id 272 if (count($request) < 2) { 273 http_response_code(400); 274 print "Incomplete REST query."; 275 exit; 276 } 277 $id = $request[1]; 278 $result = $conn->query("SELECT value FROM $table WHERE id='$id'"); 279 if ($row = mysqli_fetch_object($result)) { 280 print $row->value; 281 } else { 282 http_response_code(404); 283 print "Cannot get item id '$id' as it does not exist."; 284 } 285 break; 286 case 'name': 287 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/name/{name} 288 // Get a record given its name 289 if (count($request) < 2) { 290 http_response_code(400); 291 print "Incomplete REST query."; 292 exit; 293 } 294 $name = $request[1]; 295 $result = $conn->query("SELECT value FROM $table WHERE name='$name'"); 296 if ($row = mysqli_fetch_object($result)) { 297 print $row->value; 298 } else { 299 http_response_code(404); 300 print "Cannot get item named '$name' as it does not exist."; 301 } 302 break; 303 default: 304 http_response_code(404); 305 print "I don't understand this request."; 306 break; 307 } 308 } 309 310 ///////////////////////////////////////////////////////////////////////// 311 // POST 312 function post($conn, $table, $request) { 313 $ts = time(); 314 $action = $request[0]; 315 switch ($action) { 316 case 'set': 317 // Set the value of a record 318 if (count($request) > 2) { 319 switch ($request[1]) { 320 case 'id': 321 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/id/{id} 322 // Set by id. The record must already exist 323 header("Content-Type: application/text"); 324 $value = stripslashes(file_get_contents("php://input")); 325 $id = $request[2]; 326 // See if there's an item with this id 327 $result = $conn->query("SELECT id FROM $table WHERE id=$id"); 328 if (mysqli_fetch_object($result)) { 329 // It exists, so update it 330 $value = urldecode($value); 331 logger("UPDATE $table SET value='$value',ts=$ts WHERE id=$id"); 332 query($conn, "UPDATE $table SET value='$value',ts=$ts WHERE id=$id"); 333 } else { 334 // Not found 335 http_response_code(404); 336 logger("{\"code\":\"404\",\"message\":\"Cannot set record $id of $table.\"}"); 337 print "{\"message\":\"Cannot set record $id of $table.\"}"; 338 } 339 break; 340 case 'name': 341 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/name/{name} 342 // Set by name. If the record does not exist, add it 343 header("Content-Type: application/text"); 344 $value = stripslashes(file_get_contents("php://input")); 345 $name = $request[2]; 346 // See if there's an item with this name 347 $result = $conn->query("SELECT id FROM $table WHERE name='$name'"); 348 if (mysqli_fetch_object($result)) { 349 // It exists, so update it 350 query($conn, "UPDATE $table SET value='$value',ts=$ts WHERE name='$name'"); 351 } else { 352 // Add a new item 353 query($conn, "INSERT INTO $table (name,value,ts) VALUES ('$name','$value','$ts')"); 354 http_response_code(201); 355 } 356 break; 357 default: 358 http_response_code(400); 359 print "{\"message\":\"Value '".$request[1]."' should be 'id' or 'name'.\"}"; 360 break; 361 } 362 } else { 363 http_response_code(400); 364 print "{\"message\":\"Incomplete REST query.\"}"; 365 } 366 break; 367 case 'delete': 368 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/delete/{id} 369 // Or: .../rest.php/table/delete/{name} 370 // Delete a record, by id or by name 371 if (count($request) > 1) { 372 $item = $request[1]; 373 if (is_int($item)) { 374 // Delete the requested id 375 query($conn, "DELETE FROM $table WHERE id=$id"); 376 } else { 377 // Delete the named item 378 query($conn, "DELETE FROM $table WHERE name='$item'"); 379 } 380 } 381 break; 382 case 'rename': 383 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/rename 384 // Rename a record 385 $value = $_POST['value']; 386 $id = $_POST['id']; 387 if (!$id && count($request) > 1) { 388 $id = $request[1]; 389 } 390 if ($id) { 391 query($conn, "UPDATE $table SET name='$name',value='$value' WHERE id=$id"); 392 } else { 393 $name = $_POST['name']; 394 $newname = $_POST['newname']; 395 // See if there's a data item with the new name 396 $result = $conn->query("SELECT id FROM $table WHERE name='$newname'"); 397 if ($row = mysqli_fetch_object($result)) { 398 // Conflict 399 http_response_code(409); 400 print "{\"message\":\"Cannot rename item '$name' to '$newname' as it already exists.\"}"; 401 } else { 402 // See if there's a data item with this name 403 $result = $conn->query("SELECT id FROM $table WHERE name='$name'"); 404 if ($row = mysqli_fetch_object($result)) { 405 // There's a data item to rename 406 $id = $row->id; 407 query($conn, "UPDATE $table SET name='$newname',value='$value' WHERE id=$id"); 408 } else { 409 // Not found 410 http_response_code(404); 411 print "{\"message\":\"Cannot rename item '$name' as it does not exist.\"}"; 412 } 413 } 414 } 415 break; 416 default: 417 http_response_code(404); 418 print "{\"message\":\"Unrecognised action '$action' requested.\"}"; 419 break; 420 } 421 } 422 423 ///////////////////////////////////////////////////////////////////////// 424 // Do an SQL query 425 function query($conn, $sql) 426 { 427 $result = mysqli_query($conn, $sql); 428 if (!$result) { 429 http_response_code(404); 430 logger('Error: '.mysqli_error($conn)); 431 die('Error: '.mysqli_error($conn)); 432 } 433 mysqli_free_result($result); 434 return $result; 435 } 436 437 //////////////////////////////////////////////////////////////////////////// 438 // Log a message. 439 function logger($message) 440 { 441 $timestamp = time(); 442 $date = date("Y/m/d H:i", $timestamp); 443 if (!file_exists("log")) mkdir("log"); 444 $file = "log/".date("Y", $timestamp); 445 if (!file_exists($file)) mkdir($file); 446 $file.= "/".date("Ymd", $timestamp).".txt"; 447 $fp = fopen($file, "a+") or die("Can't open $file"); 448 fwrite($fp, "$date: $message\n"); 449 fclose($fp); 450 } 2 // REST server 3 4 // This small REST server gives you the ability to manage tables 5 // in your site database. WordPress provides a full set of API endpoints 6 // for its own functions but if you need extra tables or extra features 7 // you have to write your own. This server provides you with access to 8 // your own tables as long as they fit a specified format. It also lets you 9 // add an extension of your own to handle special needs. 10 // 11 // Don't modify this file; it will be overwritten on the next upgrade 12 // of EasyCoder. Instead, use the extension script, a skeleton for which 13 // was set up when you first installed EasyCoder, at 14 // {root of your WordPress installation}/easycoder/rest-local.php. 15 16 date_default_timezone_set('Europe/London'); 17 logger(substr($_SERVER['PATH_INFO'], 1)); 18 $request = explode("/", substr($_SERVER['PATH_INFO'], 1)); 19 $table = array_shift($request); 20 $method = $_SERVER['REQUEST_METHOD']; 21 22 // The properties file is best kept above the site root to prevent access by browsers. 23 // For WordPress, that's 4 levels up from our current location, the 'plugins/easycoder' folder. 24 $props = array(); 25 $filename = '../../../../' . $_SERVER['HTTP_HOST'] . '.txt'; 26 if (file_exists($filename)) { 27 $file = fopen($filename, 'r'); 28 while (!feof($file)) { 29 $ss = trim(fgets($file)); 30 if (!$ss || substr($ss, 0, 1) == '#') { 31 continue; 32 } 33 $ss = explode('=', $ss, 2); 34 if (count($ss) > 1) { 35 $props[$ss[0]] = $ss[1]; 36 } 37 } 38 fclose($file); 39 } 40 41 // First, the commands that don't require a database connection. 42 switch ($method) { 43 case 'GET': 44 switch ($table) { 45 case '_list': 46 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_list/[{path}] 47 // List the contents of a directory 48 if (count($request)) { 49 $path = '/' . str_replace('~', '/', $request[0]); 50 } 51 // Start at the resources folder 52 $path = "../../../resources$path"; 53 $files = scandir($path); 54 print '['; 55 // First list all the directories 56 $flag = false; 57 foreach ($files as $file) { 58 if (strpos($file, '.') !== 0) { 59 if (is_dir("$path/$file")) { 60 if ($flag) { 61 print ','; 62 } else { 63 $flag = true; 64 } 65 print "{\"name\":\"$file\",\"type\":\"dir\"}"; 66 } 67 } 68 } 69 // Now do the ordinary files 70 foreach ($files as $file) { 71 if (strpos($file, '.') !== 0) { 72 if (!is_dir("$path/$file")) { 73 if ($flag) { 74 print ','; 75 } else { 76 $flag = true; 77 } 78 $ext = substr($file, strrpos($file, '.') + 1); 79 $type = $ext; 80 switch (strtolower($ext)) { 81 case 'jpg': 82 case 'png': 83 case 'gif': 84 $type = 'img'; 85 break; 86 } 87 print "{\"name\":\"$file\",\"type\":\"$type\"}"; 88 } 89 } 90 } 91 print ']'; 92 exit; 93 case '_hash': 94 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_hash/{value-to-hash} 95 print password_hash($request[0], PASSWORD_DEFAULT); 96 exit; 97 case '_verify': 98 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_verify/{value-to-verify} 99 print password_verify($request[0], $props['password']) ? 'yes' : 'no'; 100 exit; 101 case '_validate': 102 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_validate/{value-to-validate}/{encrypted-value} 103 print password_verify($request[0], str_replace('~', '/', $request[1])) ? 'yes' : 'no'; 104 exit; 105 case '_load': 106 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_load/{path} 107 $path = '../../../resources/' . str_replace('~', '/', $request[0]); 108 print file_get_contents($path); 109 exit; 110 break; 111 } 112 case 'POST': 113 switch ($table) { 114 case '_mkdir': 115 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_mkdir 116 header("Content-Type: application/text"); 117 $path = stripslashes(file_get_contents("php://input")); 118 logger("Create directory $path"); 119 mkdir($path); 120 exit; 121 case '_upload': 122 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_upload 123 // Upload a file (an image) to the current directory 124 $path = $_POST['path']; 125 $fileName = $_FILES['source']['name']; 126 $tempName = $_FILES['source']['tmp_name']; 127 $fileType = $_FILES['source']['type']; 128 $fileSize = $_FILES['source']['size']; 129 $fileError = $_FILES['source']['error']; 130 if (!move_uploaded_file($tempName, "$path/$fileName")) { 131 unlink($tempName); 132 http_response_code(400); 133 logger("Failed to upload $fileName to $path.\ntempName: $tempName\nfileType: $fileType\nfileSize:$fileSize\nfileError: $fileError"); 134 } else { 135 logger("File $fileName uploaded successfully to $path/$fileName"); 136 $size = getimagesize("$path/$fileName"); 137 logger("$path/$fileName: width:".$size[0].", height:".$size[1]); 138 if ($size[0] > 1024) { 139 logger("mogrify -resize 1024x1024 $path/$fileName"); 140 system("mogrify -resize 1024x1024 $path/$fileName"); 141 } 142 } 143 exit; 144 case '_delete': 145 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_delete 146 header("Content-Type: application/text"); 147 $path = stripslashes(file_get_contents("php://input")); 148 if (is_dir($path)) { 149 rmdir($path); 150 } else { 151 unlink($path); 152 } 153 exit; 154 case '_email': 155 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_email 156 header("Content-Type: application/text"); 157 $value = stripslashes(file_get_contents("php://input")); 158 $json = json_decode($value); 159 $from = $json->from; 160 $to = $json->to; 161 $subject = $json->subject; 162 $message = $json->message; 163 $headers = "MIME-Version: 1.0\r\n"; 164 $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n"; 165 $headers .= "From: $from\r\n"; 166 mail($to, $subject, $message, "$headers\r\n"); 167 print "$headers\r\n$message"; 168 exit; 169 case '_save': 170 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_save/{path} 171 $path = '../../../resources/' . str_replace('~', '/', $request[0]); 172 $p = strrpos($path, '/'); 173 $dir = substr($path, 0, $p); 174 mkdir($dir, 0777, true); 175 header("Content-Type: application/text"); 176 $content = stripslashes(file_get_contents("php://input")); 177 $p = strrpos($path, '.'); 178 $root = substr($path, 0, $p); 179 $ext = substr($path, $p); 180 $backup = "$root-bak$ext"; 181 unlink($backup); 182 copy($path, $backup); 183 file_put_contents($path, $content); 184 exit; 185 } 186 break; 187 } 188 189 // Most further commands require use of the database. 190 $conn = mysqli_connect($props['sqlhost'], $props['sqluser'], 191 $props['sqlpassword'], $props['sqldatabase']); 192 if (!$conn) 193 { 194 http_response_code(404); 195 die("Failed to connect to MySQL: " . mysqli_connect_error()); 196 } 197 mysqli_set_charset($conn,'utf8'); 198 199 if (!count($request)) { 200 http_response_code(400); 201 print "{\"message\":\"Incomplete REST query: ".substr($_SERVER['PATH_INFO'], 1).".\"}"; 202 exit; 203 } 204 205 // You can have a custom extension that deals with special requests. 206 // These all have '_' as the table name. 207 switch ($method) { 208 209 case 'GET': 210 if ($table == '_') { 211 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_/{request-and-arguments} 212 include '../../../easycoder/rest-local.php'; 213 get_local($conn, $request); 214 return; 215 } else { 216 // Use the handler below 217 get($conn, $table, $request); 218 } 219 break; 220 221 case 'POST': 222 if ($table == '_') { 223 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/_/{request-and-arguments} 224 include '../../../easycoder/rest-local.php'; 225 post_local($conn, $request); 226 return; 227 } else { 228 // Use the handler below 229 post($conn, $table, $request); 230 } 231 break; 232 233 default: 234 http_response_code(400); 235 break; 236 } 237 mysqli_close(); 238 exit; 239 240 ///////////////////////////////////////////////////////////////////////// 241 // All the other commands deal with tables having a specific format, with the following fields: 242 // 243 // id int(11) 244 // name varchar(40) 245 // value text 246 // ts int(11) 247 // 248 // GET 249 function get($conn, $table, $request) { 250 $action = $request[0]; 251 switch ($action) { 252 case 'count': 253 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/table/count 254 // Return the number of items in the table 255 $result = $conn->query("SELECT id from $table"); 256 //print "{\"count\":".mysqli_num_rows($result)."}"; 257 print mysqli_num_rows($result); 258 mysqli_free_result($result); 259 break; 260 261 case 'list': 262 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/list/{offset}/{count} 263 // List items by ID, with optional offset & count, defaulting to 0 & 10 264 switch (count($request)) { 265 case 2: 266 $offset = 0; 267 $count = $request[1]; 268 break; 269 case 3: 270 $offset = $request[1]; 271 $count = $request[2]; 272 break; 273 default: 274 $offset = 0; 275 $count = 10; 276 break; 277 } 278 $result = $conn->query("SELECT id FROM $table LIMIT $offset, $count"); 279 $response = '['; 280 while ($row = mysqli_fetch_object($result)) { 281 if ($response != '[') { 282 $response .= ','; 283 } 284 $response .= $row->id; 285 } 286 mysqli_free_result($result); 287 $response .= ']'; 288 print $response; 289 break; 290 291 case 'names': 292 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/names/{offset}/{count} 293 // List items by name in ascending alphabetical order, 294 // with optional offset & count, defaulting to 0 & 10 295 switch (count($request)) { 296 case 2: 297 $offset = 0; 298 $count = $request[1]; 299 break; 300 case 3: 301 $offset = $request[1]; 302 $count = $request[2]; 303 break; 304 default: 305 $offset = 0; 306 $count = 10; 307 break; 308 } 309 $result = $conn->query("SELECT name FROM $table ORDER BY name LIMIT $offset, $count"); 310 $response = '['; 311 while ($row = mysqli_fetch_object($result)) { 312 if ($response != '[') { 313 $response .= ','; 314 } 315 $response .= "\"$row->name\""; 316 } 317 mysqli_free_result($result); 318 $response .= ']'; 319 print $response; 320 break; 321 322 case 'id': 323 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/id/{id} 324 // Get a record given its id 325 if (count($request) < 2) { 326 http_response_code(400); 327 print "Incomplete REST query."; 328 exit; 329 } 330 $id = $request[1]; 331 $result = $conn->query("SELECT value FROM $table WHERE id='$id'"); 332 if ($row = mysqli_fetch_object($result)) { 333 print $row->value; 334 } else { 335 http_response_code(404); 336 print "Cannot get item id '$id' as it does not exist."; 337 } 338 mysqli_free_result($result); 339 break; 340 341 case 'name': 342 case 'query': 343 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/name/{name} 344 // Get a record given its name 345 if (count($request) < 2) { 346 http_response_code(400); 347 print "Incomplete REST query."; 348 exit; 349 } 350 $name = $request[1]; 351 $result = $conn->query("SELECT value FROM $table WHERE name='$name'"); 352 if ($row = mysqli_fetch_object($result)) { 353 print $row->value; 354 } else if ($action == 'name') { 355 http_response_code(404); 356 print "Cannot get item named '$name' as it does not exist."; 357 } 358 break; 359 360 default: 361 http_response_code(404); 362 print "I don't understand this request."; 363 break; 364 } 365 } 366 367 ///////////////////////////////////////////////////////////////////////// 368 // POST 369 function post($conn, $table, $request) { 370 $ts = time(); 371 $action = $request[0]; 372 switch ($action) { 373 case 'set': 374 // Set the value of a record 375 if (count($request) > 2) { 376 switch ($request[1]) { 377 case 'id': 378 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/id/{id} 379 // Set by id. The record must already exist 380 header("Content-Type: application/text"); 381 $value = stripslashes(file_get_contents("php://input")); 382 $id = $request[2]; 383 // See if there's an item with this id 384 $result = $conn->query("SELECT id FROM $table WHERE id=$id"); 385 if (mysqli_fetch_object($result)) { 386 // It exists, so update it 387 $value = urldecode($value); 388 logger("UPDATE $table SET value='$value',ts=$ts WHERE id=$id"); 389 query($conn, "UPDATE $table SET value='$value',ts=$ts WHERE id=$id"); 390 } else { 391 // Not found 392 http_response_code(404); 393 logger("{\"code\":\"404\",\"message\":\"Cannot set record $id of $table.\"}"); 394 print "{\"message\":\"Cannot set record $id of $table.\"}"; 395 } 396 mysqli_free_result($result); 397 break; 398 399 case 'name': 400 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/name/{name} 401 // Set by name. If the record does not exist, add it 402 header("Content-Type: application/text"); 403 $value = stripslashes(file_get_contents("php://input")); 404 $name = $request[2]; 405 // See if there's an item with this name 406 $result = $conn->query("SELECT id FROM $table WHERE name='$name'"); 407 if (mysqli_fetch_object($result)) { 408 // It exists, so update it 409 query($conn, "UPDATE $table SET value='$value',ts=$ts WHERE name='$name'"); 410 } else { 411 // Add a new item 412 query($conn, "INSERT INTO $table (name,value,ts) VALUES ('$name','$value','$ts')"); 413 http_response_code(201); 414 } 415 mysqli_free_result($result); 416 break; 417 418 default: 419 http_response_code(400); 420 print "{\"message\":\"Value '".$request[1]."' should be 'id' or 'name'.\"}"; 421 break; 422 } 423 } else { 424 http_response_code(400); 425 print "{\"message\":\"Incomplete REST query.\"}"; 426 } 427 break; 428 429 case 'delete': 430 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/delete/{id} 431 // Or: .../rest.php/table/delete/{name} 432 // Delete a record, by id or by name 433 if (count($request) > 1) { 434 $item = $request[1]; 435 if (is_int($item)) { 436 // Delete the requested id 437 query($conn, "DELETE FROM $table WHERE id=$id"); 438 } else { 439 // Delete the named item 440 query($conn, "DELETE FROM $table WHERE name='$item'"); 441 } 442 } 443 break; 444 445 case 'rename': 446 // Endpoint: {site root}/wp-content/plugins/easycoder/rest.php/{table}/rename 447 // Rename a record 448 $value = $_POST['value']; 449 $id = $_POST['id']; 450 if (!$id && count($request) > 1) { 451 $id = $request[1]; 452 } 453 if ($id) { 454 query($conn, "UPDATE $table SET name='$name',value='$value' WHERE id=$id"); 455 } else { 456 $name = $_POST['name']; 457 $newname = $_POST['newname']; 458 // See if there's a data item with the new name 459 $result = $conn->query("SELECT id FROM $table WHERE name='$newname'"); 460 if ($row = mysqli_fetch_object($result)) { 461 // Conflict 462 http_response_code(409); 463 print "{\"message\":\"Cannot rename item '$name' to '$newname' as it already exists.\"}"; 464 } else { 465 // See if there's a data item with this name 466 $result = $conn->query("SELECT id FROM $table WHERE name='$name'"); 467 if ($row = mysqli_fetch_object($result)) { 468 // There's a data item to rename 469 $id = $row->id; 470 query($conn, "UPDATE $table SET name='$newname',value='$value' WHERE id=$id"); 471 } else { 472 // Not found 473 http_response_code(404); 474 print "{\"message\":\"Cannot rename item '$name' as it does not exist.\"}"; 475 } 476 } 477 mysqli_free_result($result); 478 } 479 break; 480 481 default: 482 http_response_code(404); 483 print "{\"message\":\"Unrecognised action '$action' requested.\"}"; 484 break; 485 } 486 } 487 488 ///////////////////////////////////////////////////////////////////////// 489 // Do an SQL query 490 function query($conn, $sql) 491 { 492 $result = mysqli_query($conn, $sql); 493 if (!$result) { 494 http_response_code(404); 495 logger('Error: '.mysqli_error($conn)); 496 die('Error: '.mysqli_error($conn)); 497 } 498 return $result; 499 } 500 501 //////////////////////////////////////////////////////////////////////////// 502 // Log a message. 503 function logger($message) 504 { 505 $timestamp = time(); 506 $date = date("Y/m/d H:i", $timestamp); 507 if (!file_exists("log")) mkdir("log"); 508 $file = "log/".date("Y", $timestamp); 509 if (!file_exists($file)) mkdir($file); 510 $file.= "/".date("Ymd", $timestamp).".txt"; 511 $fp = fopen($file, "a+") or die("Can't open $file"); 512 fwrite($fp, "$date: $message\n"); 513 fclose($fp); 514 } 451 515 ?>
Note: See TracChangeset
for help on using the changeset viewer.