From c1c82a1396cf8bee5ad97a02b80a30851f1f2f12 Mon Sep 17 00:00:00 2001 From: nameelza Date: Thu, 27 Jun 2024 15:45:18 -0700 Subject: [PATCH 1/4] refactor: use scoped variables to avoid conflicts and unintended modifications --- mt.js | 75 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/mt.js b/mt.js index 831a4a9..9456bd5 100644 --- a/mt.js +++ b/mt.js @@ -2,14 +2,13 @@ // https://github.com/johnaweiss/HTML-Micro-Templating // MT HTML PREFIXES (reserved attributes needed to support metadata in containers) -const gAppPref = 'mt-'; +const gAppPref = "mt-"; // RECORD DELIMITERS REGEX (new-field, new-record) const gFldDelim = /(?:\n[ \t]*)(?=\w)/g; const gRecDelim = /(?:\n[ \t]*){2,}(?=\w)/g; window.onload = function Merge_Templates() { - // loop each container in body. Singlets first, then Collections. // single recordset templates (not recordset collections) @@ -18,11 +17,11 @@ window.onload = function Merge_Templates() { // collection const collViewNodes = document.querySelectorAll( - gAppPref + 'container[' + gAppPref + 'collection]' + gAppPref + "container[" + gAppPref + "collection]" ); // loop and render collection-containers - collViewNodes.forEach(collViewNode => { + collViewNodes.forEach((collViewNode) => { makeRecordsetHeaders(collViewNode); // singlets @@ -33,34 +32,33 @@ window.onload = function Merge_Templates() { function loopOrphanSinglets() { // singlets const singletContainers = document.querySelectorAll( - gAppPref + 'container[' + gAppPref + 'records]' + gAppPref + "container[" + gAppPref + "records]" ); - + // loop and render recordset-containers - singletContainers.forEach(snglContain => { - - // get fields from parent attribs before looping - fields = getFields(snglContain); - $("fields", fields ) - + singletContainers.forEach((snglContain) => { + // get fields from parent attribs before looping + const fields = getFields(snglContain); + $("fields", fields); + // merge records into template - loadMerge(snglContain); + loadMerge(snglContain, fields); }); } function loopCollRecordsets(scope) { // get fields from parent attribs before looping - fields = getFields(scope); + const fields = getFields(scope); // singlets const singletContainers = scope.querySelectorAll( - gAppPref + 'container[' + gAppPref + 'records]' + gAppPref + "container[" + gAppPref + "records]" ); // loop and render recordset-containers - singletContainers.forEach(snglContain => { + singletContainers.forEach((snglContain) => { // merge records into template - loadMerge(snglContain); + loadMerge(snglContain, fields); }); } @@ -74,7 +72,7 @@ function makeRecordsetHeaders(collContainer) { const collectionRecordsets = getCollectionRecordsets(collContainer); // loop recordsets. Write merged template for each - collectionRecordsets.forEach(recordset => { + collectionRecordsets.forEach((recordset) => { makeRecordsetHeader(collContainer, templateHTML, recordset); }); } @@ -83,11 +81,11 @@ function makeRecordsetHeader(collContainer, templateHTML, recordset) { let recordsetHTML = templateHTML; // load recordset-id in template - recordsetHTML = recordsetHTML.replaceAll('[[id]]', recordset.id); + recordsetHTML = recordsetHTML.replaceAll("[[id]]", recordset.id); // replace fields in template with metadata from recordset const metas = getAppMeta(recordset); - Object.keys(metas).forEach(key => { + Object.keys(metas).forEach((key) => { recordsetHTML = recordsetHTML.replaceAll(`[[${key}]]`, metas[key].trim()); }); @@ -96,7 +94,7 @@ function makeRecordsetHeader(collContainer, templateHTML, recordset) { } function getAppMeta(element) { - const sMetas = element.getAttribute(gAppPref + 'meta').trim(); + const sMetas = element.getAttribute(gAppPref + "meta").trim(); const objMetas = strToObj(sMetas); return objMetas; } @@ -105,8 +103,8 @@ function getCollectionRecordsets(collContainer) { // we want // element#id // mt-collection#advisers - const ID = collContainer.getAttribute(gAppPref + 'collection'); - const tag = gAppPref + 'collection'; + const ID = collContainer.getAttribute(gAppPref + "collection"); + const tag = gAppPref + "collection"; const collectionSelector = `${tag}#${ID}`; const collectionNode = document.querySelector(collectionSelector); const recordsetNodes = collectionNode.children; @@ -118,26 +116,27 @@ function getFields(viewNode) { // return fields from mt-fields node, which is the parent of the collection or recordset // if view-container points to collection, get fieldset-name from collection data-container // if view-container points to recordset, get fieldset-name from recordset data-container - let sDataContainer = viewNode.getAttribute(gAppPref + 'collection'); + let sDataContainer = viewNode.getAttribute(gAppPref + "collection"); if (!sDataContainer) - sDataContainer = viewNode.getAttribute(gAppPref + 'records'); + sDataContainer = viewNode.getAttribute(gAppPref + "records"); const dataNode = document.getElementById(sDataContainer); - const schemaName = dataNode.getAttribute(gAppPref + 'fields'); + const schemaName = dataNode.getAttribute(gAppPref + "fields"); + const schemaNode = document.getElementById(schemaName); const rawFields = schemaNode.innerText.trim(); const fields = rawFields.split(gFldDelim); return fields; } -function loadMerge(snglContain) { +function loadMerge(snglContain, fields) { // load records into template // get template const templateHTML = getTemplateHTML(snglContain); // get data - records = getData(snglContain); + const records = getData(snglContain); // merge const mergeHTML = mergeRecords(templateHTML, fields, records); @@ -148,7 +147,7 @@ function loadMerge(snglContain) { function getTemplateHTML(container) { // get template html - const templateID = container.getAttribute(gAppPref + 'template'); + const templateID = container.getAttribute(gAppPref + "template"); const template = document.querySelector(`template#${templateID}`); return template.innerHTML; @@ -163,7 +162,7 @@ function getData(snglContain) { function getRawData(container) { // get mt-records id - const dataID = container.getAttribute(gAppPref + 'records'); + const dataID = container.getAttribute(gAppPref + "records"); // get records element const dataElement = document.querySelector(`${gAppPref}records#${dataID}`); @@ -176,7 +175,7 @@ function getRawData(container) { function getRecords(rawData) { // load records into array, rows and columns const aRows = rawData.trim().split(gRecDelim); - const records = aRows.map(sRow => { + const records = aRows.map((sRow) => { return sRow.split(gFldDelim); }); @@ -185,9 +184,9 @@ function getRecords(rawData) { function mergeRecords(templateHTML, fields, records) { // make html for each record by merging with temlate - let allRecordsHTML = ''; + let allRecordsHTML = ""; - records.forEach(record => { + records.forEach((record) => { let recordHTML = mergeRecord(templateHTML, fields, record); allRecordsHTML += recordHTML; }); @@ -205,7 +204,7 @@ function mergeRecord(templateHTML, dataFields, record) { const templateFields = recordHTML.match(templateFieldsRegex); // loop placeholders - templateFields.forEach(templFld => { + templateFields.forEach((templFld) => { // need to find matching value in record by name // we can use dataFields to get column position, or, make record a key:value set // need too see trailing underscore (to escape spaces), and with trailing under removed to get value from record @@ -215,7 +214,7 @@ function mergeRecord(templateHTML, dataFields, record) { // check for escaping spaces (last char is underscore). If found, then snip trailing underscore so can get value from record. const lastChr = noDelimFld.slice(-1); - const escapeSpaces = lastChr == '_'; + const escapeSpaces = lastChr == "_"; if (escapeSpaces) noDelimFld = noDelimFld.slice(0, -1); // get value based on data-field position @@ -223,7 +222,7 @@ function mergeRecord(templateHTML, dataFields, record) { let value = record[col]; // escape spaces - if (escapeSpaces) value = value.replaceAll(' ', '_'); + if (escapeSpaces) value = value.replaceAll(" ", "_"); // load variable into temlate recordHTML = recordHTML.replaceAll(templFld, value); @@ -234,11 +233,11 @@ function mergeRecord(templateHTML, dataFields, record) { function strToObj(str) { // return Object from string. Comma-sep key:value pairs. Just wrap in braces. - const obj = Object.fromEntries(str.split(',').map(i => i.split(':'))); + const obj = Object.fromEntries(str.split(",").map((i) => i.split(":"))); return obj; } function $(text1, text2) { console.log(text1); console.log(text2); -} \ No newline at end of file +} From ebbaced7ee592976bfbb770378ac5d4ea3074378 Mon Sep 17 00:00:00 2001 From: nameelza Date: Thu, 27 Jun 2024 17:20:33 -0700 Subject: [PATCH 2/4] feat: Add field handling to support both mt-fields and mt-records embedded fieldnames --- mt.js | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/mt.js b/mt.js index 9456bd5..8e862d5 100644 --- a/mt.js +++ b/mt.js @@ -123,9 +123,20 @@ function getFields(viewNode) { const dataNode = document.getElementById(sDataContainer); const schemaName = dataNode.getAttribute(gAppPref + "fields"); - const schemaNode = document.getElementById(schemaName); - const rawFields = schemaNode.innerText.trim(); - const fields = rawFields.split(gFldDelim); + let fields = []; + + if (schemaName) { + // get fieldnames from mt-fields + const schemaNode = document.getElementById(schemaName); + const rawFields = schemaNode.innerText.trim(); + fields = rawFields.split(gFldDelim); + } else { + // get fieldnames from mt-records + const rawData = dataNode.innerText.trim(); + const rawFields = rawData.split(gRecDelim)[0]; + fields = rawFields.split(gFldDelim); + } + return fields; } @@ -168,7 +179,18 @@ function getRawData(container) { const dataElement = document.querySelector(`${gAppPref}records#${dataID}`); // get records contents - const rawData = dataElement.innerText.trim(); + let rawData = dataElement.innerText.trim(); + + // check if 'mt-fields' attribute exists + const schemaName = dataElement.getAttribute(gAppPref + "fields"); + + // remove the first record from records contents if no 'mt-fields' attribute + if (!schemaName) { + const rawRecords = rawData.split(gRecDelim); + rawRecords.shift(); + rawData = rawRecords.join("\n\n"); + } + return rawData; } @@ -193,7 +215,7 @@ function mergeRecords(templateHTML, fields, records) { return allRecordsHTML; } -function mergeRecord(templateHTML, dataFields, record) { +function mergeRecord(templateHTML, fields, record) { let recordHTML = templateHTML; // LOOP PLACEHOLDERS in the template From 7a2e8116c9376f4b114725f748a3e91cda9b26d2 Mon Sep 17 00:00:00 2001 From: nameelza Date: Thu, 1 Aug 2024 14:10:37 -0700 Subject: [PATCH 3/4] Refactor JavaScript functions for improved readability and performance --- mt.js | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/mt.js b/mt.js index 8e862d5..33463c8 100644 --- a/mt.js +++ b/mt.js @@ -8,6 +8,9 @@ const gAppPref = "mt-"; const gFldDelim = /(?:\n[ \t]*)(?=\w)/g; const gRecDelim = /(?:\n[ \t]*){2,}(?=\w)/g; +// global boolean to indicate if schema is embedded in html +let gHasEmbeddedSchema; + window.onload = function Merge_Templates() { // loop each container in body. Singlets first, then Collections. @@ -16,16 +19,16 @@ window.onload = function Merge_Templates() { loopOrphanSinglets(); // collection - const collViewNodes = document.querySelectorAll( + const collContainers = document.querySelectorAll( gAppPref + "container[" + gAppPref + "collection]" ); // loop and render collection-containers - collViewNodes.forEach((collViewNode) => { - makeRecordsetHeaders(collViewNode); + collContainers.forEach((oContainer) => { + makeRecordsetHeaders(oContainer); // singlets - loopCollRecordsets(collViewNode); + loopCollRecordsets(oContainer); }); }; @@ -112,31 +115,34 @@ function getCollectionRecordsets(collContainer) { return recordsets; } -function getFields(viewNode) { +function getFields(oContainer) { // return fields from mt-fields node, which is the parent of the collection or recordset // if view-container points to collection, get fieldset-name from collection data-container // if view-container points to recordset, get fieldset-name from recordset data-container - let sDataContainer = viewNode.getAttribute(gAppPref + "collection"); - if (!sDataContainer) - sDataContainer = viewNode.getAttribute(gAppPref + "records"); + let sRecordset = + oContainer.getAttribute(gAppPref + "collection") || + oContainer.getAttribute(gAppPref + "records"); + + const dataNode = document.getElementById(sRecordset); - const dataNode = document.getElementById(sDataContainer); + // look for schema in HTML const schemaName = dataNode.getAttribute(gAppPref + "fields"); + gHasEmbeddedSchema = !!schemaName; - let fields = []; + let rawFields; if (schemaName) { - // get fieldnames from mt-fields + // get fieldnames from schema if found in HTML const schemaNode = document.getElementById(schemaName); - const rawFields = schemaNode.innerText.trim(); - fields = rawFields.split(gFldDelim); + rawFields = schemaNode.innerText.trim(); } else { - // get fieldnames from mt-records + // get fieldnames from top of recordset, which look like the first record in the dataset const rawData = dataNode.innerText.trim(); - const rawFields = rawData.split(gRecDelim)[0]; - fields = rawFields.split(gFldDelim); + rawFields = rawData.split(gRecDelim)[0]; } + const fields = rawFields.split(gFldDelim); + return fields; } @@ -181,11 +187,8 @@ function getRawData(container) { // get records contents let rawData = dataElement.innerText.trim(); - // check if 'mt-fields' attribute exists - const schemaName = dataElement.getAttribute(gAppPref + "fields"); - - // remove the first record from records contents if no 'mt-fields' attribute - if (!schemaName) { + // remove field names (first record) if fieldnames embedded in data + if (!gHasEmbeddedSchema) { const rawRecords = rawData.split(gRecDelim); rawRecords.shift(); rawData = rawRecords.join("\n\n"); From 1b3e77cbfe8def8c260b69b9260fdbb5c3fb687e Mon Sep 17 00:00:00 2001 From: nameelza Date: Thu, 1 Aug 2024 14:22:50 -0700 Subject: [PATCH 4/4] style: replace double quotes with single quotes --- mt.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/mt.js b/mt.js index 33463c8..7bef710 100644 --- a/mt.js +++ b/mt.js @@ -2,7 +2,7 @@ // https://github.com/johnaweiss/HTML-Micro-Templating // MT HTML PREFIXES (reserved attributes needed to support metadata in containers) -const gAppPref = "mt-"; +const gAppPref = 'mt-'; // RECORD DELIMITERS REGEX (new-field, new-record) const gFldDelim = /(?:\n[ \t]*)(?=\w)/g; @@ -20,7 +20,7 @@ window.onload = function Merge_Templates() { // collection const collContainers = document.querySelectorAll( - gAppPref + "container[" + gAppPref + "collection]" + gAppPref + 'container[' + gAppPref + 'collection]' ); // loop and render collection-containers @@ -35,14 +35,14 @@ window.onload = function Merge_Templates() { function loopOrphanSinglets() { // singlets const singletContainers = document.querySelectorAll( - gAppPref + "container[" + gAppPref + "records]" + gAppPref + 'container[' + gAppPref + 'records]' ); // loop and render recordset-containers singletContainers.forEach((snglContain) => { // get fields from parent attribs before looping const fields = getFields(snglContain); - $("fields", fields); + $('fields', fields); // merge records into template loadMerge(snglContain, fields); @@ -55,7 +55,7 @@ function loopCollRecordsets(scope) { // singlets const singletContainers = scope.querySelectorAll( - gAppPref + "container[" + gAppPref + "records]" + gAppPref + 'container[' + gAppPref + 'records]' ); // loop and render recordset-containers @@ -84,7 +84,7 @@ function makeRecordsetHeader(collContainer, templateHTML, recordset) { let recordsetHTML = templateHTML; // load recordset-id in template - recordsetHTML = recordsetHTML.replaceAll("[[id]]", recordset.id); + recordsetHTML = recordsetHTML.replaceAll('[[id]]', recordset.id); // replace fields in template with metadata from recordset const metas = getAppMeta(recordset); @@ -97,7 +97,7 @@ function makeRecordsetHeader(collContainer, templateHTML, recordset) { } function getAppMeta(element) { - const sMetas = element.getAttribute(gAppPref + "meta").trim(); + const sMetas = element.getAttribute(gAppPref + 'meta').trim(); const objMetas = strToObj(sMetas); return objMetas; } @@ -106,8 +106,8 @@ function getCollectionRecordsets(collContainer) { // we want // element#id // mt-collection#advisers - const ID = collContainer.getAttribute(gAppPref + "collection"); - const tag = gAppPref + "collection"; + const ID = collContainer.getAttribute(gAppPref + 'collection'); + const tag = gAppPref + 'collection'; const collectionSelector = `${tag}#${ID}`; const collectionNode = document.querySelector(collectionSelector); const recordsetNodes = collectionNode.children; @@ -120,13 +120,13 @@ function getFields(oContainer) { // if view-container points to collection, get fieldset-name from collection data-container // if view-container points to recordset, get fieldset-name from recordset data-container let sRecordset = - oContainer.getAttribute(gAppPref + "collection") || - oContainer.getAttribute(gAppPref + "records"); + oContainer.getAttribute(gAppPref + 'collection') || + oContainer.getAttribute(gAppPref + 'records'); const dataNode = document.getElementById(sRecordset); // look for schema in HTML - const schemaName = dataNode.getAttribute(gAppPref + "fields"); + const schemaName = dataNode.getAttribute(gAppPref + 'fields'); gHasEmbeddedSchema = !!schemaName; let rawFields; @@ -164,7 +164,7 @@ function loadMerge(snglContain, fields) { function getTemplateHTML(container) { // get template html - const templateID = container.getAttribute(gAppPref + "template"); + const templateID = container.getAttribute(gAppPref + 'template'); const template = document.querySelector(`template#${templateID}`); return template.innerHTML; @@ -179,7 +179,7 @@ function getData(snglContain) { function getRawData(container) { // get mt-records id - const dataID = container.getAttribute(gAppPref + "records"); + const dataID = container.getAttribute(gAppPref + 'records'); // get records element const dataElement = document.querySelector(`${gAppPref}records#${dataID}`); @@ -191,7 +191,7 @@ function getRawData(container) { if (!gHasEmbeddedSchema) { const rawRecords = rawData.split(gRecDelim); rawRecords.shift(); - rawData = rawRecords.join("\n\n"); + rawData = rawRecords.join('\n\n'); } return rawData; @@ -209,7 +209,7 @@ function getRecords(rawData) { function mergeRecords(templateHTML, fields, records) { // make html for each record by merging with temlate - let allRecordsHTML = ""; + let allRecordsHTML = ''; records.forEach((record) => { let recordHTML = mergeRecord(templateHTML, fields, record); @@ -239,7 +239,7 @@ function mergeRecord(templateHTML, fields, record) { // check for escaping spaces (last char is underscore). If found, then snip trailing underscore so can get value from record. const lastChr = noDelimFld.slice(-1); - const escapeSpaces = lastChr == "_"; + const escapeSpaces = lastChr == '_'; if (escapeSpaces) noDelimFld = noDelimFld.slice(0, -1); // get value based on data-field position @@ -247,7 +247,7 @@ function mergeRecord(templateHTML, fields, record) { let value = record[col]; // escape spaces - if (escapeSpaces) value = value.replaceAll(" ", "_"); + if (escapeSpaces) value = value.replaceAll(' ', '_'); // load variable into temlate recordHTML = recordHTML.replaceAll(templFld, value); @@ -258,7 +258,7 @@ function mergeRecord(templateHTML, fields, record) { function strToObj(str) { // return Object from string. Comma-sep key:value pairs. Just wrap in braces. - const obj = Object.fromEntries(str.split(",").map((i) => i.split(":"))); + const obj = Object.fromEntries(str.split(',').map((i) => i.split(':'))); return obj; }