/**
* Class represents ElementBuilder
* @class ElementBuilder
*/
class ElementBuilder {
/**
* Create ElementBuilder instance
* @param {ElementSignature[]} signatures
* @param {function} templateEngine
* @param {function} schemaValidator
*/
constructor(signatures, templateEngine, schemaValidator) {
this._schemaValidator = (schemaValidator) ? new schemaValidator() : null;
this._templateEngine = (typeof templateEngine === 'object') ? templateEngine : null;
this._signatures = this._transformToObject(signatures);
this._elements = {};
}
/**
* transform signatures array to {name: signature} object
* @param {ElementSignature[]} signatures
* @returns {object}
* @private
*/
_transformToObject(signatures) {
let obj = {};
signatures.forEach((signature) => {
obj[signature.name] = signature;
});
return obj;
}
/**
* checks if element exists
* @param {ElementSignature.<name>} name
* @return {boolean}
* @private
*/
_elementExists(name) {
return (typeof this._elements[name] === 'object');
}
/**
* get registered element
* @param {ElementSignature.<name>} name
* @return {object|null}
*/
getElement(name) {
return (typeof this._elements[name] === 'object') ? this._elements[name] : null;
}
/**
* get signature
* @param {ElementSignature.<name>} name
* @return {object|null}
*/
getSignature(name) {
return (typeof this._signatures[name] === 'object') ? this._signatures[name] : null;
}
/**
* remove signature from registry
* @param {ElementSignature.<name>} name
* @return {ElementBuilder}
*/
removeSignature(name) {
if(this._signatureExists(name)) {
delete this._signatures[name];
}
return this;
}
/**
* checks if signature exists
* @param {ElementSignature.<name>} name
* @return {boolean}
* @private
*/
_signatureExists(name) {
return (typeof this._signatures[name] === 'object');
}
/**
* checks if signature is currently loading
* @param {ElementSignature.<name>} name
* @return {boolean}
*/
isBusySignature(name) {
let signature = this.getSignature(name);
return !!(signature && signature.busy);
}
/**
* sets busy flag to
* @param {ElementSignature.<name>} name
* @return {ElementBuilder}
*/
setBusySignature(name) {
let signature = this.getSignature(name);
if(signature) {
this._signatures[name].busy = true;
}
return this;
}
/**
* get template element HTMLElement Node
* @param {string|function} template - Template string or render function
* @param {*} data - data
* @return {Node}
*/
getTemplateElement(template, data) {
const templateElement = document.createElement('template');
templateElement.innerHTML = (
this._templateEngine &&
typeof this._templateEngine === 'object' &&
typeof this._templateEngine.render === 'function'
)
? this._templateEngine.render(template, data)
: template;
return templateElement.content;
}
/**
* get schema reference
* @param {ElementSignature.<name>} name
* @return {object}
*/
getSchema(name) {
const element = this.getElement(name);
return (element) ? element.schema : null;
}
/**
* validate data against schema
* @param {ElementSignature.<name>} elementName
* @param {*} data
* @return {boolean}
* @private
*/
_validate(elementName, data) {
const schema = this.getSchema(elementName);
if(
this._schemaValidator &&
schema
) {
return this._schemaValidator.validate(schema, data);
} else {
return true;
}
}
/**
* adds element to registry
* @param {ElementSignature.<name>} name
* @param {object} schema
* @param {string} template
* @param {ElementAbstract} module
* @return {ElementBuilder}
*/
addElement(name, schema, template, module) {
const templateView = (
this._templateEngine &&
typeof this._templateEngine === 'object'
)
? this._templateEngine.createView(template)
: template;
this._elements[name] = {
schema,
template: templateView,
module,
};
return this;
}
/**
* loads dependency creates element by name and data
* @param {ElementSignature.<name>} name
* @param {*} data
* @return {Promise}
*/
async create(name, data) {
if(this._elementExists(name)) {
if(this._validate(name, data)) {
const element = this.getElement(name, data);
const elementInstance = new element.module(
data,
this.getTemplateElement(element.template, data)
);
return elementInstance.create();
} else {
throw new Error(`Create Element ${name} failed. Given data do not match given schema.`);
}
} else if(
this._signatureExists(name) &&
!this.isBusySignature(name)
) {
const signature = this.getSignature(name);
this.setBusySignature(name);
return await Promise.all([
signature.schemaImport(),
signature.templateImport(),
signature.elementImport(),
])
.then((imports) => {
this.addElement(name, imports[0], imports[1], imports[2]);
if(this._elementExists(name)) {
this.removeSignature(name);
return this.create(name, data);
} else {
throw new Error(`Unfortunately Element ${name} could not have been instanciated.`);
}
})
.catch((err) => {
throw new Error(`Unfortunately Element ${name} could not have been instanciated. ${err}`);
});
} else if(
this._signatureExists(name) &&
this.isBusySignature(name)
) {
return new Promise((resolve) => {
window.setTimeout(() => {
resolve(this.create(name, data));
}, 100);
});
} else {
throw new Error(`Element ${name} is not have registered.`);
}
}
}
export {
ElementBuilder,
};