summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html2
-rw-r--r--js/codeq/core.js298
2 files changed, 267 insertions, 33 deletions
diff --git a/index.html b/index.html
index bde01cb..ec9641f 100644
--- a/index.html
+++ b/index.html
@@ -204,7 +204,7 @@
<script src="js/codemirror/python.js"></script>
<script src="js/codemirror/show-hint.js"></script>
<!-- codeq app -->
- <script src="js/codeq.js"></script>
+ <script src="js/codeq/core.js"></script>
<script src="js/codeq/navigation.js"></script>
<script src="js/codeq/comms.js"></script>
<script src="js/codeq/console.js"></script>
diff --git a/js/codeq/core.js b/js/codeq/core.js
index dd417d4..564aec3 100644
--- a/js/codeq/core.js
+++ b/js/codeq/core.js
@@ -1,33 +1,267 @@
-window.siteDefinition = { logLevel: 'debug' }; // for debug purposes
-
-window.codeq = {
- /**
- * XML namespaces.
- */
- ns: {
- svg: 'http://www.w3.org/2000/svg'
- },
-
- noOnlineStatus: !('onLine' in navigator),
-
- /**
- * REST API URL prefix.
- */
- urlPrefix: '/svc/',
-
- /**
- * Returns the number of Unicode code points in the given string.
- *
- * @param s {string}
- * @returns {number}
- */
- codePointCount: function (s) {
- var n = 0, i, code;
- for (i = s.length - 1; i >= 0; i--) {
- code = s.charCodeAt(i);
- if ((code >= 0xd800) && (code < 0xe000)) i++;
- n++;
+(function () {
+ window.siteDefinition = {logLevel: 'debug'}; // for debug purposes
+
+ // ================================================================================
+ // JSON-ization (stringification) function
+ // ================================================================================
+
+ var jsonize = JSON && JSON.stringify || function () {
+ 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);
+ };
+
+ // ================================================================================
+ // The log module: contains methods for logging, sending logs to the server
+ // ================================================================================
+
+ var log = (function () {
+ var logServiceUrl = window.siteDefinition && (typeof siteDefinition.logService === 'string') && siteDefinition.logService,
+ assembleOutput = function (stuff, e) {
+ var lines = [ stuff ];
+ if (e && e.stack) lines.push(e.stack);
+ return lines.join('\n');
+ },
+ console = window.console || {},
+ levelPrefix = {'debug': 'DEBUG: ', 'info': 'INFO: ', 'warn': 'WARN: ', 'error': 'ERROR: '},
+ levelSeverity = {'debug': 1, 'info': 2, 'warn': 3, 'error': 4, 'off': 5},
+ severityThreshold = window.siteDefinition && levelSeverity[siteDefinition.logLevel] || levelSeverity['off'],
+ logToConsole = function (level) {
+ var prefix = levelPrefix[level];
+ if (levelSeverity[level] < severityThreshold) return function () {}; // level is below threshold, ignore
+ if (typeof console[level] === 'function') return function (output) {console[level](output);};
+ if (typeof console.log === 'function') return function (output) {console.log(prefix + output);};
+ if (typeof dump === 'function') return function (output) {dump(prefix + output);};
+ return function () {}; // no way to log in browser
+ },
+ consoleLogger = {
+ 'debug': logToConsole('debug'),
+ 'info': logToConsole('info'),
+ 'warn': logToConsole('warn'),
+ 'error': logToConsole('error')
+ },
+ logger = {},
+ level;
+
+ if (logServiceUrl) {
+ // log to the service and to the console
+ var logs = []; // buffered logs
+ for (level in levelPrefix) {
+ if (!levelPrefix.hasOwnProperty(level)) continue;
+ logger[level] = (function (levelLogger, level) {
+ if (levelSeverity[level] < severityThreshold) return function () {}; // level is below threshold, ignore
+ return function (stuff, e) {
+ var output = assembleOutput(stuff, e);
+ levelLogger(output);
+ logs.push({
+ 't': Date.now(),
+ 'l': level,
+ 'm': output
+ });
+ };
+ })(consoleLogger[level], level);
+ }
+ setInterval(function () {
+ // each second send any logs to the server
+ var copyOfLogs;
+ if (logs.length < 1) return;
+ copyOfLogs = jsonize({'logs': logs});
+ logs = [];
+ $.ajax({
+ contentType: 'application/json',
+ dataType: 'application/json',
+ type: 'POST',
+ url: logServiceUrl,
+ data: copyOfLogs,
+ error: function (jqXHR, textStatus, errorThrown) {
+ consoleLogger.error(assembleOutput('Posting of logs to ' + logServiceUrl + ' failed: ' + textStatus, errorThrown));
+ },
+ success: function (data, textStatus, jqXHR) {}
+ });
+ }, 1000);
+ }
+ else {
+ // log only to the console
+ for (level in levelPrefix) {
+ if (!levelPrefix.hasOwnProperty(level)) continue;
+ logger[level] = (function (levelLogger) {
+ if (levelSeverity[level] < severityThreshold) return function () {}; // level is below threshold, ignore
+ return function (stuff, e) { levelLogger(assembleOutput(stuff, e)); };
+ })(consoleLogger[level]);
+ }
+ }
+
+ return logger;
+ })();
+
+ // ================================================================================
+ // The core CodeQ module
+ // ================================================================================
+
+ var jqDisabled = $('#disabled'), // used in codeq.wait()
+ waitCssEnter = {'cursor': 'wait', 'display': ''};
+
+ // regular expressions for the templating function, 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'),
+ regexpWhiteSpace = new RegExp('[ \\r\\n\\t]+'),
+ regexpWhiteSpaceTrim = new RegExp('^[ \\t\\r\\n]*(.*[^ \\t\\r\\n])[ \\t\\r\\n]*$', 'm');
+
+ // convert a string into its definition (javascript literal)
+ 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('>');
+ };
+
+ window.codeq = {
+ 'jsonize': jsonize,
+
+ 'log': log,
+
+ /**
+ * Returns the number of Unicode code points in the given string.
+ *
+ * @param s {string}
+ * @returns {number}
+ */
+ 'codePointCount': function (s) {
+ var n = 0, i, code;
+ if (typeof s !== 'string') {
+ code = 'codePointCount(): argument not a string: type = ' + typeof s + ', is null = ' + (s === null);
+ if ((typeof s === 'object') && (s !== null) && s.constructor) code += ', constructor = ' + s.constructor.name;
+ codeq.log.error(code);
+ return 0;
+ }
+ for (i = s.length - 1; i >= 0; i--) {
+ try {
+ code = s.charCodeAt(i);
+ }
+ catch (e) {
+ codeq.log.error('Invocation of charCodeAt() failed at iteration #' + i + ': ' + e, e);
+ return 0;
+ }
+ if ((code >= 0xd800) && (code < 0xe000)) i++;
+ n++;
+ }
+ return n;
+ },
+
+ 'wait': function (promise) {
+ jqDisabled.css(waitCssEnter);
+ return promise.fin(function () {
+ jqDisabled.css('display', 'none');
+ });
+ },
+
+ 'templator': function (str, templateName) {
+ var f, parts, i, subparts, subpart,
+ src = [ 'var _result = [], echo = function (s) { _result.push(s); };\n' ],
+ componentName, atoms, j, atom, debugPrefix, s, r;
+
+ if (!templateName) templateName = 'unknown template';
+ debugPrefix = '[' + templateName + ']';
+
+ // remove comments
+ parts = str.split('[%--'); // break on start-of-comment
+ atoms = [ parts[0] ]; // first part is not a comment
+ for (i = 1; i < parts.length; i++) { // iterate over start-of-comments
+ atom = parts[i].split('--%]'); // break on end-of-comment
+ if (atom.length > 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
+ s = subpart.slice(1);
+ r = regexpWhiteSpaceTrim.exec(s);
+ src.push('_result.push(this.', r && r[1] || s, ');\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(src.join('')); // create a function based on the given template
+ }
+ catch (e) {
+ codeq.log.error('createTemplate(): ' + debugPrefix + ' Failed to instantiate template function: ' + e + '\nfunction() {\n' + src.join('') + '\n}\n=== Created from template: ===\n' + str, e);
+ throw e;
+ }
+
+ return function (args) {
+ if ((typeof args !== 'object') || (args === null)) args = {};
+ return f.apply(args);
+ };
}
- return n;
- }
-};
+ };
+})();