/* CodeQ: an online programming tutor. Copyright (C) 2016 UL FRI This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ (function () { "use strict"; // resource tree, loaded from data/resources.json in the boot sequence var resources = {}; var resolveResource = function (resourceName, resourceBranches) { var traversedPath = ['data'], // top-level directory branch = resources, candidate = null, i, fragment; if (!resourceName) { codeq.log.error('No resource name provided; ' + 'path: "' + resourceBranches.join('/') + '"'); return null; } if (branch[resourceName]) candidate = 'data/' + resourceName; // top-level match for (i = 0; i < resourceBranches.length; i++) { fragment = resourceBranches[i]; branch = branch[fragment]; if (!branch) { codeq.log.error('Resource sub-branch ' + fragment + ' does not exist; ' + 'resource: "' + resourceName + '", ' + 'path: "' + resourceBranches.join('/') + '"'); break; } traversedPath.push(fragment); if (branch[resourceName]) { candidate = traversedPath.join('/') + '/' + resourceName; } } if (candidate) return codeq.ajaxPrefix + candidate; codeq.log.error('Resource ' + resourceName + ' was not found; ' + 'path: "' + resourceBranches.join('/') + '"'); return null; }; // parse a key-value string of the form: a=1 b='2 + 3' c="4" var parseKeyval = function (str) { // match foo=bar, foo="bar", foo = 'bar baz' etc. var regex = /\s*([^=\s]*)\s*=\s*([^'"]\S*|"[^"]*"|'[^']*')/g, dict = {}, match, key, val; while ((match = regex.exec(str)) !== null) { key = match[1]; val = match[2]; if (val[0] == val[val.length-1] && (val[0] === '"' || val[0] === "'")) { val = val.slice(1, -1); } dict[key] = val; } return dict; }; var directiveHandlers = { 'resource': function (str, templatePath) { return resolveResource(str, templatePath) || 'data/broken.png'; }, 'img': function (str, templatePath) { var attrs = parseKeyval(str), output, key; output = '' + codeq.escapeHtml(value[i]) + ''; } } else { output += codeq.escapeHtml(value); } break; case '@': // a directive handler = directiveHandlers[match[2]]; if (handler) { output += handler(match[3].trim(), templatePath); } else { codeq.log.error('Invalid directive in ' + templateName + ': ' + match[2]); } break; default: codeq.log.error('Invalid template in ' + templateName + ': ' + subpart); } } } // add any remaining text output += template.substr(end == -1 ? start : end); // render latex formulas return renderMath(output); }; codeq.template = { 'setResources': function (newResources) { resources = newResources; }, // instantiate a template 'process': process, // instantiate templates in a lang→template dictionary 'processDictionary': function (dict, templatePath, args) { var lang; for (lang in dict) { if (!dict.hasOwnProperty(lang)) continue; dict[lang] = codeq.template.process(dict[lang], templatePath, args); } } }; })();