(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'), regexpAmp = new RegExp('&', 'g'), regexpLt = new RegExp('<', 'g'), regexpGt = new RegExp('>', 'g'); // 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('>'); }; var lang = 'en'; // this is overridden in the boot sequence below, if the browser uses a supported language var eventListeners = {}, // keyed by event name, value is an array of listeners asyncTimer = null, queuedEvents = [], fireEvents = function () { if (asyncTimer !== null) return; asyncTimer = setTimeout(function () { var N = queuedEvents.length, i, event, args, listeners, j; asyncTimer = null; for (i = 0; i < N; i++) { event = queuedEvents[i]; listeners = eventListeners[event.name]; if (!listeners) continue; args = event.args; listeners = listeners.slice(); // make a copy of the list, so we're unaffected of any changes for (j = 0; j < listeners.length; j++) { try { listeners[j](args); } catch (e) { codeq.log.error('Error while invoking an event handler for ' + event.name + ': ' + e, e); } } } queuedEvents.splice(0, N); if (queuedEvents.length > 0) fireEvents(); }, 0); }; window.codeq = { 'jsonize': jsonize, 'log': log, 'availableLangs': [], // filled at boot from 'supportedLangs' 'supportedLangs': { 'en': 'English', 'sl': 'Slovenščina' }, 'getLang': function () { return lang; }, 'setLang': function (newLang) { lang = newLang; codeq.fire('langchange', {'lang': newLang}); }, 'chooseTranslation': function (translations, language) { var tr, lang; if ((typeof translations !== 'object') || (translations === null)) return {}; tr = translations[language || codeq.lang]; if ((typeof tr === 'object') && (tr !== null)) return tr; // default fallback tr = translations['en']; if ((typeof tr === 'object') && (tr !== null)) return tr; // fallback to whatever is available for (lang in translations) { tr = translations[lang]; if ((typeof tr === 'object') && (tr !== null)) return tr; } // all options were exhausted, we have nothing return {}; }, 'escapeHtml': function (s) { return ('' + s).replace(regexpAmp, '&').replace(regexpLt, '<').replace(regexpGt, '>'); }, /** * 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); }; }, // codeq event handling 'fire': function (eventName, args) { queuedEvents.push({'name': eventName, 'args': args}); fireEvents(); }, 'on': function (eventName, callback) { var listeners = eventListeners[eventName], i; if (listeners) { for (i = listeners.length - 1; i >= 0; i--) { if (listeners[i] === callback) return; // already registered } } else { listeners = []; eventListeners[eventName] = listeners; } listeners.push(callback); }, 'off': function (eventName, callback) { var listeners = eventListeners[eventName], i; if (listeners) { for (i = listeners.length - 1; i >= 0; i--) { if (listeners[i] === callback) listeners.splice(i, 1); } } } }; // ================================================================================ // The boot sequence // ================================================================================ $(document).ready(function () { // set the language var navigatorLang = navigator.language || navigator.browserLanguage, // language reported by browser lang = null, // the translation language that will be chosen key; if (typeof navigatorLang === 'string') { navigatorLang = navigatorLang.split('-')[0]; // truncate the language variant, in eg. en-US } else navigatorLang = 'en'; for (key in codeq.supportedLangs) { if (!codeq.supportedLangs.hasOwnProperty(key)) continue; if (key === navigatorLang) lang = key; // we support the browser's language codeq.availableLangs.push(key); } codeq.fire('init'); // tell any interested modules that we are not initialized, perhaps they want to initialize too codeq.setLang(lang || 'en'); // initial language setting // go to login codeq.globalStateMachine.transition('login'); //For performance reasons, the Tooltip and Popover data-apis are opt-in, meaning you must initialize them yourself. $('[data-toggle="popover"]').popover()}); })();