-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmt.js
More file actions
244 lines (191 loc) · 7.15 KB
/
mt.js
File metadata and controls
244 lines (191 loc) · 7.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// github for this project
// https://github.com/johnaweiss/HTML-Micro-Templating
// MT HTML PREFIXES (reserved attributes needed to support metadata in containers)
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)
// write sep fx, then determine what to combine with loopCollSinglets
loopOrphanSinglets();
// collection
const collViewNodes = document.querySelectorAll(
gAppPref + 'container[' + gAppPref + 'collection]'
);
// loop and render collection-containers
collViewNodes.forEach(collViewNode => {
makeRecordsetHeaders(collViewNode);
// singlets
loopCollRecordsets(collViewNode);
});
};
function loopOrphanSinglets() {
// singlets
const singletContainers = document.querySelectorAll(
gAppPref + 'container[' + gAppPref + 'records]'
);
// loop and render recordset-containers
singletContainers.forEach(snglContain => {
// get fields from parent attribs before looping
fields = getFields(snglContain);
$("fields", fields )
// merge records into template
loadMerge(snglContain);
});
}
function loopCollRecordsets(scope) {
// get fields from parent attribs before looping
fields = getFields(scope);
// singlets
const singletContainers = scope.querySelectorAll(
gAppPref + 'container[' + gAppPref + 'records]'
);
// loop and render recordset-containers
singletContainers.forEach(snglContain => {
// merge records into template
loadMerge(snglContain);
});
}
function makeRecordsetHeaders(collContainer) {
// merge metadata from recordset into header-template
// get collection-template
const templateHTML = getTemplateHTML(collContainer);
// get all recordsets in the collection
const collectionRecordsets = getCollectionRecordsets(collContainer);
// loop recordsets. Write merged template for each
collectionRecordsets.forEach(recordset => {
makeRecordsetHeader(collContainer, templateHTML, recordset);
});
}
function makeRecordsetHeader(collContainer, templateHTML, recordset) {
let recordsetHTML = templateHTML;
// load recordset-id in template
recordsetHTML = recordsetHTML.replaceAll('[[id]]', recordset.id);
// replace fields in template with metadata from recordset
const metas = getAppMeta(recordset);
Object.keys(metas).forEach(key => {
recordsetHTML = recordsetHTML.replaceAll(`[[${key}]]`, metas[key].trim());
});
// append to container
collContainer.innerHTML += recordsetHTML;
}
function getAppMeta(element) {
const sMetas = element.getAttribute(gAppPref + 'meta').trim();
const objMetas = strToObj(sMetas);
return objMetas;
}
function getCollectionRecordsets(collContainer) {
// we want
// element#id
// mt-collection#advisers
const ID = collContainer.getAttribute(gAppPref + 'collection');
const tag = gAppPref + 'collection';
const collectionSelector = `${tag}#${ID}`;
const collectionNode = document.querySelector(collectionSelector);
const recordsetNodes = collectionNode.children;
recordsets = [...recordsetNodes];
return recordsets;
}
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');
if (!sDataContainer)
sDataContainer = viewNode.getAttribute(gAppPref + 'records');
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);
return fields;
}
function loadMerge(snglContain) {
// load records into template
// get template
const templateHTML = getTemplateHTML(snglContain);
// get data
records = getData(snglContain);
// merge
const mergeHTML = mergeRecords(templateHTML, fields, records);
// append merged-HTML to container
snglContain.innerHTML += mergeHTML;
}
function getTemplateHTML(container) {
// get template html
const templateID = container.getAttribute(gAppPref + 'template');
const template = document.querySelector(`template#${templateID}`);
return template.innerHTML;
}
function getData(snglContain) {
// return headers and records as arrays
const rawData = getRawData(snglContain);
const records = getRecords(rawData);
return records;
}
function getRawData(container) {
// get mt-records id
const dataID = container.getAttribute(gAppPref + 'records');
// get records element
const dataElement = document.querySelector(`${gAppPref}records#${dataID}`);
// get records contents
const rawData = dataElement.innerText.trim();
return rawData;
}
function getRecords(rawData) {
// load records into array, rows and columns
const aRows = rawData.trim().split(gRecDelim);
const records = aRows.map(sRow => {
return sRow.split(gFldDelim);
});
return records;
}
function mergeRecords(templateHTML, fields, records) {
// make html for each record by merging with temlate
let allRecordsHTML = '';
records.forEach(record => {
let recordHTML = mergeRecord(templateHTML, fields, record);
allRecordsHTML += recordHTML;
});
return allRecordsHTML;
}
function mergeRecord(templateHTML, dataFields, record) {
let recordHTML = templateHTML;
// LOOP PLACEHOLDERS in the template
// - THUS, NOT LOOPING FIELDS THAT DON'T APPEAR IN TEMPLATE
// - AND HANDLING SPACE-ESCAPES EFFICIENTLY
// REGEX + matchAll
const templateFieldsRegex = /\{\{\w+\}\}/g;
const templateFields = recordHTML.match(templateFieldsRegex);
// loop placeholders
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
// remove delimiters
let noDelimFld = templFld.slice(2, -2);
// 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 == '_';
if (escapeSpaces) noDelimFld = noDelimFld.slice(0, -1);
// get value based on data-field position
const col = fields.indexOf(noDelimFld);
let value = record[col];
// escape spaces
if (escapeSpaces) value = value.replaceAll(' ', '_');
// load variable into temlate
recordHTML = recordHTML.replaceAll(templFld, value);
});
return recordHTML;
}
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(':')));
return obj;
}
function $(text1, text2) {
console.log(text1);
console.log(text2);
}