From 5f8015714f94a7d1785cf148c257d3be72d1e6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Mon, 13 Jul 2015 17:56:49 +0200 Subject: Initial commit: bare page for Prolog tasks with editor and command prompt, lightweight task definition parser of pythonic assignment statements. --- js/codeq.js | 742 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 js/codeq.js (limited to 'js/codeq.js') diff --git a/js/codeq.js b/js/codeq.js new file mode 100644 index 0000000..3a30823 --- /dev/null +++ b/js/codeq.js @@ -0,0 +1,742 @@ +// introduce the namespace object for codeq +window.codeq = {}; +window.siteDefinition = { logLevel: 'debug' }; // for debug purposes + +(function () { + + // regular expressions for the templating function, the logging system, etc. + var regexpQuote = new RegExp('"', 'g'), + regexpBackslash = new RegExp('\\\\', 'g'), + regexpWhiteSpaceStart = new RegExp('^[ \r\n\t]+'), + regexpWhiteSpaceEnd = new RegExp('[ \r\n\t]+$'), + regexpWhiteSpaceNonPrintable = new RegExp('[\r\n\t]', 'g'), + regexpWhiteSpaceBeforeTag = new RegExp('[ \r\n\t]+(?=<)', 'g'), + regexpWhiteSpaceAfterTag = new RegExp('>[ \r\n\t]+', 'g'), + regexpIKeyMarker = new RegExp('(?:^|\\s)(ikey-marker.*)(?:$|\\s)'), + regexpWhiteSpace = new RegExp('[ \\r\\n\\t]+'); + + // ================================================================================ + // The log module: contains methods for logging, sending logs to the server + // ================================================================================ + + var jsonize; // JSONization function + + if (JSON && JSON.stringify) { + jsonize = JSON.stringify; + } + else { + jsonize = function (obj) { + var t, buffer, i, isFirst; + if (null === obj) return 'null'; + t = typeof obj; + if (t === 'string') { + return '"' + obj.replace(regexpBackslash, '\\\\').replace(regexpQuote, '\\"') + '"'; + } + if (t === 'number') { + if (isFinite(obj)) return obj.toString(); + throw new Error('Cannot jsonize a non-finite number: ' + obj.toString()); + } + if (t === 'boolean') { + if (obj) return 'true'; + return 'false'; + } + if (t === 'object') { + if (obj instanceof String) return jsonize(obj.valueOf()); + if (obj instanceof Number) return jsonize(obj.valueOf()); + if (obj instanceof Boolean) return jsonize(obj.valueOf()); + if (obj instanceof Array) { + buffer = [ '[' ]; + isFirst = true; + for (i = 0; i < obj.length; i++) { + if (isFirst) isFirst = false; + else buffer.push(','); + buffer.push(jsonize(obj[i])); + } + buffer.push(']'); + return buffer.join(''); + } + buffer = [ '{' ]; + isFirst = true; + for (i in obj) { + if (isFirst) isFirst = false; + else buffer.push(','); + buffer.push(jsonize(i), ':', jsonize(obj[i])); + } + buffer.push('}'); + return buffer.join(''); + } + throw new Error('Cannot jsonize ' + t); + }; + } + + codeq.log = {}; + (function () { + var assembleOutput = function (stuff, e) { + var lines = [ stuff ]; + if (e && e.stack) lines.push(e.stack); + return lines.join('\n'); + }; + + if (window.siteDefinition && window.siteDefinition.logService) { + var url = window.siteDefinition.logService, + logs = [], + storeLog = function (level, stuff, e) { + logs.push({ + 't': Date.now(), + 'l': level, + 'm': assembleOutput(stuff, e) + }); + }; + if (window.siteDefinition && (window.siteDefinition.logLevel == 'debug')) { + codeq.log.debug = function (stuff, e) { storeLog('debug', stuff, e); }; + } + else codeq.log.debug = function () {}; + + if (window.siteDefinition && ((window.siteDefinition.logLevel == 'info') || (window.siteDefinition.logLevel == 'debug'))) { + codeq.log.info = function (stuff, e) { storeLog('info', stuff, e); }; + } + else codeq.log.info = function () {}; + + codeq.log.error = function (stuff, e) { storeLog('error', stuff, e); }; + + setInterval(function () { + var copyOfLogs; + if (logs.length < 1) return; + copyOfLogs = jsonize({'logs': logs}); + logs = []; + $.ajax({ + contentType: 'application/json', + dataType: 'application/json', + type: 'POST', + url: url, + data: copyOfLogs, + error: function (jqXHR, textStatus, errorThrown) { + if (window.console && console.log) console.log(assembleOutput('Posting of logs to ' + url + ' failed: ' + textStatus, errorThrown)); + else dump(assembleOutput('Posting of logs to ' + url + ' failed: ' + textStatus, errorThrown)); + }, + success: function (data, textStatus, jqXHR) { + } + }); + }, 1000); + } + else if (window.console && console.log) { + if (window.siteDefinition && (window.siteDefinition.logLevel == 'debug')) { + codeq.log.debug = function (stuff, e) { console.log(assembleOutput('DEBUG: ' + stuff, e)); }; + } + else codeq.log.debug = function () {}; + + if (window.siteDefinition && ((window.siteDefinition.logLevel == 'info') || (window.siteDefinition.logLevel == 'debug'))) { + codeq.log.info = function (stuff, e) { console.log(assembleOutput('INFO: ' + stuff, e)); }; + } + else codeq.log.info = function () {}; + + codeq.log.error = function (stuff, e) { console.log(assembleOutput('ERROR: ' + stuff, e)); }; + } + else { + if (window.siteDefinition && (window.siteDefinition.logLevel == 'debug')) { + codeq.log.debug = function (stuff, e) { dump(assembleOutput('DEBUG: ' + stuff, e)); }; + } + else codeq.log.debug = function () {}; + + if (window.siteDefinition && ((window.siteDefinition.logLevel == 'info') || (window.siteDefinition.logLevel == 'debug'))) { + codeq.log.info = function (stuff, e) { dump(assembleOutput('INFO: ' + stuff, e)); }; + } + else codeq.log.info = function () {}; + + codeq.log.error = function (stuff, e) { dump(assembleOutput('ERROR: ' + stuff, e)); }; + } + })(); + + // ================================================================================ + // The system module: contains essential methods for the operation of the app + // ================================================================================ + + // -------------------------------------------------------------------------------- + // The templating part: the createTemplate() and its utility functions + // -------------------------------------------------------------------------------- + + // jQuery extension + jQuery.fn.makeUnselectable = function () { + this.attr("unselectable", "on").attr("draggable", "false"); + this.find("*").attr("unselectable", "on").attr("draggable", "false"); + return this; + }; + + var + emptyConstObject = {}; // for use as a default read-only parameter in various methods, so we don't instantiate empty objects for no reason + + // convert a string into its definition + var stringToDef = function (str) { + return str.replace(regexpBackslash, '\\\\').replace(regexpQuote, '\\"').replace(regexpWhiteSpaceNonPrintable, ' '); + }; + + // given a HTML source, remove whitespace among tags + var cleanHtml = function (html) { + // JavaScript doesn't support lookbehind, so we use the split-join trick + return html.replace(regexpWhiteSpaceBeforeTag, '').split(regexpWhiteSpaceAfterTag).join('>'); + }; + + var trailingBackslashCount = function (s) { + var n = 0, i = s.length - 1; + while ((i >= 0) && (s.charAt(i) === '\\')) { + n++; + i--; + } + return n; + }; + + /** + * Takes a string of "argName=argValue" arguments separated with a comma. + * Creates and returns an array of arguments, where each argument is + * represented as an object in the form { name: "argName", value: "argValue" }. + * Heading and trailing whitespace is auto-removed. + */ + var splitComponentArguments = function (s) { + var commaParts = s.split(','), + args = [], // array of arguments, an argument is an object with the properties name and value (atomL and atomR) + doubleParts, singleParts, i, atom, j, k, l, + doublePart, singlePart, doubleLastIndex, singleLastIndex, + equalsParts, atomL, atomR, + singleOpen = false, doubleOpen = false; + + atom = []; // substrings of the currently assembling atom + atomL = false; // the left-side atom + atomR = false; // the right-side atom, between them is an equals sign + for (i = 0; i < commaParts.length; i++) { + doubleParts = commaParts[i].split('"'); // first split after double quotes + doubleLastIndex = doubleParts.length - 1; + for (j = 0; j < doubleParts.length; j++) { // and process the parts + doublePart = doubleParts[j]; + singleParts = doublePart.split("'"); // split after single quotes + singleLastIndex = singleParts.length - 1; + for (k = 0; k < singleParts.length; k++) { // process the parts delimited by single quotes + if (atomL || singleOpen || doubleOpen) { // left side of the assignment was already found, or quotes are active + atom.push(singleParts[k]); // don't search for the equals sign + } + else { // we don't have the left side of the assignment yet and no quotes are open + equalsParts = singleParts[k].split('='); // search for the equals sign + atom.push(equalsParts[0]); + if (equalsParts.length > 1) { // equals sign was found: we have the left side of the assignment + atomL = jQuery.trim(atom.join('')); // store the left side of the assignment + if (atomL === '') throw new Error('splitComponentArguments(): left side of the argument at index ' + args.length + ' is empty: ' + s); + atom = []; // and reset the atom buffer + atom.push(equalsParts[1]); // save the rest into the atom buffer + for (l = 2; l < equalsParts.length; l++) { // there may be more than 1 equals sign, store them, too + atom.push('='); + atom.push(equalsParts[l]); + } + } + } + if (k < singleLastIndex) { // the last item does not have a quote attached + atom.push("'"); + if (!doubleOpen) { + if ((trailingBackslashCount(singleParts[k]) % 2) == 0) singleOpen = !singleOpen; + } + } + } + if (j < doubleLastIndex) { // the last item does not have a quote attached + atom.push('"'); + if (!singleOpen) { + if ((trailingBackslashCount(doublePart) % 2) == 0) doubleOpen = !doubleOpen; + } + } + } + if (singleOpen || doubleOpen) { // a quote is still open: include the comma + atom.push(','); + } + else { // no open quotes, and a comma or end-of-line encoutered: means end of an atom + if (!atomL) throw new Error('splitComponentArguments(): the argument at index ' + args.length + ' does not contain a parameter assignment: ' + s); + atomR = jQuery.trim(atom.join('')); + args.push({ 'name': atomL, 'value': atomR }); + atomL = false; + atomR = false; + atom = []; + } + } + + if (atomL || (atom.length > 0)) throw new Error('splitComponentArguments(): premature end of the last argument: ' + s); + + return args; + }; + + + var QuotesWalker = function (s, isLongQuotes) { + var doubleQuotes, singleQuotes, quoteLen, iteration = 0, + n = s.length, + startCharPos = 0, + unescapedIndexOf = function (s, quotes, startPos) { + var n = s.length, pos; + while (startPos < n) { + pos = s.indexOf(quotes, startPos); + if (pos <= 0) return pos; + if (s.charAt(pos-1) != '\\') return pos; + startPos = pos + 1; + } + return -1; + }; + + if (isLongQuotes) { + doubleQuotes= '"""'; + singleQuotes = "'''"; + quoteLen = 3; + } + else { + doubleQuotes= '"'; + singleQuotes = "'"; + quoteLen = 1; + } + + this.prefixString = ''; + this.quotedString = ''; + this.quoteType = ''; + + this.next = function () { + var doubleQuotePos, singleQuotePos, quotePos, quoteType; + iteration++; + if (startCharPos >= n) { + this.prefixString = ''; + this.quotedString = ''; + this.quoteType = ''; + codeq.log.debug("QuotesWalker #" + iteration + ': no more data, returning false'); + return false; + } + // with which quotes to start, single or double? + doubleQuotePos = unescapedIndexOf(s, doubleQuotes, startCharPos); + singleQuotePos = unescapedIndexOf(s, singleQuotes, startCharPos); + if (doubleQuotePos < 0) { + if (singleQuotePos < 0) { + // the end + this.prefixString = s.slice(startCharPos, n); + this.quotedString = ''; + this.quoteType = ''; + startCharPos = n; + codeq.log.debug("QuotesWalker #" + iteration + ': last data, remaining string: ' + this.prefixString); + return true; + } + quotePos = singleQuotePos; + quoteType = singleQuotes; + } + else if (singleQuotePos < 0) { + quotePos = doubleQuotePos; + quoteType = doubleQuotes; + } + else if (doubleQuotePos < singleQuotePos) { + quotePos = doubleQuotePos; + quoteType = doubleQuotes; + } + else { + quotePos = singleQuotePos; + quoteType = singleQuotes; + } + this.quoteType = quoteType; + + this.prefixString = s.slice(startCharPos, quotePos); + startCharPos = quotePos + quoteLen; + quotePos = unescapedIndexOf(s, quoteType, startCharPos); + this.quotedString = s.slice(startCharPos, quotePos); + startCharPos = quotePos + quoteLen; + codeq.log.debug('QuotesWalker #' + iteration + ': quoteType=' + this.quoteType + ', data:\nprefix=' + this.prefixString + '\nquoted=' + this.quotedString); + return true; + }; + }; + + var escapePythonicLongString = function (s, output) { + var pos = 0, + parts = s.split("'"), + n = parts.length, + i, part, previousPart; + output.push("'"); // starting quote + + previousPart = parts[0]; + output.push(previousPart.split('\r').join('\\r').split('\t').join('\\t').split('\n').join('\\n')); + for (i = 1; i < n; i++) { + part = parts[i]; + // first escape the single quote, if required + if (previousPart.charAt(previousPart.length - 1) != '\\') output.push('\\'); + // escape \r, \n, \t + output.push(part.split('\r').join('\\r').split('\t').join('\\t').split('\n').join('\\n')); + previousPart = part; + } + + output.push("'"); // ending quote + }; + + // the "engine" of codeq + codeq.system = { + // define XML namespaces, for use in generating HTML content + ns: { + svg: 'http://www.w3.org/2000/svg' + }, + + // the method for creating a templating function from the given string template + createTemplate: function (str, templateName, _internalDoLog_) { + var f, parts, i, subparts, subpart, + isSvg = str.substring(0, 4) === ' 1) { // if end-of-comment was encountered + atoms.push(atom[1]); // add whatever is trailing it + for (j = 2; j < atom.length; j++) { // re-add even dangling end-of-comments + atoms.push('--%]'); + atoms.push(atom[j]); + } + } + } + + // start processing + parts = atoms.join('').split('[%'); + + if (parts[0].length > 0) src.push('_result.push("', stringToDef(cleanHtml(parts[0])), '");\n'); // the first part that doesn't begin with '[%' + for (i = 1; i < parts.length; i++) { // for every part that begins with '[%' + if (parts[i].slice(0, 2) === '--') { // a comment start + subparts = parts[i].split('--%]'); // split at comment end + } + else { // a start of a statement or of a value reference + subparts = parts[i].split('%]'); // there should be only one terminating '%]', find it + subpart = subparts[0].replace(regexpWhiteSpaceStart, '').replace(regexpWhiteSpaceEnd, ''); // trim the white space + if (subpart.length > 0) { + if (subpart[0] === '=') { // a value reference + src.push('_result.push(', subpart.slice(1), ');\n'); + } + else if (subpart[0] === '@') { // a component directive, this gets processed in two phases + atoms = subpart.slice(1).replace(regexpWhiteSpaceStart, ''); // get the whole directive, remove any heading whitespace + componentName = atoms.split(regexpWhiteSpace, 1)[0]; // extract the component name + atoms = splitComponentArguments(atoms.slice(componentName.length)); // extract the atoms + src.push('_tmp={"name":"', stringToDef(componentName), '","params":{},"key":"image"+_counter};\n'); + src.push('_components_.push(_tmp);\n'); + src.push('_tmp=_tmp.params;\n'); + for (j = 0; j < atoms.length; j++) { + atom = atoms[j]; + src.push('_tmp["', stringToDef(atom.name), '"]=', atom.value, ';\n'); + } +// src.push('_result.push(\'<', nodeName, ' class="image\', _counter, \'">\');\n'); + src.push('_result.push(\'\');\n'); + src.push('_counter++;\n'); + } + else { // a statement + src.push(subpart, '\n'); + } + } + } + if ((subparts.length > 1) && (subparts[1].length > 0)) { // there's a trailing text + src.push('_result.push("', stringToDef(cleanHtml(subparts[1])), '");\n'); + } + } + src.push('return _result.join("");'); + if (_internalDoLog_) codeq.log.debug('createTemplate(): ' + debugPrefix + ' creating templating function:\nfunction(_components_) {\n' + src.join('') + '\n}\n=== Created from template: ===\n' + str); + try { + f = new Function('_components_', src.join('')); // create a function that takes the single argument named _components_ (an array) + } + catch (e) { + codeq.log.error('createTemplate(): ' + debugPrefix + ' Failed to instantiate template function: ' + e + '\nfunction(_components_) {\n' + src.join('') + '\n}\n=== Created from template: ===\n' + str, e); + throw e; + } + + return function () { // takes params, callback + var param, templateParams, callback, html, components = [], i, jq, jqContainer, processDom; + + // parse arguments + // first set defaults + templateParams = false; + callback = false; + jqContainer = false; + for (i = arguments.length - 1; i >= 0; i--) { + param = arguments[i]; + if (param instanceof Function) { + // set the optional callback that will be invoked with a jQuery argument representing the "compiled" template, after the template is all set up + if (callback) throw new Error(debugPrefix + ' More than one callback provided to a template function'); + callback = param; + } + else if (param instanceof jQuery) { + // set the container that will receive the template content, the container is mandatory for SVG + if (jqContainer) throw new Error(debugPrefix + ' More than one jQuery container provided to a template function'); + jqContainer = param; + } + else if (param instanceof Object) { // BEWARE: this one catches all Objects, so do testing for descendants (e.g.: Function, jQuery) before testing for Object! + // set the parameters to the template function, they are optional + if (templateParams) throw new Error(debugPrefix + ' More than one object with template parameters provided to a template function'); + templateParams = param; + } + else throw new Error(debugPrefix + ' Unknown parameter provided to a template function: ' + typeof param); + } + // we need at least an empty object for the template function + if (!templateParams) templateParams = emptyConstObject; + // the container is mandatory for SVG + //if (isSvg && !jqContainer) throw new Error(debugPrefix + ' jQuery object is a mandatory parameter to the template function when the template is a SVG object'); + + // process DOM, after the template is instantiated + processDom = function () { + var componentDef, i, finishedIteration, finishComponent, cjq, params, key, variant, variantsCache, + iterationCount, currentIteration, newJq; + + jq.data('templateName', templateName); // for debugging + + iterationCount = 1 + components.length; // first one is the sentinel + currentIteration = 0; // how many iterations were processed + finishedIteration = function () { + currentIteration++; + if (iterationCount === currentIteration) { + jq.makeUnselectable(); + if (callback) callback(jq, templateParams); + } + }; + + finishComponent = function (cjq, classes, params, reservedParams) { + var attrName; + if (params['class']) classes.push(params['class']); + cjq.attr('class', classes.join(' ')); + for (attrName in params) { + if (attrName === 'class') continue; // a fixed reserved parameter + if (attrName in reservedParams) continue; // reserved parameters as specified by the corresponding handler + cjq.attr(attrName, params[attrName]); // set the attribute + } + finishedIteration(); + }; + + for (i = 0; i < components.length; i++) { + componentDef = components[i]; + params = componentDef.params; + cjq = jq.find('.' + componentDef.key); + if (cjq.length == 0) { + if (jq.hasClass(componentDef.key)) cjq = jq; + } + if (cjq.length == 0) { + codeq.log.error('codeq.system.createTemplate::fn(): internal error: could not obtain reference to a template component of type ' + componentDef.name + ', key=' + componentDef.key); + continue; + } + if (_internalDoLog_) codeq.log.debug('codeq.system.createTemplate::fn(): ' + debugPrefix + ' searching for a component with class ' + componentDef.key + ', found ' + cjq.length); + isSvg = 'ownerSVGElement' in cjq[0]; + switch (componentDef.name) { + case 'text': + key = params.key; + if (!key) { + codeq.log.error('codeq.system.createTemplate::fn(): ' + debugPrefix + ' A text component is missing the key'); + finishedIteration(); + } + else { + if (isSvg) newJq = $(document.createElementNS(codeq.ns.svg, 'tspan')); + else newJq = $(''); + cjq.replaceWith(newJq); + if (jq === cjq) jq = newJq; + cjq = newJq; + cjq.attr('data-tkey', key); + translate(cjq); + if (_internalDoLog_) codeq.log.debug('codeq.system.createTemplate::fn(): ' + debugPrefix + ' Instantiated intellitext: key=' + key); + finishComponent(cjq, ['intellitext'], params, {'key':true}); + } + break; + case 'image': + key = params.key; + if (isSvg) { + codeq.log.error('codeq.system.createTemplate::fn(): ' + debugPrefix + ' Currently there is no support for inlining images into SVG'); + finishedIteration(); + } + else if (!key) { + codeq.log.error('codeq.system.createTemplate::fn(): ' + debugPrefix + ' An image component is missing the key'); + finishedIteration(); + } + else { + newJq = $(''); + cjq.replaceWith(newJq); + if (jq === cjq) jq = newJq; + cjq = newJq; + variant = params.variant || 'normal'; + cjq.attr('data-ikey', key); // HTML5 data + cjq.attr('data-ivariant', variant); + cjq.data['ikey'] = key; // jQuery data + cjq.data['ivariant'] = variant; + cjq.data['ivariants'] = variantsCache = {}; + setButtonVariant(cjq, variantsCache, key, variant, (function (cjq, params, variant) { + return function () { + if (_internalDoLog_) codeq.log.debug('codeq.system.createTemplate::fn(): ' + debugPrefix + ' Instantiated image: key=' + params.key + ', variant=' + variant); + finishComponent(cjq, ['intellimage'], params, {'key':true, 'variant':true}); + }; + })(cjq, params, variant)); + } + break; + default: + codeq.log.error('codeq.system.createTemplate::fn(): ' + debugPrefix + ' Invalid component name: ' + componentDef.name); + finishedIteration(); + break; + } + } + finishedIteration(); // the sentinel + }; + + // create DOM + + try { + html = f.apply(templateParams, [components]); + if (_internalDoLog_) { + codeq.log.debug('createTemplate(): ' + debugPrefix + ' instantiating template:\n' + html); + } + } + catch (e) { + codeq.log.error('createTemplate(): ' + debugPrefix + ' Failed to invoke template function: ' + e + '\nfunction(_components_) {\n' + src.join('') + '\n}\n=== Created from template: ===\n' + str, e); + throw e; + } + + jq = $(html); + if (jqContainer) jqContainer.append(jq); + processDom(); + + // TODO: what follows is the code that uses SVG jQuery plugin to instantiate SVG DOM, but the created images are not always correctly sized; needs analysis of what is happening + /* if (isSvg) { + jqContainer.empty().svg({ + 'loadURL': html, + 'initPath': '/js/svg/', // where to load blank.svg from if needed + 'changeSize': true, // get the size from the SVG markup, don't retain the existing size of the container + 'onLoad': function (jqSvg) { + // it should hold that jqContainer == jqSvg, but we ignore the parameter anyway + jq = $(jqContainer.children()[0]); + processDom(); + } + }); + } + else { + jq = $(html); + if (jqContainer) jqContainer.append(jq); + processDom(); + }*/ + }; + }, // createTemplate + + // -------------------------------------------------------------------------------- + // Task info parser: converts simplified pythonic syntax to a JavaScript function + // -------------------------------------------------------------------------------- + + parseInfo: function (infoText) { + var parts = [], + n, lines, line, i, j, len, walker, fn, obj; + // convert pythonic long-strings to ordinary strings, escaping things as we go + walker = new QuotesWalker(infoText, true); + while (walker.next()) { + if (walker.prefixString.length > 0) parts.push(walker.prefixString); + if (walker.quotedString.length > 0) escapePythonicLongString(walker.quotedString, parts); + } + + // split into separate lines, remove comments, add semicolons + lines = parts.join('').split('\n'); // split at line feed characters + n = lines.length; + for (i = 0; i < n; i++) { + line = lines[i]; + len = line.length; + if (len > 0) { + // if exists: find the first python's comment character (#) that is not in a string (between two quotes) + parts = []; + walker = new QuotesWalker(line, false); + while (walker.next()) { + if (walker.prefixString.length > 0) { + j = walker.prefixString.indexOf('#'); + if (j >= 0) { + parts.push(walker.prefixString.slice(0, j)); + break; // commented out till the EOL + } + parts.push(walker.prefixString); + } + parts.push(walker.quoteType); + parts.push(walker.quotedString); + parts.push(walker.quoteType); + } + + line = parts.join('').replace(regexpWhiteSpaceEnd, ''); // trim the white space at the end + if ((line.length > 0) && (line[line.length - 1] != ';')) line = line + ';'; // and add a semicolon + lines[i] = line; + } + } + + // compose the function + lines.unshift("var description, hint;"); + lines.push("__params__.description = description;", "__params__.hint = hint;"); + codeq.log.debug("Creating a new parseInfo function having the body: "); + codeq.log.debug(lines); + fn = new Function("__params__", lines.join('\n')); + obj = {}; + fn(obj); + return obj; // obj now contains "description" and "hint" + }, + + load: function (request) { + $.ajax({ + contentType: request.contentType, + dataType: request.type, + type: request.data ? 'POST' : 'GET', + url: request.url + '?_=' + Date.now(), + data: request.data, + error: function (jqXHR, textStatus, errorThrown) { + codeq.log.error('Loading of ' + request.url + ' failed: ' + textStatus, errorThrown); + try { + if (request.callback) request.callback(null, 'Error: ' + (errorThrown ? '' + errorThrown : textStatus), request.url); + } + catch (e) { + codeq.log.error('Callback with error failed on request ' + request.url + ': ' + e, e); + } + }, + success: function (data, textStatus, jqXHR) { + try { + if (request.callback) request.callback(data, 'OK', request.url); + } + catch (e) { + codeq.log.error('Callback failed on successful request ' + request.url + ': ' + e, e); + } + } + }); + } + + }; // codeq.system = { + + window.start = function () { +// var s = location.hash; +// if (s.length == 0) return; // empty hash +// if (s.charAt(0) == '#') s = s.substring(1); +// if (s.length == 0) return; // empty hash + var editor = CodeMirror.fromTextArea(document.getElementById('program'), { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }); + editor.setValue('sister(X, Y) :-\n female(X),\n parent(Z, X),\n parent(Z, Y),\n X \\== Y.'); + +/* $('#console').terminal(function (command, term) { + term.echo('Not implemented.'); + }, { + prompt: '?- ', + history: false, + greetings: 'Prolog terminal', + outputLimit: 7, + exit: false, + name: 'prolog_cmd', + width: '100%' + });*/ + + var controller = $('#console').console({ + promptLabel: '?- ', + commandValidate: function (line) { + return !!line; + }, + commandHandle: function (line) { + return [{msg:'Not implemented.', className:'console-response'}]; + }, + autofocus: false, + animateScroll: false, + promptHistory: false, + welcomeMessage: 'Prolog REPL.' + }); + + codeq.system.load({ + type: 'text', + url: 'sister.py', + callback: function (data, status, url) { + if (!data) return; + var info = codeq.system.parseInfo(data); + $('#description').html(info.description); + } + }); + }; +})(); -- cgit v1.2.1