core.js

import Binding from "./binding.js"

/**
 * @global
 */
class Core {

	/**
	 * @readonly
	 */
	static PROPERTIES = [
		"tagName",
		"children",
		"identifier",
		"model",
		"binding",
		"properties"
	]

	/**
		* @readonly
		* @property Method {Method}
		* @property Method.APPEND_CHILD  {string}
		* @property Method.INSERT_BEFORE {string}
		* @property Method.REPLACE_NODE  {string}
		* @property Method.WRAP_NODE     {string}
		* @property Method.PREPEND       {string}
		* @example Core.Method.APPEND_CHILD
		*/
	static METHOD = {
		APPEND_CHILD: "APPEND_CHILD",
		INSERT_BEFORE: "INSERT_BEFORE",
		REPLACE_NODE: "REPLACE_NODE",
		WRAP_NODE: "WRAP_NODE",
		PREPEND: "PREPEND",
	}

	/**
		* @param {Object}        model
		* @param {Object}        properties
		* @param {Element}       properties.parentNode
		* @param {Binding}       [properties.binding=new Binding()]
		* @param {Method}        [properties.method=Core.METHOD.APPEND_CHILD]
		* @example Core.run(Model, { parentNode: document.body, binding: new Binding() })
		*/
	static run(model, { parentNode, binding = new Binding(), method = Core.METHOD.APPEND_CHILD } = {}) {
		const node = Core.createNode(parentNode, model, binding)
		binding._root = node
		binding._model = model
		for (const name of Object.getOwnPropertyNames(Object.getPrototypeOf(binding.eventListener)).filter(name => name !== "constructor" && typeof binding.eventListener[name] === "function")) {
			binding.listen(binding.eventListener.observable, name, binding.eventListener[name].bind(binding), true)
		}
		binding.onCreated()
		if (method === Core.METHOD.APPEND_CHILD) {
			parentNode.appendChild(node)
		} else if (method === Core.METHOD.INSERT_BEFORE) {
			parentNode.parentNode.insertBefore(node, parentNode)
		} else if (method === Core.METHOD.REPLACE_NODE) {
			parentNode.replaceWith(node)
		} else if (method === Core.METHOD.WRAP_NODE) {
			node.appendChild(parentNode.cloneNode(true))
			parentNode.replaceWith(node)
		} else if (method === Core.METHOD.PREPEND) {
			parentNode.prepend(node)
		}
		binding.onRendered()
	}

	/**
		* @ignore
		* @param   {Object} Node
		* @param   {Object} model
		* @param   {Object} Binding
		* @returns {Node}
		*/
	static createNode(parentNode, model, binding) {
		const { tagName, children = [] } = model
		let node
		if(tagName) {
			node = parentNode.ownerDocument.createElement(tagName)
		} else {
			node = parentNode.ownerDocument.createDocumentFragment()
		}
		Object.keys(model).filter(property => Core.PROPERTIES.includes(property) === false).forEach(function(property) {
			node[property] = model[property]
		})
		for (const child of children) {
			if(Object.prototype.hasOwnProperty.call(child, "model") === true) {
				let childBinding
				if(Object.prototype.hasOwnProperty.call(child, "binding") === true) {
					childBinding = new child.binding({...binding.properties, ...child.properties})
					if(Object.prototype.hasOwnProperty.call(child, "identifier") === true) {
						binding.identifier[child.identifier] = childBinding
					}
				}
				binding.run(child.model, { parentNode: node, binding: childBinding })
			} else {
				const childNode = Core.createNode(parentNode, child, binding)
				node.appendChild(childNode)
			}
		}
		if(Object.prototype.hasOwnProperty.call(model, "identifier") === true) {
			binding.identifier[model.identifier] = node
		}
		return node
	}

}

export default Core