This module contains several utility functions that aid in data processing and rendering, including string manipulation, class extraction, dynamic property definition, and table row rendering. These functions provide functionality for tasks such as escaping special characters, defining properties, and rendering HTML tables with dynamic classes.
npm run import -- "google sheet layout template"
function safeName(name) {
return name.replace(/\//ig, ' ').replace(/[^a-z0-9\- ]/ig, '-').substr(0, 40);
}
function escape(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\${OUTPUT}amp;');
}
function getDataClasses(c, data) {
// get classes from mustache vars used with supplied data
return typeof data != 'object' ? [] : (data || [])
.reduce((keys, cur) => keys.concat(Object.keys(cur)), [])
.filter((k, h, a) => a.indexOf(k) == h)
.filter(k => c.match(new RegExp(`\\{\\{\\s*[>#\\/]?\\s*${escape(k)}\\s*\\}\\}`, 'ig')))
}
function defineProperty(c, value, properties) {
if (c.substr(0, 2) === '::' || c === ':render') {
if(c === ':render') c = '::render'; // just to fix using it below
if(typeof properties[c.substr(2)] == 'undefined') {
properties[c.substr(2)] = [];
} else if (!Array.isArray(properties[c.substr(2)])) {
properties[c.substr(2)] = [properties[c.substr(2)]];
}
properties[c.substr(2)][properties[c.substr(2)].length] = value;
} else {
properties[c.substr(1)] = value;
}
}
function renderRows(key, rows, properties, templates) {
// set object properties for mustache template
var html = (rows || []).reduce((arr, row, i) => {
var rowsHtml = row.reduce((arr, c, j) => {
if(c.substr(0, 1) === ':') {
// use subsequent column for property values
defineProperty(c, row[j + 1], properties)
// render if it is not the value for the previous property
} else if(j === 0 || row[j - 1] && row[j - 1].substr(0, 1) !== ':') {
var dataClasses = getDataClasses(c, properties[key + '-original-data'])
.map(k => 'val-' + safeName(k))
.join(' ')
var sectionClasses = getDataClasses(c, [properties, templates])
.map(k => 'section-' + safeName(k))
.join(' ')
arr[arr.length] = `
<div class="cell-${arr.length} ${dataClasses} ${sectionClasses}">
${c}
</div>
`;
}
return arr;
}, []);
if(rowsHtml.length > 0) {
arr[arr.length] = `
<div class="row-${arr.length} ${properties['class'] || ''} col-${rowsHtml.length}">
${rowsHtml.join('')}
</div>
`;
}
return arr;
}, []);
return html.join('');
}
module.exports = renderRows;
/**
* Escapes special characters in a string for use in regular expressions.
*
* @param {string} s - The string to escape.
* @returns {string} The escaped string.
*/
function escape(s) {
return s.replace(/[[-\/\\^$*+?.()|[\]{}]/g, '\\${OUTPUT}amp;');
}
/**
* Generates a safe name for a string by replacing special characters with spaces and trimming to 40 characters.
*
* @param {string} name - The string to generate a safe name for.
* @returns {string} The safe name.
*/
function safeName(name) {
return name.replace(/\//ig,'').replace(/[^a-z0-9\- ]/ig, '-').substr(0, 40);
}
/**
* Retrieves data classes from Mustache variables used with supplied data.
*
* @param {string} c - The Mustache variable to check.
* @param {object} data - The data to search for Mustache variables.
* @returns {string[]} An array of matching data classes.
*/
function getDataClasses(c, data) {
// Check if data is an object and ensure it's an array of properties
const props = Array.isArray(data)? data : Object.values(data);
return props.reduce((acc, cur) => acc.concat(Object.keys(cur)), []).filter((k, i, arr) => arr.indexOf(k) === i).filter(k => c.match(new RegExp(`\\{\\{\\s*[>#\\/]?\\s*${escape(k)}\\s*\\}\\}`, 'ig')));
}
/**
* Defines a property for an object.
*
* @param {string} c - The property name (with or without leading colon).
* @param {*} value - The value to assign to the property.
* @param {object} properties - The object to assign the property to.
* @returns {object} The updated properties object.
*/
function defineProperty(c, value, properties) {
if (c.startsWith '::') {
c = c.slice(2);
if (!properties[c]) properties[c] = [];
else if (!Array.isArray(properties[c])) properties[c] = [properties[c]];
properties[c].push(value);
} else {
properties[c.slice(1)] = value;
}
return properties;
}
/**
* Renders rows of data using Mustache templates.
*
* @param {string} key - The key of the data to render (used for Mustache variables).
* @param {array} rows - The data to render.
* @param {object} properties - The object of properties to use for rendering.
* @param {object} templates - The object of Mustache templates to use.
* @returns {string} The rendered HTML.
*/
function renderRows(key, rows, properties, templates) {
return rows.reduce((acc, row, i) => {
const rowHtml = row.reduce((arr, c, j) => {
if (c.startsWith(':')) {
// Use subsequent column for property values
defineProperty(c, row[j + 1], properties);
} else if (j === 0 || (row[j - 1] &&!row[j - 1].startsWith(':'))) {
const dataClasses = getDataClasses(c, properties[`${key}-original-data`]).map(k => `val-${safeName(k)}`).join(' ');
const sectionClasses = getDataClasses(c, [properties, templates]).map(k => `section-${safeName(k)}`).join(' ');
arr.push(`
<div class="cell-${i} ${dataClasses} ${sectionClasses}">
${c}
</div>
`);
}
return arr;
}, []).join('');
if (rowHtml) {
const className = properties['class'] || '';
acc.push(`
<div class="row-${i} ${className} col-${row.length}">
${rowHtml}
</div>
`);
}
return acc;
}, []).join('');
}
module.exports = renderRows;
Function Breakdown
safeName(name)
/
) with spaces.escape(s)
getDataClasses(c, data)
defineProperty(c, value, properties)
properties
object.::
or is :render
, it is treated as a special property and added to the corresponding array in the properties
object.renderRows(key, rows, properties, templates)
defineProperty
function to set object properties for Mustache template rendering.