287 lines
6.8 KiB
JavaScript
287 lines
6.8 KiB
JavaScript
'use strict';
|
|
|
|
var pug_has_own_property = Object.prototype.hasOwnProperty;
|
|
|
|
/**
|
|
* Merge two attribute objects giving precedence
|
|
* to values in object `b`. Classes are special-cased
|
|
* allowing for arrays and merging/joining appropriately
|
|
* resulting in a string.
|
|
*
|
|
* @param {Object} a
|
|
* @param {Object} b
|
|
* @return {Object} a
|
|
* @api private
|
|
*/
|
|
|
|
exports.merge = pug_merge;
|
|
function pug_merge(a, b) {
|
|
if (arguments.length === 1) {
|
|
var attrs = a[0];
|
|
for (var i = 1; i < a.length; i++) {
|
|
attrs = pug_merge(attrs, a[i]);
|
|
}
|
|
return attrs;
|
|
}
|
|
|
|
for (var key in b) {
|
|
if (key === 'class') {
|
|
var valA = a[key] || [];
|
|
a[key] = (Array.isArray(valA) ? valA : [valA]).concat(b[key] || []);
|
|
} else if (key === 'style') {
|
|
var valA = pug_style(a[key]);
|
|
valA = valA && valA[valA.length - 1] !== ';' ? valA + ';' : valA;
|
|
var valB = pug_style(b[key]);
|
|
valB = valB && valB[valB.length - 1] !== ';' ? valB + ';' : valB;
|
|
a[key] = valA + valB;
|
|
} else {
|
|
a[key] = b[key];
|
|
}
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* Process array, object, or string as a string of classes delimited by a space.
|
|
*
|
|
* If `val` is an array, all members of it and its subarrays are counted as
|
|
* classes. If `escaping` is an array, then whether or not the item in `val` is
|
|
* escaped depends on the corresponding item in `escaping`. If `escaping` is
|
|
* not an array, no escaping is done.
|
|
*
|
|
* If `val` is an object, all the keys whose value is truthy are counted as
|
|
* classes. No escaping is done.
|
|
*
|
|
* If `val` is a string, it is counted as a class. No escaping is done.
|
|
*
|
|
* @param {(Array.<string>|Object.<string, boolean>|string)} val
|
|
* @param {?Array.<string>} escaping
|
|
* @return {String}
|
|
*/
|
|
exports.classes = pug_classes;
|
|
function pug_classes_array(val, escaping) {
|
|
var classString = '',
|
|
className,
|
|
padding = '',
|
|
escapeEnabled = Array.isArray(escaping);
|
|
for (var i = 0; i < val.length; i++) {
|
|
className = pug_classes(val[i]);
|
|
if (!className) continue;
|
|
escapeEnabled && escaping[i] && (className = pug_escape(className));
|
|
classString = classString + padding + className;
|
|
padding = ' ';
|
|
}
|
|
return classString;
|
|
}
|
|
function pug_classes_object(val) {
|
|
var classString = '',
|
|
padding = '';
|
|
for (var key in val) {
|
|
if (key && val[key] && pug_has_own_property.call(val, key)) {
|
|
classString = classString + padding + key;
|
|
padding = ' ';
|
|
}
|
|
}
|
|
return classString;
|
|
}
|
|
function pug_classes(val, escaping) {
|
|
if (Array.isArray(val)) {
|
|
return pug_classes_array(val, escaping);
|
|
} else if (val && typeof val === 'object') {
|
|
return pug_classes_object(val);
|
|
} else {
|
|
return val || '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert object or string to a string of CSS styles delimited by a semicolon.
|
|
*
|
|
* @param {(Object.<string, string>|string)} val
|
|
* @return {String}
|
|
*/
|
|
|
|
exports.style = pug_style;
|
|
function pug_style(val) {
|
|
if (!val) return '';
|
|
if (typeof val === 'object') {
|
|
var out = '';
|
|
for (var style in val) {
|
|
/* istanbul ignore else */
|
|
if (pug_has_own_property.call(val, style)) {
|
|
out = out + style + ':' + val[style] + ';';
|
|
}
|
|
}
|
|
return out;
|
|
} else {
|
|
return val + '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render the given attribute.
|
|
*
|
|
* @param {String} key
|
|
* @param {String} val
|
|
* @param {Boolean} escaped
|
|
* @param {Boolean} terse
|
|
* @return {String}
|
|
*/
|
|
exports.attr = pug_attr;
|
|
function pug_attr(key, val, escaped, terse) {
|
|
if (
|
|
val === false ||
|
|
val == null ||
|
|
(!val && (key === 'class' || key === 'style'))
|
|
) {
|
|
return '';
|
|
}
|
|
if (val === true) {
|
|
return ' ' + (terse ? key : key + '="' + key + '"');
|
|
}
|
|
var type = typeof val;
|
|
if (
|
|
(type === 'object' || type === 'function') &&
|
|
typeof val.toJSON === 'function'
|
|
) {
|
|
val = val.toJSON();
|
|
}
|
|
if (typeof val !== 'string') {
|
|
val = JSON.stringify(val);
|
|
if (!escaped && val.indexOf('"') !== -1) {
|
|
return ' ' + key + "='" + val.replace(/'/g, ''') + "'";
|
|
}
|
|
}
|
|
if (escaped) val = pug_escape(val);
|
|
return ' ' + key + '="' + val + '"';
|
|
}
|
|
|
|
/**
|
|
* Render the given attributes object.
|
|
*
|
|
* @param {Object} obj
|
|
* @param {Object} terse whether to use HTML5 terse boolean attributes
|
|
* @return {String}
|
|
*/
|
|
exports.attrs = pug_attrs;
|
|
function pug_attrs(obj, terse) {
|
|
var attrs = '';
|
|
|
|
for (var key in obj) {
|
|
if (pug_has_own_property.call(obj, key)) {
|
|
var val = obj[key];
|
|
|
|
if ('class' === key) {
|
|
val = pug_classes(val);
|
|
attrs = pug_attr(key, val, false, terse) + attrs;
|
|
continue;
|
|
}
|
|
if ('style' === key) {
|
|
val = pug_style(val);
|
|
}
|
|
attrs += pug_attr(key, val, false, terse);
|
|
}
|
|
}
|
|
|
|
return attrs;
|
|
}
|
|
|
|
/**
|
|
* Escape the given string of `html`.
|
|
*
|
|
* @param {String} html
|
|
* @return {String}
|
|
* @api private
|
|
*/
|
|
|
|
var pug_match_html = /["&<>]/;
|
|
exports.escape = pug_escape;
|
|
function pug_escape(_html) {
|
|
var html = '' + _html;
|
|
var regexResult = pug_match_html.exec(html);
|
|
if (!regexResult) return _html;
|
|
|
|
var result = '';
|
|
var i, lastIndex, escape;
|
|
for (i = regexResult.index, lastIndex = 0; i < html.length; i++) {
|
|
switch (html.charCodeAt(i)) {
|
|
case 34:
|
|
escape = '"';
|
|
break;
|
|
case 38:
|
|
escape = '&';
|
|
break;
|
|
case 60:
|
|
escape = '<';
|
|
break;
|
|
case 62:
|
|
escape = '>';
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (lastIndex !== i) result += html.substring(lastIndex, i);
|
|
lastIndex = i + 1;
|
|
result += escape;
|
|
}
|
|
if (lastIndex !== i) return result + html.substring(lastIndex, i);
|
|
else return result;
|
|
}
|
|
|
|
/**
|
|
* Re-throw the given `err` in context to the
|
|
* the pug in `filename` at the given `lineno`.
|
|
*
|
|
* @param {Error} err
|
|
* @param {String} filename
|
|
* @param {String} lineno
|
|
* @param {String} str original source
|
|
* @api private
|
|
*/
|
|
|
|
exports.rethrow = pug_rethrow;
|
|
function pug_rethrow(err, filename, lineno, str) {
|
|
if (!(err instanceof Error)) throw err;
|
|
if ((typeof window != 'undefined' || !filename) && !str) {
|
|
err.message += ' on line ' + lineno;
|
|
throw err;
|
|
}
|
|
var context, lines, start, end;
|
|
try {
|
|
str = str || require('fs').readFileSync(filename, {encoding: 'utf8'});
|
|
context = 3;
|
|
lines = str.split('\n');
|
|
start = Math.max(lineno - context, 0);
|
|
end = Math.min(lines.length, lineno + context);
|
|
} catch (ex) {
|
|
err.message +=
|
|
' - could not read from ' + filename + ' (' + ex.message + ')';
|
|
pug_rethrow(err, null, lineno);
|
|
return;
|
|
}
|
|
|
|
// Error context
|
|
context = lines
|
|
.slice(start, end)
|
|
.map(function(line, i) {
|
|
var curr = i + start + 1;
|
|
return (curr == lineno ? ' > ' : ' ') + curr + '| ' + line;
|
|
})
|
|
.join('\n');
|
|
|
|
// Alter exception message
|
|
err.path = filename;
|
|
try {
|
|
err.message =
|
|
(filename || 'Pug') +
|
|
':' +
|
|
lineno +
|
|
'\n' +
|
|
context +
|
|
'\n\n' +
|
|
err.message;
|
|
} catch (e) {}
|
|
throw err;
|
|
}
|