|
// https://codepen.io/r-k-b/pen/OgYmwp?editors=0010 |
|
// https://gist.github.com/r-k-b/731dbdc684e3366fd7d17140cd31a7e5 |
|
|
|
// note on field name vs ID / LongID: |
|
// - 'name' is how the field is shown by BC, |
|
// - 'LongID' is how the field must be named when POSTing to BC |
|
|
|
const config = { |
|
showProximityFields: true, |
|
} |
|
|
|
const trim = s => s.trim(); |
|
|
|
const nameRE = /\>(.+)\</; |
|
|
|
const valsRE = /value="(.+)"/; |
|
|
|
const srcElem = document.getElementById("input"); |
|
|
|
const jsTypeAOutputElem = document.getElementById("js-output-a"); |
|
|
|
const jsTypeBOutputElem = document.getElementById("js-output-b"); |
|
|
|
const htmlOutputElem = document.getElementById("html-output"); |
|
|
|
const typeMap = { |
|
"1": { |
|
typeName: "Text (String)", |
|
typeClass: 'text', |
|
renderIDTag: false |
|
}, |
|
"2": { |
|
typeName: "Number", |
|
typeClass: 'number', |
|
renderIDTag: false |
|
}, |
|
"3": { |
|
typeName: "True/False (Boolean)", |
|
typeClass: 'boolean-checkbox', |
|
renderIDTag: false |
|
}, |
|
"4": { |
|
typeName: "DateTime", |
|
typeClass: 'datetime', |
|
renderIDTag: false |
|
}, |
|
"5": { |
|
typeName: "List (Dropdown List)", |
|
typeClass: 'select', |
|
renderIDTag: false |
|
}, |
|
"6": { |
|
typeName: "List (Checkbox List)", |
|
typeClass: 'checkbox', |
|
renderIDTag: false |
|
}, |
|
"7": { |
|
typeName: "List (Radio List)", |
|
typeClass: 'radio', |
|
renderIDTag: false |
|
}, |
|
"8": { |
|
typeName: "Media", |
|
typeClass: 'media', |
|
renderIDTag: false |
|
}, |
|
"9": { |
|
typeName: "Text (Multiline)", |
|
typeClass: 'text', |
|
renderIDTag: false |
|
}, |
|
"10": { |
|
typeName: "List (Listbox List)", |
|
typeClass: 'listbox', |
|
renderIDTag: false |
|
}, |
|
"11": { |
|
typeName: "Text (Hyperlink)", |
|
typeClass: 'text', |
|
renderIDTag: false |
|
}, |
|
"12": { |
|
typeName: "Data Source", |
|
typeClass: 'select', |
|
renderIDTag: true |
|
} |
|
}; |
|
|
|
// Only present when webapp is configured for 'Allow Proximity Search' |
|
const itemAddressFields = [ |
|
{ |
|
name: 'address1', |
|
fieldID: 'ItemAddress', |
|
fieldIDLong: 'ItemAddress', |
|
type: '1', |
|
mandatory: undefined, |
|
dataSourceID: undefined, |
|
useWYSIWYG: undefined, |
|
unknownProperty: undefined, |
|
isEditable: true, |
|
isCustom: false, |
|
}, |
|
{ |
|
name: 'city', |
|
fieldID: 'ItemCity', |
|
fieldIDLong: 'ItemCity', |
|
type: '1', |
|
mandatory: undefined, |
|
dataSourceID: undefined, |
|
useWYSIWYG: undefined, |
|
unknownProperty: undefined, |
|
isEditable: true, |
|
isCustom: false, |
|
}, |
|
{ |
|
name: 'state', |
|
fieldID: 'ItemState', |
|
fieldIDLong: 'ItemState', |
|
type: '1', |
|
mandatory: undefined, |
|
dataSourceID: undefined, |
|
useWYSIWYG: undefined, |
|
unknownProperty: undefined, |
|
isEditable: true, |
|
isCustom: false, |
|
}, |
|
{ |
|
name: 'country', |
|
fieldID: 'ItemCountry', |
|
fieldIDLong: 'ItemCountry', |
|
type: '5', |
|
mandatory: undefined, |
|
dataSourceID: undefined, |
|
useWYSIWYG: undefined, |
|
unknownProperty: undefined, |
|
isEditable: true, |
|
isCustom: false, |
|
}, |
|
{ |
|
name: 'zipcode', |
|
fieldID: 'ItemZip', |
|
fieldIDLong: 'ItemZip', |
|
type: '1', |
|
mandatory: undefined, |
|
dataSourceID: undefined, |
|
useWYSIWYG: undefined, |
|
unknownProperty: undefined, |
|
isEditable: true, |
|
isCustom: false, |
|
}, |
|
] |
|
|
|
|
|
const getTypeName = t => typeMap[t].typeName || "?"; |
|
|
|
|
|
const merge = defaultsObj => objA => { |
|
return Object.assign({}, defaultsObj, objA) |
|
} |
|
|
|
|
|
const parse = s => { |
|
const name = s.match(nameRE)[1].replace(/\*$/, ""); |
|
|
|
const vals = s.match(valsRE)[1].split(","); |
|
|
|
const fieldID = vals[0] |
|
|
|
const isCustom = fieldID > 0 |
|
|
|
const fieldIDLong = isCustom |
|
? 'CAT_Custom_' + fieldID |
|
: name |
|
|
|
const mergeBase = merge({ |
|
name, |
|
fieldID, |
|
fieldIDLong, |
|
type: vals[1], |
|
mandatory: vals[2] === "1", |
|
dataSourceID: vals[3], |
|
useWYSIWYG: vals[4] === "1", |
|
unknownProperty: vals[5], |
|
isEditable: true, |
|
isCustom, |
|
}) |
|
|
|
switch (fieldID) { |
|
case '-1': // URL |
|
return mergeBase({ |
|
isEditable: false, |
|
}) |
|
case '-2': // Name |
|
return mergeBase({ |
|
fieldIDLong: 'ItemName' |
|
}) |
|
|
|
case '-3': // Weighting |
|
return mergeBase({ |
|
isEditable: false, |
|
}) |
|
|
|
case '-4': // Release Date |
|
return mergeBase({ |
|
isEditable: false, |
|
}) |
|
|
|
case '-5': // Expiry Date |
|
return mergeBase({ |
|
isEditable: false, |
|
}) |
|
|
|
case '-6': // Enabled |
|
return mergeBase({ |
|
isEditable: false, |
|
}) |
|
|
|
case '-7': // Content |
|
return mergeBase({ |
|
fieldIDLong: 'ItemDescription' |
|
}) |
|
|
|
default: |
|
return mergeBase({}) |
|
} |
|
}; |
|
|
|
const scream = s => s.toUpperCase().replace(/\s/g, "_"); |
|
|
|
|
|
const stringifyObjectsTypeA = x => ` obj[${scream(x.name)}] = { |
|
name: ${scream(x.name)}, |
|
fieldID: '${x.fieldID}', |
|
fieldIDLong: '${x.fieldIDLong}', |
|
type: '${x.type}', |
|
typeName: '${getTypeName(x.type)}', |
|
mandatory: ${x.mandatory}, |
|
dataSourceID: '${x.dataSourceID}', |
|
useWYSIWYG: ${x.useWYSIWYG}, |
|
unknownProperty: '${x.unknownProperty}', |
|
isEditable: ${x.isEditable}, |
|
isCustom: ${x.isCustom}, |
|
}`; |
|
|
|
|
|
const stringifyObjectsTypeB = x => ` '${x.name}': { |
|
name: '${x.name}', |
|
fieldID: '${x.fieldID}', |
|
fieldIDLong: '${x.fieldIDLong}', |
|
type: '${x.type}', // '${getTypeName(x.type)}' |
|
mandatory: ${x.mandatory}, |
|
dataSourceID: '${x.dataSourceID}', |
|
useWYSIWYG: ${x.useWYSIWYG}, |
|
unknownProperty: '${x.unknownProperty}', |
|
isEditable: ${x.isEditable}, |
|
isCustom: ${x.isCustom}, |
|
},`; |
|
|
|
const stringifyDeclarations = x => `var ${scream(x.name)} = '${x.name}'`; |
|
|
|
|
|
const showJSTypeAOutput = s => { |
|
jsTypeAOutputElem.textContent = s; |
|
}; |
|
|
|
|
|
const showJSTypeBOutput = s => { |
|
jsTypeBOutputElem.textContent = s; |
|
}; |
|
|
|
|
|
const showHTMLOutput = s => { |
|
htmlOutputElem.textContent = s; |
|
}; |
|
|
|
|
|
const renderJSTypeA = data => { |
|
const declarations = data.map(stringifyDeclarations).join("\n"); |
|
|
|
const objects = data.map(stringifyObjectsTypeA).join("\n\n"); |
|
|
|
const result = `${declarations} |
|
|
|
function getWebappSchema() { |
|
var obj = {} |
|
|
|
${objects} |
|
|
|
return obj |
|
} |
|
`; |
|
|
|
console.log({ result }, "js type-A"); |
|
return result |
|
}; |
|
|
|
|
|
const renderJSTypeB = data => { |
|
const objects = data.map(stringifyObjectsTypeB); |
|
|
|
const result = `{\n${objects.join("\n\n")}\n}`; |
|
|
|
console.log({ result }, "js type-B"); |
|
return result |
|
}; |
|
|
|
|
|
const stringifyObjectsHTMLTypeA = field => { |
|
const fieldType = typeMap[field.type] |
|
|
|
const name = fieldType.renderIDTag |
|
? field.name + '_id' |
|
: field.name |
|
|
|
return ` <div |
|
data-field-type='${field.type}' |
|
data-field-name='${name}' |
|
class='${fieldType.typeClass}' |
|
rel='${field.fieldIDLong}' |
|
>{{ this["${name}"] }}</div>` |
|
} |
|
|
|
|
|
const renderHTMLTypeA = data => { |
|
const objects = data |
|
.filter(field => field.isEditable) |
|
.map(stringifyObjectsHTMLTypeA) |
|
|
|
const result = `${objects.join("\n\n")}\n`; |
|
|
|
console.log({ result }, "html type-A"); |
|
return result |
|
} |
|
|
|
|
|
const run = () => { |
|
try { |
|
console.log("running..."); |
|
showJSTypeAOutput("running..."); |
|
showHTMLOutput("running..."); |
|
|
|
const srcText = srcElem.value; |
|
|
|
if (srcText.trim() === "") { |
|
throw new Error("empty input?"); |
|
} |
|
|
|
const src = srcText.trim().split("\n").map(trim); |
|
|
|
const explicitData = src.map(parse); |
|
|
|
const data = config.showProximityFields |
|
? explicitData.concat(itemAddressFields) |
|
: explicitData |
|
|
|
showJSTypeAOutput(renderJSTypeA(data)); |
|
showJSTypeBOutput(renderJSTypeB(data)); |
|
showHTMLOutput(renderHTMLTypeA(data)); |
|
} catch (error) { |
|
showJSTypeAOutput(error.stack); |
|
showJSTypeBOutput(''); |
|
showHTMLOutput(''); |
|
} |
|
}; |
|
|
|
run(); |
|
srcElem.addEventListener("input", run); |