You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/public/src/templates.js

370 lines
9.4 KiB
JavaScript

(function (module) {
var config = {},
templates,
fs = null,
available_templates = [],
parsed_variables = {};
module.exports = templates = {};
try {
fs = require('fs');
} catch (e) {}
templates.force_refresh = function (tpl) {
return !!config.force_refresh[tpl];
}
templates.get_custom_map = function (tpl) {
if (config['custom_mapping'] && tpl) {
for (var pattern in config['custom_mapping']) {
if (tpl.match(pattern)) {
return (config['custom_mapping'][pattern]);
}
}
}
return false;
}
templates.is_available = function (tpl) {
return jQuery.inArray(tpl, available_templates) !== -1;
};
templates.ready = function (callback) {
if (callback == null) {
if (this.ready_callback) {
this.ready_callback();
} else {
this.loaded = true;
}
} else {
if (this.loaded == true) {
callback();
} else {
this.ready_callback = callback;
}
}
};
templates.prepare = function (raw_tpl, data) {
var template = {};
template.html = raw_tpl;
template.parse = parse;
template.blocks = {};
return template;
};
function loadTemplates(templatesToLoad, customTemplateDir) {
function loadServer() {
var loaded = templatesToLoad.length;
function getTemplates(directory) {
for (var t in templatesToLoad) {
(function (file) {
fs.readFile(directory + '/' + file + '.tpl', function (err, html) {
var template = function () {
this.toString = function () {
return this.html;
};
}
template.prototype.file = file;
template.prototype.parse = parse;
template.prototype.html = String(html);
global.templates[file] = new template;
loaded--;
if (loaded == 0) templates.ready();
});
}(templatesToLoad[t]));
}
}
if (customTemplateDir) {
fs.exists(customTemplateDir, function (exists) {
var directory = (exists ? customTemplateDir : __dirname + '/../templates');
getTemplates(directory);
});
} else {
getTemplates(__dirname + '/../templates');
}
}
function loadClient() {
jQuery.when(jQuery.getJSON(RELATIVE_PATH + '/templates/config.json'), jQuery.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function (config_data, templates_data) {
config = config_data[0];
available_templates = templates_data[0];
templates.ready();
});
}
if (fs === null) loadClient();
else loadServer();
}
templates.init = function (templates_to_load, custom_templates) {
loadTemplates(templates_to_load || [], custom_templates || false);
}
templates.getTemplateNameFromUrl = function (url) {
var parts = url.split('?')[0].split('/');
for (var i = 0; i < parts.length; ++i) {
if (templates.is_available(parts[i])) {
return parts[i];
}
}
return '';
}
templates.load_template = function (callback, url, template) {
var location = document.location || window.location,
api_url = (url === '' || url === '/') ? 'home' : url,
tpl_url = templates.get_custom_map(api_url.split('?')[0]),
trimmed = api_url;
if (!tpl_url) {
tpl_url = templates.getTemplateNameFromUrl(api_url);
}
var template_data = null;
var timestamp = new Date().getTime(); //debug
if (!templates[tpl_url]) {
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function (html) {
var template = function () {
this.toString = function () {
return this.html;
};
}
template.prototype.parse = parse;
template.prototype.html = String(html);
template.prototype.blocks = {};
templates[tpl_url] = new template;
parse_template();
});
} else {
parse_template();
}
jQuery.get(RELATIVE_PATH + '/api/' + api_url, function (data) {
if (!data) {
ajaxify.go('404');
return;
}
template_data = data;
parse_template();
}).fail(function (data) {
if (data && data.status == 404) {
return ajaxify.go('404');
} else if (data && data.status === 403) {
return ajaxify.go('403');
} else {
app.alertError("Can't load template data!");
}
});
function parse_template() {
if (!templates[tpl_url] || !template_data) return;
if (typeof global !== "undefined")
template_data['relative_path'] = nconf.get('relative_path');
else
template_data['relative_path'] = RELATIVE_PATH;
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
$('#content').html(translatedTemplate);
jQuery('#content [template-variable]').each(function (index, element) {
var value = null;
switch ($(element).attr('template-type')) {
case 'boolean':
value = ($(element).val() === 'true' || $(element).val() === '1') ? true : false;
break;
case 'int': // Intentional fall-through
case 'integer':
value = parseInt($(element).val());
break;
default:
value = $(element).val();
break;
}
templates.set($(element).attr('template-variable'), value);
});
if (callback) {
callback(true);
}
});
}
}
templates.flush = function () {
parsed_variables = {};
}
templates.get = function (key) {
return parsed_variables[key];
}
templates.set = function (key, value) {
parsed_variables[key] = value;
}
//modified from https://github.com/psychobunny/dcp.templates
var parse = function (data) {
var self = this;
function replace(key, value, template) {
var searchRegex = new RegExp('{' + key + '}', 'g');
return template.replace(searchRegex, value);
}
function makeRegex(block) {
return new RegExp("<!--[\\s]*BEGIN " + block + "[\\s]*-->[\\s\\S]*<!--[\\s]*END " + block + "[\\s]*-->", 'g');
}
function makeConditionalRegex(block) {
return new RegExp("<!--[\\s]*IF " + block + "[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF " + block + "[\\s]*-->", 'g');
}
function getBlock(regex, block, template) {
data = template.match(regex);
if (data == null) return;
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
var begin = new RegExp("(\r\n)*<!-- BEGIN " + block + " -->(\r\n)*", "g"),
end = new RegExp("(\r\n)*<!-- END " + block + " -->(\r\n)*", "g"),
data = data[0]
.replace(begin, "")
.replace(end, "");
return data;
}
function setBlock(regex, block, template) {
return template.replace(regex, block);
}
var template = this.html,
regex, block;
return (function parse(data, namespace, template, blockInfo) {
if (!data || data.length == 0) {
template = '';
}
for (var d in data) {
if (data.hasOwnProperty(d)) {
if (typeof data[d] === 'undefined') {
continue;
} else if (data[d] === null) {
template = replace(namespace + d, '', template);
} else if (data[d].constructor == Array) {
namespace += d + '.';
var regex = makeRegex(d),
block = getBlock(regex, namespace.substring(0, namespace.length - 1), template);
if (block == null) {
namespace = namespace.replace(d + '.', '');
continue;
}
var numblocks = data[d].length - 1,
i = 0,
result = "";
do {
result += parse(data[d][i], namespace, block, {iterator: i, total: numblocks});
} while (i++ < numblocks);
namespace = namespace.replace(d + '.', '');
template = setBlock(regex, result, template);
} else if (data[d] instanceof Object) {
namespace += d + '.';
regex = makeRegex(d),
block = getBlock(regex, namespace, template)
if (block == null) continue;
block = parse(data[d], namespace, block);
template = setBlock(regex, block, template);
} else {
function checkConditional(key, value) {
var conditional = makeConditionalRegex(key),
matches = template.match(conditional);
if (matches !== null) {
for (var i = 0, ii = matches.length; i < ii; i++) {
var conditionalBlock = matches[i].split(/<!-- ELSE -->/);
if (conditionalBlock[1]) {
// there is an else statement
if (!value) {
template = template.replace(matches[i], conditionalBlock[1]);
} else {
template = template.replace(matches[i], conditionalBlock[0]);
}
} else {
// regular if statement
if (!value) {
template = template.replace(matches[i], '');
}
}
}
}
}
checkConditional(namespace + d, data[d]);
checkConditional('!' + namespace + d, !data[d]);
if (blockInfo) {
checkConditional('@first', blockInfo.iterator === 0);
checkConditional('@last', blockInfo.iterator === blockInfo.total);
}
template = replace(namespace + d, data[d], template);
}
}
}
if (namespace) {
var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
template = template.replace(regex, '');
}
return template;
})(data, "", template);
}
if ('undefined' !== typeof window) {
window.templates = module.exports;
templates.init();
}
})('undefined' === typeof module ? {
module: {
exports: {}
}
} : module)