'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]); var valB = pug_style(b[key]); 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.|Object.|string)} val * @param {?Array.} 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)} 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 { val += ''; if (val[val.length - 1] !== ';') return val + ';'; 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 + '"'); } if (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; } try { str = str || require('fs').readFileSync(filename, 'utf8') } catch (ex) { pug_rethrow(err, null, lineno) } var context = 3 , lines = str.split('\n') , start = Math.max(lineno - context, 0) , end = Math.min(lines.length, lineno + context); // Error context var 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; err.message = (filename || 'Pug') + ':' + lineno + '\n' + context + '\n\n' + err.message; throw err; };