(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); }; } }; // ================================================================================ // The boot sequence // ================================================================================ $(document).ready(function () { codeq.globalStateMachine.transition('login'); }); })();