mirror of
https://github.com/boxgaming/qbjs.git
synced 2024-05-12 08:00:12 +00:00
initial revision
This commit is contained in:
parent
2f099c5d0d
commit
18789a9d9c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
tools/webserver.exe
|
||||
tools/qb2js.exe
|
48
codemirror/qb-ide.css
Normal file
48
codemirror/qb-ide.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
@font-face {
|
||||
font-family: dosvga;
|
||||
src: url(lp-dosvga.ttf);
|
||||
}
|
||||
|
||||
.cm-s-lesser-dark {
|
||||
line-height: 1em;
|
||||
}
|
||||
.cm-s-lesser-dark.CodeMirror { background: rgb(0, 0, 39); color: rgb(216, 216, 216); text-shadow: 0 -1px 1px #262626; font-family: dosvga; /*letter-spacing: -1px*/}
|
||||
.cm-s-lesser-dark div.CodeMirror-selected { background: #45443B; } /* 33322B*/
|
||||
.cm-s-lesser-dark .CodeMirror-line::selection, .cm-s-lesser-dark .CodeMirror-line > span::selection, .cm-s-lesser-dark .CodeMirror-line > span > span::selection { background: rgba(0, 49, 78, .99); }
|
||||
.cm-s-lesser-dark .CodeMirror-line::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); }
|
||||
.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid #fff; }
|
||||
.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/
|
||||
|
||||
.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/
|
||||
|
||||
.cm-s-lesser-dark .CodeMirror-gutters { background: rgb(0, 49, 78); border-right:1px solid #aaa; }
|
||||
.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; }
|
||||
.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; }
|
||||
.cm-s-lesser-dark .CodeMirror-linenumber { color: rgb(175, 175, 175); }
|
||||
|
||||
.cm-s-lesser-dark span.cm-header { color: #a0a; }
|
||||
.cm-s-lesser-dark span.cm-quote { color: #090; }
|
||||
.cm-s-lesser-dark span.cm-keyword { color: rgb(69, 118, 147); }
|
||||
.cm-s-lesser-dark span.cm-atom { color: #C2B470; }
|
||||
.cm-s-lesser-dark span.cm-number { color: rgb(216, 98, 78); }
|
||||
.cm-s-lesser-dark span.cm-def { color: white; }
|
||||
.cm-s-lesser-dark span.cm-variable { color:rgb(216, 216, 216); }
|
||||
.cm-s-lesser-dark span.cm-variable-2 { color: #669199; }
|
||||
.cm-s-lesser-dark span.cm-variable-3, .cm-s-lesser-dark span.cm-type { color: white; }
|
||||
.cm-s-lesser-dark span.cm-property { color: #92A75C; }
|
||||
.cm-s-lesser-dark span.cm-operator { color: #92A75C; }
|
||||
.cm-s-lesser-dark span.cm-comment { color: rgb(98, 98, 98); }
|
||||
.cm-s-lesser-dark span.cm-string { color: rgb(255, 167, 0); }
|
||||
.cm-s-lesser-dark span.cm-string-2 { color: #f50; }
|
||||
.cm-s-lesser-dark span.cm-meta { color: #738C73; }
|
||||
.cm-s-lesser-dark span.cm-qualifier { color: #555; }
|
||||
.cm-s-lesser-dark span.cm-builtin { color: rgb(69, 118, 147); }
|
||||
.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; }
|
||||
.cm-s-lesser-dark span.cm-tag { color: #669199; }
|
||||
.cm-s-lesser-dark span.cm-attribute { color: #81a4d5; }
|
||||
.cm-s-lesser-dark span.cm-hr { color: #999; }
|
||||
.cm-s-lesser-dark span.cm-link { color: #7070E6; }
|
||||
.cm-s-lesser-dark span.cm-error { color: #9d1e15; }
|
||||
|
||||
.cm-s-lesser-dark .CodeMirror-activeline-background { background: rgb(0, 49, 78); }
|
||||
.cm-s-lesser-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
|
372
codemirror/qb-lang.js
Normal file
372
codemirror/qb-lang.js
Normal file
|
@ -0,0 +1,372 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
/*
|
||||
For extra ASP classic objects, initialize CodeMirror instance with this option:
|
||||
isASP: true
|
||||
|
||||
E.G.:
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
isASP: true
|
||||
});
|
||||
*/
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineMode("vbscript", function(conf, parserConf) {
|
||||
var ERRORCLASS = 'error';
|
||||
|
||||
function wordRegexp(words) {
|
||||
return new RegExp("^((" + words.join(")|(") + "))\\b", "i");
|
||||
}
|
||||
|
||||
var singleOperators = new RegExp("^[\\+\\-\\*/&\\\\\\^<>=]");
|
||||
var doubleOperators = new RegExp("^((<>)|(<=)|(>=))");
|
||||
var singleDelimiters = new RegExp('^[\\.,]');
|
||||
var brakets = new RegExp('^[\\(\\)]');
|
||||
var identifiers = new RegExp("^[A-Za-z][_A-Za-z0-9]*");
|
||||
|
||||
var openingKeywords = ['sub','select','while','if','function', 'property', 'with', 'for', 'type'];
|
||||
var middleKeywords = ['else','elseif','case'];
|
||||
var endKeywords = ['next','loop','wend'];
|
||||
|
||||
var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'is', 'mod', 'eqv', 'imp']);
|
||||
var commonkeywords = ['dim', 'as', 'redim', 'then', 'until', 'randomize',
|
||||
'byval','byref','new','property', 'exit', 'in',
|
||||
'const', 'integer', 'single', 'double', '_unsigned', 'string', '_byte',
|
||||
'let', 'option explicit', 'call'];
|
||||
|
||||
//This list was from: http://msdn.microsoft.com/en-us/library/f8tbc79x(v=vs.84).aspx
|
||||
var atomWords = ['true', 'false', 'nothing', 'empty', 'null'];
|
||||
|
||||
// QB64 Keywords
|
||||
var builtinFuncsWords = ['_delay', '_fontwidth', '_height', '_limit', '_keyhit', '_newimage', '_pi', '_printstring', '_printwidth', '_rgb',
|
||||
'_rgb32', '_round', '_title', '_trim', '_width', 'abs', 'asc', 'atn', 'chr\\$', 'cls', 'color', 'cos', 'fix', 'input',
|
||||
'instr', 'int', 'left\\$', 'lcase\\$', 'len', 'line', 'locate', 'ltrim\\$', 'mid\\$', 'print', 'right\\$', 'rtrim\\$', 'rnd',
|
||||
'screen', 'sgn', 'sin', 'sleep', 'sqr', 'str\\$', 'tan', 'ubound', 'ucase\\$', 'val', '_screenexists', 'exp', 'timer',
|
||||
'pset', '_red32', '_green32', '_blue32', '_red', '_green', '_blue', 'circle', '_alpha32', '_keydown', 'swap', '_instrrev',
|
||||
'command\\$', '_mouseinput', '_mousex', '_mousey', '_mousebutton', 'log', '_atan2', 'inkey\\$'];
|
||||
|
||||
//This list was from: http://msdn.microsoft.com/en-us/library/ydz4cfk3(v=vs.84).aspx
|
||||
var builtinConsts = ['gx_true','vbBlack', 'vbRed', 'vbGreen', 'vbYellow', 'vbBlue', 'vbMagenta', 'vbCyan', 'vbWhite', 'vbBinaryCompare', 'vbTextCompare',
|
||||
'vbSunday', 'vbMonday', 'vbTuesday', 'vbWednesday', 'vbThursday', 'vbFriday', 'vbSaturday', 'vbUseSystemDayOfWeek', 'vbFirstJan1', 'vbFirstFourDays', 'vbFirstFullWeek',
|
||||
'vbGeneralDate', 'vbLongDate', 'vbShortDate', 'vbLongTime', 'vbShortTime', 'vbObjectError',
|
||||
'vbOKOnly', 'vbOKCancel', 'vbAbortRetryIgnore', 'vbYesNoCancel', 'vbYesNo', 'vbRetryCancel', 'vbCritical', 'vbQuestion', 'vbExclamation', 'vbInformation', 'vbDefaultButton1', 'vbDefaultButton2',
|
||||
'vbDefaultButton3', 'vbDefaultButton4', 'vbApplicationModal', 'vbSystemModal', 'vbOK', 'vbCancel', 'vbAbort', 'vbRetry', 'vbIgnore', 'vbYes', 'vbNo',
|
||||
'vbCr', 'VbCrLf', 'vbFormFeed', 'vbLf', 'vbNewLine', 'vbNullChar', 'vbNullString', 'vbTab', 'vbVerticalTab', 'vbUseDefault', 'vbTrue', 'vbFalse',
|
||||
'vbEmpty', 'vbNull', 'vbInteger', 'vbLong', 'vbSingle', 'vbDouble', 'vbCurrency', 'vbDate', 'vbString', 'vbObject', 'vbError', 'vbBoolean', 'vbVariant', 'vbDataObject', 'vbDecimal', 'vbByte', 'vbArray'];
|
||||
//This list was from: http://msdn.microsoft.com/en-us/library/hkc375ea(v=vs.84).aspx
|
||||
var builtinObjsWords = ['WScript', 'err', 'debug', 'RegExp', '\\$if', '\\$end if'];
|
||||
var knownProperties = ['description', 'firstindex', 'global', 'helpcontext', 'helpfile', 'ignorecase', 'length', 'number', 'pattern', 'source', 'value', 'count'];
|
||||
//var knownMethods = ['clear', 'execute', 'raise', 'replace', 'test', 'write', 'writeline', 'close', 'open', 'state', 'eof', 'update', 'addnew', 'end', 'createobject', 'quit', 'gxscenecreate'];
|
||||
var knownMethods = ['gxongameevent', 'gxmousex', 'gxmousey', 'gxsoundload', 'gxsoundplay', 'gxsoundrepeat', 'gxsoundvolume', 'gxsoundpause', 'gxsoundstop',
|
||||
'gxsoundmuted', 'gxsoundmuted', 'gxentityanimate', 'gxentityanimatestop', 'gxentityanimatemode', 'gxentityanimatemode',
|
||||
'gxscreenentitycreate', 'gxentitycreate', 'gxentityvisible', 'gxentitymove', 'gxentitypos', 'gxentityvx', 'gxentityvy',
|
||||
'gxentityx', 'gxentityy', 'gxentitywidth', 'gxentityheight', 'gxentityframenext', 'gxentityframeset', 'gxentitytype',
|
||||
'gxentitytype', 'gxentityuid', 'gxfontuid', 'gxentityapplygravity', 'gxentitycollisionoffset',
|
||||
'gxentitycollisionoffsetleft', 'gxentitycollisionoffsettop', 'gxentitycollisionoffsetright',
|
||||
'gxentitycollisionoffsetbottom', 'gxfullscreen', 'gxbackgroundadd', 'gxbackgroundy', 'gxbackgroundheight',
|
||||
'gxbackgroundclear', 'gxsceneembedded', 'gxscenecreate', 'gxscenewindowsize', 'gxscenescale', 'gxsceneresize',
|
||||
'gxscenedestroy', 'gxcustomdraw', 'gxframerate', 'gxframe', 'gxscenedraw', 'gxscenemove', 'gxscenepos', 'gxscenex',
|
||||
'gxsceney', 'gxscenewidth', 'gxsceneheight', 'gxscenecolumns', 'gxscenerows', 'gxscenestart', 'gxsceneupdate',
|
||||
'gxscenefollowentity', 'gxsceneconstrain', 'gxscenestop', 'gxmapcreate', 'gxmapcolumns', 'gxmaprows', 'gxmaplayers',
|
||||
'gxmaplayervisible', 'gxmaplayeradd', 'gxmaplayerinsert', 'gxmaplayerremove', 'gxmapresize', 'gxmapdraw',
|
||||
'gxmaptileposat', 'gxmaptile', 'gxmaptile', 'gxmaptiledepth', 'gxmaptileadd', 'gxmaptileremove', 'gxmapversion',
|
||||
'gxmapsave', 'gxmapload', 'gxmapisometric', 'gxspritedraw', 'gxspritedrawscaled', 'gxtilesetcreate',
|
||||
'gxtilesetreplaceimage', 'gxtilesetload', 'gxtilesetsave', 'gxtilesetpos', 'gxtilesetwidth', 'gxtilesetheight',
|
||||
'gxtilesetcolumns', 'gxtilesetrows', 'gxtilesetfilename', 'gxtilesetimage', 'gxtilesetanimationcreate',
|
||||
'gxtilesetanimationadd', 'gxtilesetanimationremove', 'gxtilesetanimationframes', 'gxtilesetanimationspeed',
|
||||
'gxfontcreate', 'gxfontwidth', 'gxfontheight', 'gxfontcharspacing', 'gxfontlinespacing',
|
||||
'gxdrawtext', 'gxdebug', 'gxdebug', 'gxdebugscreenentities', 'gxdebugfont', 'gxdebugtilebordercolor',
|
||||
'gxdebugentitybordercolor', 'gxdebugentitycollisioncolor', 'gxkeyinput', 'gxkeydown', 'gxdeviceinputdetect',
|
||||
'gxdeviceinputtest', 'gxdevicename', 'gxdevicetypename', 'gxinputtypename', 'gxkeybuttonname'];
|
||||
|
||||
var aspBuiltinObjsWords = ['server', 'response', 'request', 'session', 'application'];
|
||||
var aspKnownProperties = ['buffer', 'cachecontrol', 'charset', 'contenttype', 'expires', 'expiresabsolute', 'isclientconnected', 'pics', 'status', //response
|
||||
'clientcertificate', 'cookies', 'form', 'querystring', 'servervariables', 'totalbytes', //request
|
||||
'contents', 'staticobjects', //application
|
||||
'codepage', 'lcid', 'sessionid', 'timeout', //session
|
||||
'scripttimeout']; //server
|
||||
var aspKnownMethods = ['addheader', 'appendtolog', 'binarywrite', 'end', 'flush', 'redirect', //response
|
||||
'binaryread', //request
|
||||
'remove', 'removeall', 'lock', 'unlock', //application
|
||||
'abandon', //session
|
||||
'getlasterror', 'htmlencode', 'mappath', 'transfer', 'urlencode']; //server
|
||||
|
||||
var knownWords = knownMethods.concat(knownProperties);
|
||||
|
||||
builtinObjsWords = builtinObjsWords.concat(builtinConsts);
|
||||
|
||||
if (conf.isASP){
|
||||
builtinObjsWords = builtinObjsWords.concat(aspBuiltinObjsWords);
|
||||
knownWords = knownWords.concat(aspKnownMethods, aspKnownProperties);
|
||||
};
|
||||
|
||||
var keywords = wordRegexp(commonkeywords);
|
||||
var atoms = wordRegexp(atomWords);
|
||||
var builtinFuncs = wordRegexp(builtinFuncsWords);
|
||||
var builtinObjs = wordRegexp(builtinObjsWords);
|
||||
var known = wordRegexp(knownWords);
|
||||
var stringPrefixes = '"';
|
||||
|
||||
var opening = wordRegexp(openingKeywords);
|
||||
var middle = wordRegexp(middleKeywords);
|
||||
var closing = wordRegexp(endKeywords);
|
||||
var doubleClosing = wordRegexp(['end']);
|
||||
var doOpening = wordRegexp(['do']);
|
||||
var noIndentWords = wordRegexp(['on error resume next', 'exit']);
|
||||
var comment = wordRegexp(['rem']);
|
||||
|
||||
|
||||
function indent(_stream, state) {
|
||||
state.currentIndent++;
|
||||
}
|
||||
|
||||
function dedent(_stream, state) {
|
||||
state.currentIndent--;
|
||||
}
|
||||
// tokenizers
|
||||
function tokenBase(stream, state) {
|
||||
if (stream.eatSpace()) {
|
||||
return 'space';
|
||||
//return null;
|
||||
}
|
||||
|
||||
var ch = stream.peek();
|
||||
|
||||
// Handle Comments
|
||||
if (ch === "'") {
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
}
|
||||
if (stream.match(comment)){
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
}
|
||||
|
||||
|
||||
// Handle Number Literals
|
||||
if (stream.match(/^((&H)|(&O))?[0-9\.]/i, false) && !stream.match(/^((&H)|(&O))?[0-9\.]+[a-z_]/i, false)) {
|
||||
var floatLiteral = false;
|
||||
// Floats
|
||||
if (stream.match(/^\d*\.\d+/i)) { floatLiteral = true; }
|
||||
else if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
|
||||
else if (stream.match(/^\.\d+/)) { floatLiteral = true; }
|
||||
|
||||
if (floatLiteral) {
|
||||
// Float literals may be "imaginary"
|
||||
stream.eat(/J/i);
|
||||
return 'number';
|
||||
}
|
||||
// Integers
|
||||
var intLiteral = false;
|
||||
// Hex
|
||||
if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; }
|
||||
// Octal
|
||||
else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; }
|
||||
// Decimal
|
||||
else if (stream.match(/^[1-9]\d*F?/)) {
|
||||
// Decimal literals may be "imaginary"
|
||||
stream.eat(/J/i);
|
||||
// TODO - Can you have imaginary longs?
|
||||
intLiteral = true;
|
||||
}
|
||||
// Zero by itself with no other piece of number.
|
||||
else if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
|
||||
if (intLiteral) {
|
||||
// Integer literals may be "long"
|
||||
stream.eat(/L/i);
|
||||
return 'number';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Strings
|
||||
if (stream.match(stringPrefixes)) {
|
||||
state.tokenize = tokenStringFactory(stream.current());
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
|
||||
// Handle operators and Delimiters
|
||||
if (stream.match(doubleOperators)
|
||||
|| stream.match(singleOperators)
|
||||
|| stream.match(wordOperators)) {
|
||||
return 'operator';
|
||||
}
|
||||
if (stream.match(singleDelimiters)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stream.match(brakets)) {
|
||||
return "bracket";
|
||||
}
|
||||
|
||||
if (stream.match(noIndentWords)) {
|
||||
state.doInCurrentLine = true;
|
||||
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
if (stream.match(doOpening)) {
|
||||
indent(stream,state);
|
||||
state.doInCurrentLine = true;
|
||||
|
||||
return 'keyword';
|
||||
}
|
||||
if (stream.match(opening)) {
|
||||
if (! state.doInCurrentLine)
|
||||
indent(stream,state);
|
||||
else
|
||||
state.doInCurrentLine = false;
|
||||
|
||||
return 'keyword';
|
||||
}
|
||||
if (stream.match(middle)) {
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
|
||||
if (stream.match(doubleClosing)) {
|
||||
dedent(stream,state);
|
||||
dedent(stream,state);
|
||||
|
||||
return 'keyword';
|
||||
}
|
||||
if (stream.match(closing)) {
|
||||
if (! state.doInCurrentLine)
|
||||
dedent(stream,state);
|
||||
else
|
||||
state.doInCurrentLine = false;
|
||||
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
if (stream.match(keywords)) {
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
if (stream.match(atoms)) {
|
||||
return 'atom';
|
||||
}
|
||||
|
||||
if (stream.match(known)) {
|
||||
return 'variable-2';
|
||||
}
|
||||
|
||||
if (stream.match(builtinFuncs)) {
|
||||
return 'builtin';
|
||||
}
|
||||
|
||||
if (stream.match(builtinObjs)){
|
||||
return 'variable-2';
|
||||
}
|
||||
|
||||
if (stream.match(identifiers)) {
|
||||
return 'variable';
|
||||
}
|
||||
|
||||
// Handle non-detected items
|
||||
stream.next();
|
||||
return ERRORCLASS;
|
||||
}
|
||||
|
||||
function tokenStringFactory(delimiter) {
|
||||
var singleline = delimiter.length == 1;
|
||||
var OUTCLASS = 'string';
|
||||
|
||||
return function(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
stream.eatWhile(/[^'"]/);
|
||||
if (stream.match(delimiter)) {
|
||||
state.tokenize = tokenBase;
|
||||
return OUTCLASS;
|
||||
} else {
|
||||
stream.eat(/['"]/);
|
||||
}
|
||||
}
|
||||
if (singleline) {
|
||||
if (parserConf.singleLineStringErrors) {
|
||||
return ERRORCLASS;
|
||||
} else {
|
||||
state.tokenize = tokenBase;
|
||||
}
|
||||
}
|
||||
return OUTCLASS;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function tokenLexer(stream, state) {
|
||||
var style = state.tokenize(stream, state);
|
||||
var current = stream.current();
|
||||
|
||||
// Handle '.' connected identifiers
|
||||
if (current === '.') {
|
||||
style = state.tokenize(stream, state);
|
||||
|
||||
current = stream.current();
|
||||
if (style && (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword')){//|| knownWords.indexOf(current.substring(1)) > -1) {
|
||||
if (style === 'builtin' || style === 'keyword') style='variable';
|
||||
if (knownWords.indexOf(current.substr(1)) > -1) style='variable-2';
|
||||
|
||||
return style;
|
||||
} else {
|
||||
return ERRORCLASS;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
var external = {
|
||||
electricChars:"dDpPtTfFeE ",
|
||||
startState: function() {
|
||||
return {
|
||||
tokenize: tokenBase,
|
||||
lastToken: null,
|
||||
currentIndent: 0,
|
||||
nextLineIndent: 0,
|
||||
doInCurrentLine: false,
|
||||
ignoreKeyword: false
|
||||
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.sol()) {
|
||||
state.currentIndent += state.nextLineIndent;
|
||||
state.nextLineIndent = 0;
|
||||
state.doInCurrentLine = 0;
|
||||
}
|
||||
var style = tokenLexer(stream, state);
|
||||
|
||||
state.lastToken = {style:style, content: stream.current()};
|
||||
|
||||
if (style==='space') style=null;
|
||||
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
var trueText = textAfter.replace(/^\s+|\s+$/g, '') ;
|
||||
if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);
|
||||
if(state.currentIndent < 0) return 0;
|
||||
return state.currentIndent * conf.indentUnit;
|
||||
}
|
||||
|
||||
};
|
||||
return external;
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/vbscript", "vbscript");
|
||||
|
||||
});
|
BIN
gx/__gx_font_default.png
Normal file
BIN
gx/__gx_font_default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
BIN
gx/__gx_font_default_black.png
Normal file
BIN
gx/__gx_font_default_black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
img/fullscreen.png
Normal file
BIN
img/fullscreen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
BIN
img/gx-logo.png
Normal file
BIN
img/gx-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
img/play.png
Normal file
BIN
img/play.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 533 B |
243
index.html
Normal file
243
index.html
Normal file
|
@ -0,0 +1,243 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: dosvga;
|
||||
src: url(lp-dosvga.ttf);
|
||||
}
|
||||
body {
|
||||
background-color: rgb(0, 0, 39);
|
||||
font-family: dosvga, Arial, Helvetica, sans-serif;
|
||||
color: #999;
|
||||
}
|
||||
a, a:link, a:visited {
|
||||
text-decoration: none;
|
||||
color: #ccc;
|
||||
}
|
||||
a:hover { color: #fff; }
|
||||
a:before { content: "< "; }
|
||||
a:after { content: " >"; }
|
||||
|
||||
#code-container {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
#code {
|
||||
width: 600px;
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
#game-container {
|
||||
position: absolute;
|
||||
left: 620px;
|
||||
top: 10px;
|
||||
}
|
||||
#gx-container {
|
||||
border: 1px solid #666;
|
||||
text-align: center;
|
||||
background-color: #000;
|
||||
}
|
||||
#gx-canvas {
|
||||
border: 1px solid #222;
|
||||
background-color: #000;
|
||||
}
|
||||
#output-container {
|
||||
position: absolute;
|
||||
color: #ccc;
|
||||
display: none;
|
||||
border: 1px solid #666;
|
||||
overflow: scroll;
|
||||
height: 150px;
|
||||
}
|
||||
#js-code {
|
||||
font-size: 14px;
|
||||
font-family: courier;
|
||||
white-space: pre;
|
||||
}
|
||||
#show-js-container {
|
||||
color: #666;
|
||||
position: absolute;
|
||||
}
|
||||
#warning-container {
|
||||
white-space: pre;
|
||||
font-family: dosvga;
|
||||
color: #999;
|
||||
padding: 4px;
|
||||
}
|
||||
#share-button {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"></link>
|
||||
<link rel="stylesheet" href="codemirror/qb-ide.css"></link>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script>
|
||||
<script type="text/javascript" src="codemirror/qb-lang.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/selection/active-line.js"></script>
|
||||
<script type="text/javascript" src="util/shorty.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="code-container">
|
||||
<div id="code"></div>
|
||||
<a id="run-button" href="javascript:runProgram()">Run Program</a>
|
||||
<a id="stop-button" href="javascript:stopProgram()">Stop</a>
|
||||
<a id="share-button" href="javascript:shareProgram()">Share</a>
|
||||
</div>
|
||||
<div id="game-container">
|
||||
<div id="gx-container"></div>
|
||||
<div id="output-container">
|
||||
<div id="warning-container"></div>
|
||||
<div id="js-code"></div>
|
||||
</div>
|
||||
<div id="show-js-container"><input type="checkbox" id="show-js" onclick="window.onresize()"/> Show Javascript</div>
|
||||
</div>
|
||||
<div id="gx-footer"></div>
|
||||
</body>
|
||||
<script language="javascript" src="gx/gx.js"></script>
|
||||
<script language="javascript" src="qb.js"></script>
|
||||
<script language="javascript" src="qb2js.js"></script>
|
||||
<script language="javascript">
|
||||
// if code has been passed on the query string load it into the editor
|
||||
var qbcode = "";
|
||||
var url = location.href;
|
||||
|
||||
if (url && url.indexOf("?")) {
|
||||
var queryString = url.substring(url.indexOf("?")+1);
|
||||
var nvpairs = queryString.split("&");
|
||||
for (var i = 0; i < nvpairs.length; i++) {
|
||||
var nv = nvpairs[i].split("=");
|
||||
if (nv[0] == "qbcode") {
|
||||
var zin = new Shorty();
|
||||
qbcode = zin.inflate(atob(nv[1]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the code editor
|
||||
var editor = CodeMirror(document.querySelector("#code"), {
|
||||
lineNumbers: true,
|
||||
tabSize: 4,
|
||||
indentUnit: 4,
|
||||
value: qbcode,
|
||||
module: "vbscript",
|
||||
theme: "lesser-dark",
|
||||
height: "auto",
|
||||
styleActiveLine: true,
|
||||
extraKeys: {
|
||||
"Tab": function(cm) {
|
||||
cm.replaceSelection(" ", "end");
|
||||
}
|
||||
}
|
||||
});
|
||||
editor.setSize(600, 600);
|
||||
editor.on("beforeChange", (cm, change) => {
|
||||
if (change.origin === "paste") {
|
||||
const newText = change.text.map(line => line.replace(/\t/g, " "));
|
||||
change.update(null, null, newText);
|
||||
}
|
||||
});
|
||||
|
||||
var warnCount = 0;
|
||||
|
||||
async function runProgram() {
|
||||
GX.reset();
|
||||
QB.start();
|
||||
var qbCode = editor.getValue();
|
||||
var jsCode = QBCompiler.compile(qbCode);
|
||||
|
||||
displayWarnings();
|
||||
//displayTypes();
|
||||
|
||||
var jsDiv = document.getElementById("js-code");
|
||||
jsDiv.innerHTML = jsCode;
|
||||
window.onresize();
|
||||
|
||||
try {
|
||||
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
||||
var codeFn = new AsyncFunction(jsCode);
|
||||
await codeFn();
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
document.getElementById("gx-container").focus();
|
||||
}
|
||||
|
||||
function stopProgram() {
|
||||
QB.halt();
|
||||
GX.sceneStop();
|
||||
}
|
||||
|
||||
function shareProgram() {
|
||||
var zout = new Shorty();
|
||||
var wdiv = document.getElementById("warning-container");
|
||||
var b64 = btoa(zout.deflate(editor.getValue()));
|
||||
var baseUrl = location.href.split('?')[0];
|
||||
wdiv.innerHTML = baseUrl + "?qbcode=" + b64;
|
||||
warnCount = 1;
|
||||
window.onresize();
|
||||
}
|
||||
|
||||
function displayWarnings() {
|
||||
var wstr = "";
|
||||
var w = QBCompiler.getWarnings();
|
||||
warnCount = w.length;
|
||||
for (var i=0; i < w.length; i++) {
|
||||
wstr += w[i].line + ": " + w[i].text + "\n";
|
||||
}
|
||||
var wdiv = document.getElementById("warning-container");
|
||||
wdiv.innerHTML = wstr;
|
||||
}
|
||||
|
||||
function displayTypes() {
|
||||
var tstr = "";
|
||||
var t = QBCompiler.getTypes();
|
||||
for (var i=0; i < t.length; i++) {
|
||||
tstr += t[i].name
|
||||
}
|
||||
var wdiv = document.getElementById("warning-container");
|
||||
wdiv.innerHTML = tstr;
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
var f = document.getElementById("gx-container");
|
||||
var jsDiv = document.getElementById("output-container");
|
||||
|
||||
f.style.width = (window.innerWidth - 635) + "px";
|
||||
jsDiv.style.width = f.style.width;
|
||||
|
||||
if (document.getElementById("show-js").checked || warnCount > 0) {
|
||||
f.style.height = (window.innerHeight - 210) + "px";
|
||||
jsDiv.style.display = "block";
|
||||
jsDiv.style.top = (window.innerHeight - 200) + "px";
|
||||
}
|
||||
else {
|
||||
f.style.height = (window.innerHeight - 50) + "px";
|
||||
jsDiv.style.display = "none";
|
||||
}
|
||||
document.getElementById("show-js-container").style.top = (window.innerHeight - 45) + "px";
|
||||
document.getElementById("show-js-container").style.right = "5px";
|
||||
|
||||
document.getElementById("js-code").style.display = document.getElementById("show-js").checked ? "block" : "none";
|
||||
|
||||
editor.setSize(600, window.innerHeight - 50);
|
||||
}
|
||||
window.onresize();
|
||||
|
||||
function checkButtonState() {
|
||||
var stopButton = document.getElementById("stop-button");
|
||||
if (GX.sceneActive() || QB.running()) {
|
||||
stopButton.style.display = "inline";
|
||||
}
|
||||
else {
|
||||
stopButton.style.display = "none";
|
||||
}
|
||||
|
||||
setTimeout(checkButtonState, 100);
|
||||
}
|
||||
checkButtonState();
|
||||
|
||||
</script>
|
||||
</html>
|
BIN
lp-dosvga.ttf
Normal file
BIN
lp-dosvga.ttf
Normal file
Binary file not shown.
922
qb.js
Normal file
922
qb.js
Normal file
|
@ -0,0 +1,922 @@
|
|||
var QB = new function() {
|
||||
// QB constants
|
||||
this.COLUMN_ADVANCE = Symbol("COLUMN_ADVANCE");
|
||||
this.PREVENT_NEWLINE = Symbol("PREVENT_NEWLINE");
|
||||
|
||||
var _fgColor = null;
|
||||
var _bgColor = null;
|
||||
var _lastX = 0;
|
||||
var _lastY = 0;
|
||||
//var _fntDefault = null;
|
||||
var _locX = 0;
|
||||
var _locY = 0;
|
||||
var _lastKey = null;
|
||||
var _keyBuffer = [];
|
||||
var _inputMode = false;
|
||||
var _haltedFlag = false;
|
||||
var _runningFlag = false;
|
||||
|
||||
// Array handling methods
|
||||
// ----------------------------------------------------
|
||||
this.initArray = function(dimensions, obj) {
|
||||
var a = {};
|
||||
if (dimensions && dimensions.length > 0) {
|
||||
a._dimensions = dimensions;
|
||||
}
|
||||
else {
|
||||
// default to single dimension to support Dim myArray() syntax
|
||||
// for convenient hashtable declaration
|
||||
a._dimensions = [0];
|
||||
}
|
||||
a._newObj = { value: obj };
|
||||
return a;
|
||||
};
|
||||
|
||||
this.resizeArray = function(a, dimensions, obj, preserve) {
|
||||
if (!preserve) {
|
||||
var props = Object.getOwnPropertyNames(a);
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
if (props[i] != "_newObj") {
|
||||
delete a[props[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
a._dimensions = dimensions;
|
||||
};
|
||||
|
||||
this.arrayValue = function(a, indexes) {
|
||||
var value = a;
|
||||
for (var i=0; i < indexes.length; i++) {
|
||||
if (value[indexes[i]] == undefined) {
|
||||
if (i == indexes.length-1) {
|
||||
value[indexes[i]] = JSON.parse(JSON.stringify(a._newObj));
|
||||
}
|
||||
else {
|
||||
value[indexes[i]] = {};
|
||||
}
|
||||
}
|
||||
value = value[indexes[i]];
|
||||
}
|
||||
|
||||
// added to treat regular objects and qb hashtable/arrays the same
|
||||
if (value.value == undefined) {
|
||||
value = { value: value };
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
this.import = async function(url) {
|
||||
await fetch(url).then(response => response.text()).then((response) => {
|
||||
var f = new Function(response);
|
||||
f();
|
||||
});
|
||||
};
|
||||
/*
|
||||
this.byRef = function(v) {
|
||||
if (v != undefined && v.value == undefined) {
|
||||
return { value: v };
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
this.byValue = function(v) {
|
||||
if (v !== undefined && v.value != undefined) {
|
||||
return v.value;
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
this.$var = function(varname) {
|
||||
return window[varname];
|
||||
};
|
||||
|
||||
this.$func = function(methodName) {
|
||||
var func = window["sub_" + methodName];
|
||||
if (func == undefined) {
|
||||
func = window["func_" + methodName]
|
||||
}
|
||||
return func;
|
||||
}
|
||||
*/
|
||||
// Process control methods
|
||||
// -------------------------------------------
|
||||
this.halt = function() {
|
||||
_haltedFlag = true;
|
||||
_runningFlag = false;
|
||||
};
|
||||
|
||||
this.halted = function() {
|
||||
return _haltedFlag;
|
||||
};
|
||||
|
||||
this.end = function() {
|
||||
_runningFlag = false;
|
||||
};
|
||||
|
||||
this.start = function() {
|
||||
_runningFlag = true;
|
||||
_haltedFlag = false;
|
||||
}
|
||||
|
||||
this.running = function() {
|
||||
return _runningFlag;
|
||||
};
|
||||
|
||||
|
||||
// Extended QB64 Keywords
|
||||
// --------------------------------------------
|
||||
this.func__Alpha = function(rgb, imageHandle) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).a * 255;
|
||||
};
|
||||
|
||||
this.func__Alpha32 = function(rgb) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).a * 255;
|
||||
};
|
||||
|
||||
this.func__Atan2 = function(y, x) {
|
||||
return Math.atan2(y, x);
|
||||
};
|
||||
|
||||
this.func__Blue = function(rgb, imageHandle) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).b;
|
||||
};
|
||||
|
||||
this.func__Blue32 = function(rgb) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).b;
|
||||
};
|
||||
|
||||
this.sub__Delay = async function(seconds) {
|
||||
await GX.sleep(seconds*1000);
|
||||
};
|
||||
|
||||
this.func__FontHeight = function(fnt) {
|
||||
//return GX.fontHeight(_fntDefault) + GX.fontLineSpacing(_fntDefault);
|
||||
return 16;
|
||||
};
|
||||
|
||||
this.func__FontWidth = function(fnt) {
|
||||
//return GX.fontWidth(_fntDefault);
|
||||
return 8;
|
||||
};
|
||||
|
||||
this.func__Green = function(rgb, imageHandle) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).g;
|
||||
};
|
||||
|
||||
this.func__Green32 = function(rgb) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).g;
|
||||
};
|
||||
|
||||
this.func__Height = function(img) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return GX.sceneHeight();
|
||||
};
|
||||
|
||||
this.func__InStrRev = function(arg1, arg2, arg3) {
|
||||
var startIndex = +Infinity;
|
||||
var strSource = "";
|
||||
var strSearch = "";
|
||||
if (arg3 != undefined) {
|
||||
startIndex = arg1-1;
|
||||
strSource = String(arg2);
|
||||
strSearch = String(arg3);
|
||||
}
|
||||
else {
|
||||
strSource = String(arg1);
|
||||
strSearch = String(arg2);
|
||||
}
|
||||
return strSource.lastIndexOf(strSearch, startIndex)+1;
|
||||
};
|
||||
|
||||
this.func__KeyDown = function(keyCode) {
|
||||
// TODO: actual implementation (maybe)
|
||||
// this is here just to allow converted programs to compile
|
||||
return GX.keyDown(keyCode) ? -1 : 0;
|
||||
};
|
||||
this.func__KeyHit = function() {
|
||||
// TODO: actual implementation (maybe)
|
||||
// this is here just to support rendering loops that are using _KeyHit as the exit criteria
|
||||
return 0;
|
||||
};
|
||||
|
||||
this.sub__Limit = async function(fps) {
|
||||
// TODO: limit based on frame rate
|
||||
// need to incorporate time elapsed from last loop invocation
|
||||
|
||||
await GX.sleep(1000/fps);
|
||||
};
|
||||
|
||||
this.func__MouseInput = function() {
|
||||
return GX._mouseInput();
|
||||
};
|
||||
|
||||
this.func__MouseX = function() {
|
||||
return GX.mouseX();
|
||||
};
|
||||
|
||||
this.func__MouseY = function() {
|
||||
return GX.mouseY();
|
||||
};
|
||||
|
||||
this.func__MouseButton = function(button) {
|
||||
return GX.mouseButton(button);
|
||||
};
|
||||
|
||||
this.func__NewImage = function(iwidth, iheight) {
|
||||
return {
|
||||
width: iwidth,
|
||||
height: iheight
|
||||
};
|
||||
};
|
||||
|
||||
this.sub__PrintString = function(x, y, s) {
|
||||
// TODO: check the background opacity mode
|
||||
// Draw the text background
|
||||
var ctx = GX.ctx();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(x, y, QB.func__FontWidth(), QB.func__FontHeight());
|
||||
//GX.drawText(_fntDefault, x, y, s);
|
||||
ctx.font = "16px dosvga";
|
||||
ctx.fillStyle = _fgColor.rgba();
|
||||
ctx.fillText(s, x, y+QB.func__FontHeight()-6);
|
||||
};
|
||||
|
||||
this.func__PrintWidth = function(s) {
|
||||
if (!s) { return 0; }
|
||||
return String(s).length * QB.func__FontWidth();
|
||||
};
|
||||
|
||||
this.func__Pi = function(m) {
|
||||
if (m == undefined) {
|
||||
m = 1;
|
||||
}
|
||||
return Math.PI * m;
|
||||
}
|
||||
|
||||
function _rgb(r, g, b) {
|
||||
return {
|
||||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: 1,
|
||||
rgb: function() { return "rgb(" + this.r + "," + this.g + "," + this.b + ")"; },
|
||||
rgba: function() { return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")"; }
|
||||
}
|
||||
}
|
||||
|
||||
this.func__Red = function(rgb, imageHandle) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).r;
|
||||
};
|
||||
|
||||
this.func__Red32 = function(rgb) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return _color(rgb).r;
|
||||
};
|
||||
|
||||
this.func__RGB = function(r, g, b) {
|
||||
return this.func__RGB32(r, g, b);
|
||||
};
|
||||
|
||||
this.func__RGB32 = function(r, g, b, a) {
|
||||
if (a == undefined) {
|
||||
a = 255;
|
||||
}
|
||||
if (b == undefined && g != undefined) {
|
||||
a = g;
|
||||
g = r;
|
||||
b = r;
|
||||
}
|
||||
else if (b == undefined) {
|
||||
g = r;
|
||||
b = r;
|
||||
}
|
||||
a = a / 255;
|
||||
|
||||
return {
|
||||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: a,
|
||||
rgb: function() { return "rgb(" + this.r + "," + this.g + "," + this.b + ")"; },
|
||||
rgba: function() { return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")"; }
|
||||
}
|
||||
}
|
||||
|
||||
this.func__Round = function(value) {
|
||||
return Math.round(value);
|
||||
};
|
||||
|
||||
this.func__ScreenExists = function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.sub__Title = function(title) {
|
||||
document.title = title;
|
||||
};
|
||||
|
||||
this.func__Trim = function(value) {
|
||||
return value.trim();
|
||||
};
|
||||
|
||||
this.func__Width = function(img) {
|
||||
// TODO: implement corresponding logic when an image handle is supplied (maybe)
|
||||
return GX.sceneWidth();
|
||||
};
|
||||
|
||||
|
||||
// QB45 Keywords
|
||||
// --------------------------------------------
|
||||
this.func_Asc = function(value, pos) {
|
||||
if (pos == undefined) {
|
||||
pos = 0;
|
||||
}
|
||||
else { pos--; }
|
||||
|
||||
return String(value).charCodeAt(pos);
|
||||
}
|
||||
|
||||
this.func_Abs = function(value) {
|
||||
return Math.abs(value);
|
||||
};
|
||||
|
||||
this.func_Chr = function(charCode) {
|
||||
return String.fromCharCode(charCode);
|
||||
};
|
||||
|
||||
this.sub_Cls = function() {
|
||||
// TODO: parameter variants
|
||||
var ctx = GX.ctx();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(0, 0, QB.func__Width() , QB.func__Height());
|
||||
};
|
||||
|
||||
function _color(c) {
|
||||
if (c != undefined && c.r != undefined) {
|
||||
return c;
|
||||
}
|
||||
return QB.func__RGB(0,0,0);
|
||||
}
|
||||
|
||||
this.sub_Color = function(fg, bg) {
|
||||
if (fg != undefined) {
|
||||
_fgColor = _color(fg);
|
||||
}
|
||||
if (bg != undefined) {
|
||||
_bgColor = _color(bg);
|
||||
}
|
||||
};
|
||||
|
||||
this.func_Command = function() {
|
||||
return "";
|
||||
};
|
||||
|
||||
this.func_Cos = function(value) {
|
||||
return Math.cos(value);
|
||||
};
|
||||
|
||||
this.func_Exp = function(value) {
|
||||
return Math.exp(value);
|
||||
};
|
||||
|
||||
this.func_Fix = function(value) {
|
||||
if (value >=0) {
|
||||
return Math.floor(value);
|
||||
}
|
||||
else {
|
||||
return Math.floor(Math.abs(value)) * -1;
|
||||
}
|
||||
};
|
||||
|
||||
function _textColumns() {
|
||||
return Math.floor(QB.func__Width() / QB.func__FontWidth());
|
||||
}
|
||||
|
||||
function _textRows() {
|
||||
return Math.floor(QB.func__Height() / QB.func__FontHeight());
|
||||
}
|
||||
|
||||
this.sub_Input = async function(values, preventNewline, addQuestionPrompt, prompt) {
|
||||
_lastKey = null;
|
||||
var str = "";
|
||||
_inputMode = true;
|
||||
|
||||
if (prompt != undefined) {
|
||||
QB.sub_Print([prompt, QB.PREVENT_NEWLINE]);
|
||||
}
|
||||
if (prompt == undefined || addQuestionPrompt) {
|
||||
QB.sub_Print(["? ", QB.PREVENT_NEWLINE]);
|
||||
}
|
||||
|
||||
if (!preventNewline && _locY > _textRows()-1) {
|
||||
await _printScroll();
|
||||
_locY = _textRows()-1;
|
||||
}
|
||||
//QB.sub__PrintString(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), "? ");
|
||||
//_locX += 2;
|
||||
while (_lastKey != "Enter") {
|
||||
|
||||
if (_lastKey == "Backspace" && str.length > 0) {
|
||||
_locX--;
|
||||
|
||||
var ctx = GX.ctx();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), QB.func__FontWidth() , QB.func__FontHeight());
|
||||
str = str.substring(0, str.length-1);
|
||||
}
|
||||
|
||||
else if (_lastKey && _lastKey.length < 2) {
|
||||
QB.sub__PrintString(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), _lastKey);
|
||||
_locX++;
|
||||
str += _lastKey;
|
||||
}
|
||||
|
||||
_lastKey = null;
|
||||
await GX.sleep(10);
|
||||
}
|
||||
if (!preventNewline) {
|
||||
_locY++;
|
||||
_locX = 0;
|
||||
}
|
||||
|
||||
if (values.length < 2) {
|
||||
values[0] = str;
|
||||
}
|
||||
else {
|
||||
var vparts = str.split(",");
|
||||
for (var i=0; i < values.length; i++) {
|
||||
values[i] = vparts[i] ? vparts[i] : "";
|
||||
}
|
||||
}
|
||||
_inputMode = false;
|
||||
}
|
||||
|
||||
this.func_InKey = function() {
|
||||
if (_keyBuffer.length < 1) {
|
||||
return "";
|
||||
}
|
||||
return _keyBuffer.shift();
|
||||
// var temp = _lastKey;
|
||||
// _lastKey = "";
|
||||
// return temp;
|
||||
}
|
||||
|
||||
this.func_InStr = function(arg1, arg2, arg3) {
|
||||
var startIndex = 0;
|
||||
var strSource = "";
|
||||
var strSearch = "";
|
||||
if (arg3 != undefined) {
|
||||
startIndex = arg1-1;
|
||||
strSource = String(arg2);
|
||||
strSearch = String(arg3);
|
||||
}
|
||||
else {
|
||||
strSource = String(arg1);
|
||||
strSearch = String(arg2);
|
||||
}
|
||||
return strSource.indexOf(strSearch, startIndex)+1;
|
||||
};
|
||||
|
||||
this.func_Int = function(value) {
|
||||
return Math.floor(value);
|
||||
};
|
||||
|
||||
this.func_LCase = function(value) {
|
||||
return String(value).toLowerCase();
|
||||
};
|
||||
|
||||
this.func_Left = function(value, n) {
|
||||
return String(value).substring(0, n);
|
||||
};
|
||||
|
||||
this.func_Len = function(value) {
|
||||
return String(value).length;
|
||||
};
|
||||
|
||||
this.func_Log = function(value) {
|
||||
return Math.log(value);
|
||||
};
|
||||
|
||||
this.sub_Circle = function(step, x, y, radius, color, startAngle, endAngle, aspect) {
|
||||
// TODO: implement aspect parameter
|
||||
if (color == undefined) {
|
||||
color = _fgColor;
|
||||
}
|
||||
else {
|
||||
color = _color(color);
|
||||
}
|
||||
|
||||
if (startAngle == undefined) { startAngle = 0; }
|
||||
if (endAngle == undefined) { endAngle = 2 * Math.PI; }
|
||||
|
||||
if (step) {
|
||||
x = _lastX + x;
|
||||
y = _lastY + y;
|
||||
}
|
||||
_lastX = x;
|
||||
_lastY = y;
|
||||
|
||||
var ctx = GX.ctx();
|
||||
ctx.strokeStyle = color.rgba();
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, startAngle, endAngle);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
this.sub_Line = function(sstep, sx, sy, estep, ex, ey, color, style, pattern) {
|
||||
if (color == undefined) {
|
||||
if (style == "BF") {
|
||||
color = _bgColor;
|
||||
}
|
||||
else {
|
||||
color = _fgColor;
|
||||
}
|
||||
}
|
||||
else {
|
||||
color = _color(color);
|
||||
}
|
||||
|
||||
if (sstep) {
|
||||
sx = _lastX + sx;
|
||||
sy = _lastY + sy;
|
||||
}
|
||||
if (sx == undefined) {
|
||||
sx = _lastX;
|
||||
sy = _lastY;
|
||||
}
|
||||
_lastX = sx;
|
||||
_lastY = sy;
|
||||
|
||||
if (estep) {
|
||||
ex = _lastX + ex;
|
||||
ey = _lastY + ey;
|
||||
}
|
||||
_lastX = ex;
|
||||
_lastY = ey;
|
||||
|
||||
var ctx = GX.ctx();
|
||||
|
||||
if (style == "B") {
|
||||
ctx.strokeStyle = color.rgba();
|
||||
ctx.beginPath();
|
||||
ctx.strokeRect(sx, sy, ex-sx, ey-sy)
|
||||
}
|
||||
else if (style == "BF") {
|
||||
ctx.fillStyle = color.rgba();
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(sx, sy, ex-sx, ey-sy)
|
||||
}
|
||||
else {
|
||||
ctx.strokeStyle = color.rgba();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(sx, sy);
|
||||
ctx.lineTo(ex, ey);
|
||||
ctx.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
this.sub_LineInput = async function(values, preventNewline, addQuestionPrompt, prompt) {
|
||||
await QB.sub_Input(values, preventNewline, addQuestionPrompt, prompt);
|
||||
}
|
||||
|
||||
this.sub_Locate = function(row, col) {
|
||||
// TODO: implement cursor positioning/display
|
||||
if (row && row > 0 && row <= _textRows()) {
|
||||
_locY = row-1;
|
||||
}
|
||||
if (col && col > 0 && col <= _textColumns()) {
|
||||
_locX = col-1;
|
||||
}
|
||||
};
|
||||
|
||||
this.func_LTrim = function(value) {
|
||||
return String(value).trimStart();
|
||||
}
|
||||
|
||||
this.func_Mid = function(value, n, len) {
|
||||
if (len == undefined) {
|
||||
return String(value).substring(n-1);
|
||||
}
|
||||
else {
|
||||
return String(value).substring(n-1, n+len-1);
|
||||
}
|
||||
};
|
||||
|
||||
this.sub_Print = async function(args) {
|
||||
// Print called with no arguments
|
||||
if (args == undefined || args == null || args.length < 1) {
|
||||
args = [""];
|
||||
}
|
||||
|
||||
var ctx = GX.ctx();
|
||||
var preventNewline = (args[args.length-1] == QB.PREVENT_NEWLINE || args[args.length-1] == QB.COLUMN_ADVANCE);
|
||||
|
||||
for (var ai = 0; ai < args.length; ai++) {
|
||||
if (args[ai] == QB.PREVENT_NEWLINE) {
|
||||
// ignore as we will just concatenate the next arg
|
||||
}
|
||||
else if (args[ai] == QB.COLUMN_ADVANCE) {
|
||||
// TODO: advance to the next column offset
|
||||
_locX += 14 - _locX % 13;
|
||||
}
|
||||
else {
|
||||
var str = args[ai];
|
||||
var lines = String(str).split("\n");
|
||||
for (var i=0; i < lines.length; i++) {
|
||||
var x = _locX * QB.func__FontWidth();
|
||||
var y = -1;
|
||||
|
||||
// scroll the screen
|
||||
if (_locY < _textRows()-1) {
|
||||
y = _locY * QB.func__FontHeight();
|
||||
//_locY = _locY + 1;
|
||||
}
|
||||
else {
|
||||
//await _printScroll();
|
||||
|
||||
y = (_locY) * QB.func__FontHeight();
|
||||
}
|
||||
|
||||
// TODO: check the background opacity mode
|
||||
// Draw the text background
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(x, y, QB.func__FontWidth() * lines[i].length, QB.func__FontHeight());
|
||||
|
||||
//GX.drawText(_fntDefault, x, y, lines[i]);
|
||||
ctx.font = "16px dosvga";
|
||||
ctx.fillStyle = _fgColor.rgba();
|
||||
ctx.fillText(lines[i], x, y+QB.func__FontHeight()-6);
|
||||
|
||||
_locX += lines[i].length;
|
||||
|
||||
if (i < lines.length-1) {
|
||||
if (_locY < _textRows()-1) {
|
||||
_locY = _locY + 1;
|
||||
_locX = 0;
|
||||
}
|
||||
else {
|
||||
await _printScroll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!preventNewline) {
|
||||
_locX = 0;
|
||||
if (_locY < _textRows()-1) {
|
||||
_locY = _locY + 1;
|
||||
}
|
||||
else {
|
||||
await _printScroll();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
this.sub_Print = async function(str) {
|
||||
if (str == undefined || str == null) {
|
||||
str = "";
|
||||
}
|
||||
var ctx = GX.ctx();
|
||||
var lines = String(str).split("\n");
|
||||
for (var i=0; i < lines.length; i++) {
|
||||
var x = _locX * QB.func__FontWidth();
|
||||
var y = -1;
|
||||
|
||||
// scroll the screen
|
||||
if (_locY < _textRows()) {
|
||||
y = _locY * QB.func__FontHeight();
|
||||
_locY = _locY + 1;
|
||||
}
|
||||
else {
|
||||
await _printScroll();
|
||||
|
||||
y = (_locY-1) * QB.func__FontHeight();
|
||||
}
|
||||
|
||||
// TODO: check the background opacity mode
|
||||
// Draw the text background
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(x, y, QB.func__FontWidth() * lines[0].length, QB.func__FontHeight());
|
||||
|
||||
GX.drawText(_fntDefault, x, y, lines[i]);
|
||||
}
|
||||
_locX = 0;
|
||||
};
|
||||
*/
|
||||
async function _printScroll() {
|
||||
var img = new Image();
|
||||
img.src = GX.canvas().toDataURL("image/png");
|
||||
while (!img.complete) {
|
||||
await GX.sleep(10);
|
||||
}
|
||||
var ctx = GX.ctx();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = _bgColor.rgba();
|
||||
ctx.fillRect(0, 0, QB.func__Width(), QB.func__Height());
|
||||
ctx.drawImage(img, 0, -QB.func__FontHeight());
|
||||
}
|
||||
|
||||
this.sub_PSet = function(sstep, x, y, color) {
|
||||
if (color == undefined) {
|
||||
color = _fgColor;
|
||||
}
|
||||
else {
|
||||
color = _color(color);
|
||||
}
|
||||
if (sstep) {
|
||||
x = _lastX + x;
|
||||
y = _lastY + y;
|
||||
}
|
||||
_lastX = x;
|
||||
_lastY = y;
|
||||
|
||||
var ctx = GX.ctx();
|
||||
ctx.fillStyle = color.rgba();
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
};
|
||||
|
||||
this.func_Right = function(value, n) {
|
||||
if (value == undefined) {
|
||||
return "";
|
||||
}
|
||||
var s = String(value);
|
||||
return s.substring(s.length-n, s.length);
|
||||
};
|
||||
|
||||
this.func_RTrim = function(value) {
|
||||
return String(value).trimEnd();
|
||||
}
|
||||
|
||||
this.func_Rnd = function(n) {
|
||||
// TODO: implement modifier parameter
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
this.sub_Screen = async function(mode) {
|
||||
if (mode == 0) {
|
||||
GX.sceneCreate(640, 400);
|
||||
//GX.fontLineSpacing(_fntDefault, 2);
|
||||
}
|
||||
else if (mode < 2 || mode == 7 || mode == 13) {
|
||||
GX.sceneCreate(320, 200);
|
||||
//GX.fontLineSpacing(_fntDefault, 0);
|
||||
}
|
||||
else if (mode == 8) {
|
||||
GX.sceneCreate(640, 200);
|
||||
//GX.fontLineSpacing(_fntDefault, 0);
|
||||
}
|
||||
else if (mode == 9 || mode == 10) {
|
||||
GX.sceneCreate(640, 350);
|
||||
//GX.fontLineSpacing(_fntDefault, 0);
|
||||
}
|
||||
else if (mode == 11 || mode == 12) {
|
||||
GX.sceneCreate(640, 480);
|
||||
//GX.fontLineSpacing(_fntDefault, 0);
|
||||
}
|
||||
else if (mode.width != undefined) {
|
||||
GX.sceneCreate(mode.width, mode.height);
|
||||
//GX.fontLineSpacing(_fntDefault, 2);
|
||||
}
|
||||
|
||||
// initialize the graphics
|
||||
_fgColor = this.func__RGB(255, 255, 255);
|
||||
_bgColor = this.func__RGB(0, 0, 0);
|
||||
_lastX = 0;
|
||||
_lastY = 0;
|
||||
_locX = 0;
|
||||
_locY = 0;
|
||||
_lastKey = null;
|
||||
_inputMode = false;
|
||||
};
|
||||
|
||||
this.func_Sgn = function(value) {
|
||||
if (value > 0) { return 1; }
|
||||
else if (value < 0) { return -1; }
|
||||
else { return 0; }
|
||||
};
|
||||
|
||||
this.func_Sin = function(value) {
|
||||
return Math.sin(value);
|
||||
};
|
||||
|
||||
this.sub_Sleep = async function(seconds) {
|
||||
var elapsed = 0;
|
||||
var totalWait = Infinity;
|
||||
if (seconds != undefined) {
|
||||
totalWait = seconds*1000;
|
||||
}
|
||||
|
||||
_lastKey = null;
|
||||
while (!_lastKey && elapsed < totalWait) {
|
||||
await GX.sleep(100);
|
||||
elapsed += 100;
|
||||
}
|
||||
};
|
||||
|
||||
this.func_Sqr = function(value) {
|
||||
return Math.sqrt(value);
|
||||
};
|
||||
|
||||
this.func_Str = function(value) {
|
||||
return String(value);
|
||||
};
|
||||
|
||||
this.sub_Swap = function(values) {
|
||||
var temp = values[1];
|
||||
values[1] = values[0];
|
||||
values[0] = temp;
|
||||
};
|
||||
|
||||
this.func_Tan = function(value) {
|
||||
return Math.tan(value);
|
||||
};
|
||||
|
||||
this.func_Timer = function(accuracy) {
|
||||
// TODO: implement optional accuracy
|
||||
var midnight = new Date();
|
||||
midnight.setHours(0, 0, 0, 0);
|
||||
return ((new Date()).getTime() - midnight.getTime()) / 1000;
|
||||
};
|
||||
|
||||
this.func_Atn = function(value) {
|
||||
return Math.atan(value);
|
||||
};
|
||||
|
||||
this.func_UBound = function(a, dimension) {
|
||||
if (dimension == undefined) {
|
||||
dimension = 1;
|
||||
}
|
||||
return a._dimensions[dimension-1];
|
||||
//return a.length-1;
|
||||
};
|
||||
|
||||
this.func_UCase = function(value) {
|
||||
return String(value).toUpperCase();
|
||||
};
|
||||
|
||||
this.func_Val = function(value) {
|
||||
return Number(value);
|
||||
};
|
||||
|
||||
|
||||
// QBJS-only methods
|
||||
// ---------------------------------------------------------------------------------
|
||||
this.func_Fetch = async function(url) {
|
||||
var response = await fetch(url);
|
||||
var responseText = await(response.text());
|
||||
return {
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text: responseText
|
||||
};
|
||||
};
|
||||
|
||||
this.func_FromJSON = function(s) {
|
||||
return JSON.parse(s);
|
||||
};
|
||||
|
||||
this.func_ToJSON = function(a) {
|
||||
return JSON.stringify(a);
|
||||
}
|
||||
|
||||
|
||||
function _init() {
|
||||
// initialize the fonts
|
||||
/*if (!_fntDefault) {
|
||||
_fntDefault = GX.fontCreate("./qb/font.png", 8, 14,
|
||||
"`1234567890-=~!@#$%^&*()_+\n" +
|
||||
"qwertyuiop[]\\QWERTYUIOP{}|\n" +
|
||||
"asdfghjkl;'ASDFGHJKL:\"\n" +
|
||||
"zxcvbnm,./ZXCVBNM<>?");
|
||||
}*/
|
||||
|
||||
addEventListener("keydown", function(event) {
|
||||
if (_inputMode) {
|
||||
event.preventDefault();
|
||||
}
|
||||
_lastKey = event.key;
|
||||
});
|
||||
|
||||
addEventListener("keyup", function(event) {
|
||||
_keyBuffer.push(event.key);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
_init();
|
||||
}
|
2365
tools/qb2js.bas
Normal file
2365
tools/qb2js.bas
Normal file
File diff suppressed because it is too large
Load diff
360
tools/webserver.bas
Normal file
360
tools/webserver.bas
Normal file
|
@ -0,0 +1,360 @@
|
|||
' HTTP 1.1 Compliant Web Server
|
||||
' Author: luke
|
||||
' Source: https://www.qb64.org/forum/index.php?topic=2052.0
|
||||
' This program is made available for you to use, modify and distribute it as you wish,
|
||||
' all under the condition you do not claim original authorship.
|
||||
'$ExeIcon:'./../gx/resource/gx.ico'
|
||||
$Console:Only
|
||||
Option _Explicit
|
||||
DefLng A-Z
|
||||
|
||||
Const MAX_CONNECTIONS = 8
|
||||
Dim PORT As Integer: PORT = 8080
|
||||
If _CommandCount > 0 Then
|
||||
PORT = Val(Command$(1))
|
||||
End If
|
||||
|
||||
Const FALSE = 0
|
||||
Const TRUE = -1
|
||||
Dim Shared CRLF As String
|
||||
CRLF = Chr$(13) + Chr$(10)
|
||||
Const HTTP_10 = 1
|
||||
Const HTTP_11 = 11
|
||||
Const HTTP_GET = 1
|
||||
Const HTTP_HEAD = 2
|
||||
Const HTTP_POST = 3
|
||||
Type connection_t
|
||||
handle As Long
|
||||
read_buf As String
|
||||
http_version As Integer
|
||||
method As Integer
|
||||
request_uri As String
|
||||
End Type
|
||||
|
||||
Type http_error_t
|
||||
code As Integer
|
||||
message As String
|
||||
connection As Integer
|
||||
End Type
|
||||
|
||||
Type file_error_t
|
||||
failed As Integer
|
||||
code As Integer
|
||||
End Type
|
||||
|
||||
Dim i
|
||||
Dim num_active_connections
|
||||
Dim server_handle
|
||||
Dim Shared Connections(1 To MAX_CONNECTIONS) As connection_t
|
||||
Dim Shared Http_error_info As http_error_t
|
||||
Dim Shared File_error_info As file_error_t
|
||||
|
||||
On Error GoTo error_handler
|
||||
|
||||
server_handle = _OpenHost("TCP/IP:" + LTrim$(Str$(PORT)))
|
||||
Print "Listening on port:" + Str$(PORT)
|
||||
Do
|
||||
If num_active_connections < MAX_CONNECTIONS Then
|
||||
Dim new_connection
|
||||
new_connection = _OpenConnection(server_handle)
|
||||
If new_connection Then
|
||||
num_active_connections = num_active_connections + 1
|
||||
For i = 1 To MAX_CONNECTIONS
|
||||
If Connections(i).handle = 0 Then
|
||||
Dim empty_connection As connection_t
|
||||
Connections(i) = empty_connection
|
||||
Connections(i).handle = new_connection
|
||||
num_active_connections = num_active_connections - 1
|
||||
Exit For
|
||||
End If
|
||||
Next i
|
||||
End If
|
||||
End If
|
||||
|
||||
For i = 1 To MAX_CONNECTIONS
|
||||
If Connections(i).handle Then
|
||||
Dim buf$
|
||||
Get #Connections(i).handle, , buf$
|
||||
If buf$ <> "" Then
|
||||
Connections(i).read_buf = Connections(i).read_buf + buf$
|
||||
process_request i
|
||||
http_error_complete:
|
||||
End If
|
||||
End If
|
||||
Next i
|
||||
_Limit 240
|
||||
Loop
|
||||
|
||||
|
||||
|
||||
error_handler:
|
||||
If Err = 100 Then 'HTTP error
|
||||
Print "HTTP error"; Http_error_info.code; Http_error_info.message; " for connection"; Http_error_info.connection
|
||||
Resume http_error_complete
|
||||
End If
|
||||
Print "error"; Err; "on line"; _ErrorLine
|
||||
End
|
||||
|
||||
file_error_handler:
|
||||
File_error_info.failed = TRUE
|
||||
File_error_info.code = Err
|
||||
Resume Next
|
||||
|
||||
Sub http_send_status (c, code, message As String)
|
||||
Dim s$
|
||||
s$ = "HTTP/1.1" + Str$(code) + " " + message + CRLF
|
||||
Put #Connections(c).handle, , s$
|
||||
End Sub
|
||||
|
||||
Sub http_send_header (c, header As String, value As String)
|
||||
Dim s$
|
||||
s$ = header + ": " + value + CRLF
|
||||
Put #Connections(c).handle, , s$
|
||||
End Sub
|
||||
|
||||
Sub http_end_headers (c)
|
||||
Put #Connections(c).handle, , CRLF
|
||||
End Sub
|
||||
|
||||
Sub http_send_body (c, body As String)
|
||||
Put #Connections(c).handle, , body
|
||||
End Sub
|
||||
|
||||
Sub http_do_get (c)
|
||||
Dim filepath As String, filedata As String
|
||||
Dim fh
|
||||
filepath = get_requested_filesystem_path(c)
|
||||
Print filepath
|
||||
If Not _FileExists(filepath) Then http_error 404, "Not Found", c
|
||||
|
||||
On Error GoTo file_error_handler
|
||||
fh = FreeFile
|
||||
File_error_info.failed = FALSE
|
||||
Open filepath For Binary As #fh
|
||||
On Error GoTo error_handler
|
||||
If File_error_info.failed Then http_error 403, "Permission Denied", c
|
||||
|
||||
'Doing this all in one go isn't healthy for a number of reasons (memory usage, starving other clients)
|
||||
'It should be done in chunks in the main loop
|
||||
filedata = Space$(LOF(fh))
|
||||
Get #fh, , filedata
|
||||
Close #fh
|
||||
http_send_status c, 200, "OK"
|
||||
http_send_header c, "Content-Length", LTrim$(Str$(Len(filedata)))
|
||||
http_send_header c, "Access-Control-Allow-Origin", "true"
|
||||
http_send_header c, "Connection", "close"
|
||||
http_end_headers c
|
||||
http_send_body c, filedata
|
||||
close_connection c
|
||||
End Sub
|
||||
|
||||
Sub http_do_head (c)
|
||||
Print "http_do_head"
|
||||
Dim s$
|
||||
s$ = "HTTP/1.1 200 OK" + CRLF + CRLF
|
||||
Put #Connections(c).handle, , s$
|
||||
End Sub
|
||||
|
||||
Sub http_do_post (c)
|
||||
Print "POST"
|
||||
Print Connections(c).request_uri
|
||||
Dim path As String
|
||||
path = Right$(Connections(c).request_uri, Len(Connections(c).request_uri) - 1)
|
||||
Dim idx As Integer
|
||||
idx = _InStrRev(path, "/")
|
||||
path = Left$(path, idx)
|
||||
|
||||
Dim basFile As String
|
||||
basFile = path + "game.bas"
|
||||
Dim jsFile As String
|
||||
jsFile = path + "game.js"
|
||||
|
||||
If _FileExists(basFile) Then Kill basFile
|
||||
Dim fh
|
||||
fh = FreeFile
|
||||
Open basFile For Binary As #fh
|
||||
Put #fh, , Connections(c).read_buf
|
||||
Close #fh
|
||||
|
||||
Shell "qb2js " + basFile + " > " + jsFile
|
||||
|
||||
close_connection c
|
||||
End Sub
|
||||
|
||||
Sub close_connection (c)
|
||||
Close #Connections(c).handle
|
||||
Connections(c).handle = 0
|
||||
End Sub
|
||||
|
||||
Function get_requested_filesystem_path$ (c)
|
||||
'7230 5.3 also 3986 for URI
|
||||
'Origin form only for now
|
||||
Dim raw_path As String
|
||||
raw_path = Connections(c).request_uri
|
||||
If Left$(raw_path, 1) <> "/" Then http_error 400, "Malformed URI", c
|
||||
|
||||
Dim hash, questionmark, path_len
|
||||
hash = InStr(raw_path, "#") 'Clients shouldn't be sending fragments, but we will gracefully ignore them
|
||||
questionmark = InStr(raw_path, "?")
|
||||
path_len = Len(raw_path)
|
||||
If hash > 0 Then path_len = hash - 1
|
||||
'If questionmark > 0 And questionmark < hash Then path_len = questionmark - 1
|
||||
If questionmark > 0 Then path_len = questionmark - 1
|
||||
' Query strings are ignored for now
|
||||
|
||||
'Dim cwd As String
|
||||
'cwd = _CWD$
|
||||
'$If WIN Then
|
||||
' 'raw_path = GXSTR_Replace(raw_path, "/", "\")
|
||||
' cwd = GXSTR_Replace(cwd, "\", "/")
|
||||
'$End If
|
||||
|
||||
'Print "--> " + Left$(raw_path, path_len)
|
||||
|
||||
get_requested_filesystem_path = _STARTDIR$ + cannonicalise_path(percent_decode(Left$(raw_path, path_len)))
|
||||
End Function
|
||||
|
||||
Function percent_decode$ (raw_string As String)
|
||||
Dim final_string As String, hexchars As String
|
||||
Dim i, c
|
||||
For i = 1 To Len(raw_string)
|
||||
c = Asc(raw_string, i)
|
||||
If c = 37 Then '%
|
||||
hexchars = Mid$(raw_string, i + 1, 2)
|
||||
If Len(hexchars) = 2 And InStr("0123456789abcdefABCDEF", Left$(hexchars, 1)) > 0 And InStr("0123456789abcdefABCDEF", Right$(hexchars, 1)) > 0 Then
|
||||
final_string = final_string + Chr$(Val("&H" + hexchars))
|
||||
Else
|
||||
'String ends in something like "%1", or is invalid hex characters
|
||||
final_string = final_string + "%" + hexchars
|
||||
End If
|
||||
i = i + Len(hexchars)
|
||||
Else
|
||||
final_string = final_string + Chr$(c)
|
||||
End If
|
||||
Next i
|
||||
percent_decode = final_string
|
||||
End Function
|
||||
|
||||
|
||||
Function cannonicalise_path$ (raw_path As String)
|
||||
Dim path As String
|
||||
ReDim segments(1 To 1) As String
|
||||
Dim i, uplevels
|
||||
split raw_path, "/", segments()
|
||||
For i = UBound(segments) To 1 Step -1
|
||||
If segments(i) = "." Or segments(i) = "" Then
|
||||
_Continue
|
||||
ElseIf segments(i) = ".." Then
|
||||
uplevels = uplevels + 1
|
||||
Else
|
||||
If uplevels = 0 Then
|
||||
path = "/" + segments(i) + path
|
||||
Else
|
||||
uplevels = uplevels - 1
|
||||
End If
|
||||
End If
|
||||
Next i
|
||||
If path = "" Then path = "/"
|
||||
'Note: if uplevels > 0 at this point, the path attempted to go above the root
|
||||
'This is usually a client trying to be naughty
|
||||
cannonicalise_path = path
|
||||
End Function
|
||||
|
||||
'https://www.qb64.org/forum/index.php?topic=1607.0
|
||||
Sub split (SplitMeString As String, delim As String, loadMeArray() As String)
|
||||
Dim curpos As Long, arrpos As Long, LD As Long, dpos As Long 'fix use the Lbound the array already has
|
||||
curpos = 1: arrpos = LBound(loadMeArray): LD = Len(delim)
|
||||
dpos = InStr(curpos, SplitMeString, delim)
|
||||
Do Until dpos = 0
|
||||
loadMeArray(arrpos) = Mid$(SplitMeString, curpos, dpos - curpos)
|
||||
arrpos = arrpos + 1
|
||||
If arrpos > UBound(loadMeArray) Then ReDim _Preserve loadMeArray(LBound(loadMeArray) To UBound(loadMeArray) + 1000) As String
|
||||
curpos = dpos + LD
|
||||
dpos = InStr(curpos, SplitMeString, delim)
|
||||
Loop
|
||||
loadMeArray(arrpos) = Mid$(SplitMeString, curpos)
|
||||
ReDim _Preserve loadMeArray(LBound(loadMeArray) To arrpos) As String 'get the ubound correct
|
||||
End Sub
|
||||
|
||||
|
||||
Sub process_request (c)
|
||||
Dim eol
|
||||
Dim l As String
|
||||
Do
|
||||
eol = InStr(Connections(c).read_buf, CRLF)
|
||||
If eol = 0 Then Exit Sub
|
||||
l = Left$(Connections(c).read_buf, eol - 1)
|
||||
Connections(c).read_buf = Mid$(Connections(c).read_buf, eol + 2)
|
||||
If Connections(c).http_version = 0 Then 'First line not yet read
|
||||
process_start_line c, l
|
||||
Else
|
||||
If l = "" Then
|
||||
'headers complete; act upon request now
|
||||
Select Case Connections(c).method
|
||||
Case HTTP_GET
|
||||
http_do_get c
|
||||
Case HTTP_POST
|
||||
http_do_post c
|
||||
Case HTTP_HEAD
|
||||
http_do_head c
|
||||
End Select
|
||||
Exit Sub
|
||||
Else
|
||||
process_header c, l
|
||||
End If
|
||||
End If
|
||||
Loop
|
||||
End Sub
|
||||
|
||||
Sub process_start_line (c, l As String)
|
||||
'7230 3.1.1
|
||||
'METHOD uri HTTP/x.y
|
||||
Dim sp1, sp2
|
||||
sp1 = InStr(l, " ")
|
||||
If sp1 = 0 Then http_error 400, "Bad Request", c
|
||||
|
||||
'7231 4.3
|
||||
Select Case Left$(l, sp1 - 1)
|
||||
Case "GET"
|
||||
Connections(c).method = HTTP_GET
|
||||
Case "HEAD"
|
||||
Connections(c).method = HTTP_HEAD
|
||||
Case "POST"
|
||||
Connections(c).method = HTTP_POST
|
||||
Case Else
|
||||
http_error 501, "Not Implemented", c
|
||||
End Select
|
||||
|
||||
sp2 = InStr(sp1 + 1, l, " ")
|
||||
If sp2 = 0 Or sp2 - sp1 = 1 Then http_error 400, "Bad Request", c
|
||||
Connections(c).request_uri = Mid$(l, sp1 + 1, sp2 - sp1 - 1)
|
||||
|
||||
'7230 2.6
|
||||
If Mid$(l, sp2 + 1, 5) <> "HTTP/" Then
|
||||
http_error 400, "Bad Request", c
|
||||
End If
|
||||
Select Case Mid$(l, sp2 + 6)
|
||||
Case "1.0"
|
||||
Connections(c).http_version = HTTP_10
|
||||
Case "1.1"
|
||||
Connections(c).http_version = HTTP_11
|
||||
Case Else
|
||||
http_error 505, "HTTP Version Not Supported", c
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
Sub process_header (c, l As String)
|
||||
'All headers ignored for now
|
||||
End Sub
|
||||
|
||||
Sub http_error (code, message As String, connection)
|
||||
http_send_status connection, code, message
|
||||
http_send_header connection, "Content-Length", "0"
|
||||
http_send_header connection, "Connection", "close"
|
||||
http_end_headers connection
|
||||
close_connection connection
|
||||
Http_error_info.code = code
|
||||
Http_error_info.message = message
|
||||
Http_error_info.connection = connection
|
||||
Error 100
|
||||
End Sub
|
2
util/shorty.min.js
vendored
Normal file
2
util/shorty.min.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/* shorty.js - by enki - https://enkimute.github.io */
|
||||
!function(t,i,e){"undefined"!=typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define(t,e):i[t]=e()}("Shorty",this,function(){function t(t){this.tokensize=t||10,this.reset(!0)}return t.prototype.reset=function(t){t===!0&&(this.nodes=[{up:0,weight:0}],this.nyt=0,this.nodecount=0),this.data="",this.curpos=0,this.bitCount=7,this.bitChar=0},t.prototype.findNode=function(t){for(var i=this.nodes.length-1;i>0;i--)if("undefined"!=typeof this.nodes[i].symbol&&this.nodes[i].symbol==t)return i;return 0},t.prototype.addNode=function(t){return this.nodecount>=2046?0:(this.nodes[++this.nodecount]={up:this.nyt,symbol:t,weight:1},this.nodes[++this.nodecount]={up:this.nyt,weight:0},this.nodes[this.nyt].weight+=1,this.nyt=this.nodecount,this.nodes[this.nodecount-2].up!=this.nodecount-2&&this.balanceNode(this.nodes[this.nodecount-2].up),this.nodecount-2)},t.prototype.swapNode=function(t,i){var e=this.nodes[t].symbol,s=this.nodes[i].symbol,o=this.nodes[t].weight;this.nodes[t].symbol=s,this.nodes[i].symbol=e,this.nodes[t].weight=this.nodes[i].weight,this.nodes[i].weight=o;for(var h=this.nodes.length-1;h>0;h--)this.nodes[h].up==t?this.nodes[h].up=i:this.nodes[h].up==i&&(this.nodes[h].up=t)},t.prototype.balanceNode=function(t){for(;;){for(var i=t,e=this.nodes[t].weight;i>1&&this.nodes[i-1].weight==e;)i--;if(i!=t&&i!=this.nodes[t].up&&(this.swapNode(i,t),t=i),this.nodes[t].weight++,this.nodes[t].up==t)return;t=this.nodes[t].up}},t.prototype.emitNode=function(t){for(var i=[];0!=t;)i.unshift(t%2),t=this.nodes[t].up;for(var e=0;e<i.length;e++)this.emitBit(i[e])},t.prototype.emitNyt=function(t){this.emitNode(this.nyt);var i=t.length-1;this.tokensize>8&&this.emitBit(8&i),this.tokensize>4&&this.emitBit(4&i),this.tokensize>2&&this.emitBit(2&i),this.tokensize>1&&this.emitBit(1&i);for(var e=0;e<t.length;e++)this.emitByte(t.charCodeAt(e));return this.nyt},t.prototype.readNode=function(){if(0==this.nyt){for(var t=(this.tokensize>8?8*this.readBit():0)+(this.tokensize>4?4*this.readBit():0)+(this.tokensize>2?2*this.readBit():0)+(this.tokensize>1?this.readBit():0)+1,i="";t--;)i+=this.readByte();return i}for(var e=0;;){var s=this.readBit();if(void 0==this.nodes[e].symbol)for(var o=0;;o++)if(this.nodes[o].up==e&&o!=e&&o%2==s){e=o;break}if(void 0!=this.nodes[e].symbol||0==this.nodes[e].weight){if(this.nodes[e].weight)return this.nodes[e].symbol;for(var t=(this.tokensize>8?8*this.readBit():0)+(this.tokensize>4?4*this.readBit():0)+(this.tokensize>2?2*this.readBit():0)+(this.tokensize>1?this.readBit():0)+1,i="";t--;)i+=this.readByte();return i}}},t.prototype.emitBit=function(t){t&&(this.bitChar+=1<<this.bitCount),--this.bitCount<0&&(this.data+=String.fromCharCode(this.bitChar),this.bitCount=7,this.bitChar=0)},t.prototype.emitByte=function(t){for(var i=7;i>=0;i--)this.emitBit(t>>i&1)},t.prototype.readBit=function(){if(this.curpos==8*this.data.length)throw"done";var t=this.data.charCodeAt(this.curpos>>3)>>(7-this.curpos&7)&1;return this.curpos++,t},t.prototype.readByte=function(){res=0;for(var t=0;8>t;t++)res+=(128>>t)*this.readBit();return String.fromCharCode(res)},t.prototype.deflate=function(t){var i,e,s,o=t.length;for(this.reset(),e=0;o>e;e++){if(i=t[e],this.tokensize>1)if(/[a-zA-Z]/.test(i))for(;o>e+1&&i.length<this.tokensize&&/[a-zA-Z]/.test(t[e+1]);)i+=t[++e];else if(/[=\[\],\.:\"'\{\}]/.test(i))for(;o>e+1&&i.length<this.tokensize&&/[=\[\],\.:\"'\{\}]/.test(t[e+1]);)e++,i+=t[e];s=this.findNode(i),s?(this.emitNode(s),this.balanceNode(s)):(this.emitNyt(i),s=this.addNode(i))}if(7!=this.bitCount){var h=this.data.length;this.emitNode(this.nyt),h==this.data.length&&this.emitByte(0)}return this.data},t.prototype.inflate=function(t){this.reset(),this.data=t;var i="";try{for(var e=0;e>=0;e++){var s=this.readNode();i+=s;var o=this.findNode(s);o?this.balanceNode(o):this.addNode(s)}}catch(h){}return i},t});
|
Loading…
Reference in a new issue