Reference Source

js/base/component/dom.js

// NPM IMPORTS
// import assert from 'assert'
// import _ from 'lodash'

// COMMON IMPORTS
import T         from 'devapt-core-common/dist/js/utils/types'
import uid       from 'devapt-core-common/dist/js/utils/uid.js'
import Stateable from 'devapt-core-common/dist/js/base/stateable'

// BROWSER IMPORTS
import Rendering from './rendering'


const context = 'browser/base/component/dom'



/**
 * @file UI component class.
 * 
 * @author Luc BORIES
 * 
 * @license Apache-2.0
 */
export default class Dom extends Stateable
{
	/**
	 * Creates an instance of Component.
	 * @extends Stateable
	 * 
	 * 	API
	 * 		->get_name():string - Get name.
	 * 		->get_dom_id():string - Get DOM id.
	 * 		
	 * 		->has_dom_element():boolean - Test DOM Element instance.
	 * 		->get_dom_element():Element - Get DOM element.
	 * 		->set_dom_element(arg_element):nothing -Set DOM element.
	 * 
	 * 		->has_dom_parent(arg_element):boolean - Test if component element has given parent element.
	 * 		->get_dom_parent():Element - Get parent element.
	 * 		->set_dom_parent(arg_parent_element):nothing - Set given parent element.
	 * 		->set_dom_parent_of(arg_component):nothing - Set given parent element.
	 * 
	 * 		->get_dom_attr(arg_attr_name):string - DOM element manipulation:get dom element attribute.
	 * 		->set_dom_attr(arg_attr_name, arg_attr_value):nothing - DOM element manipulation:set dom element attribute value.
	 * 
	 * 		->get_dom_text():string - DOM element manipulation:get dom element text.
	 * 		->set_dom_text(arg_text_value):nothing - DOM element manipulation:set dom element text value.
	 * 		->clear_dom_text():nothing - DOM element manipulation:clear dom element text value.
	 * 
	 * 		->get_dom_value():string - DOM element manipulation:get dom element value.
	 * 		->set_dom_value(arg_value):nothing - DOM element manipulation:set dom element value.
	 * 		->clear_dom_value():nothing - DOM element manipulation:clear dom element value.
	 * 
	 * 		->on_dom_event(arg_dom_event, arg_dom_selector, arg_handler, arg_data=undefined, arg_debug=true):nothing - Mount dom event handler with delegator.
	 * 		
	 * 		->has_dom_vnode():boolean - Test DOM Virtual Node.
	 * 		->get_dom_vnode():VNode - Get DOM Virtual Node.
	 * 		->set_dom_vnode(arg_vnode):nothing - Set DOM Virtual Node.
	 * 
	 * 		->is_visible(arg_check=false):boolean - Get visibility.
	 * 		->show():nothing - Show component.
	 * 		->hide():nothing - Hide component.
	 * 
	 * @param {RuntimeBase} arg_runtime - client runtime.
	 * @param {Immutable.Map} arg_state - component initial state.
	 * @param {string} arg_log_context - context of traces of this instance (optional).
	 * 
	 * @returns {nothing}
	 */
	constructor(arg_runtime, arg_state, arg_log_context)
	{
		const log_context = arg_log_context ? arg_log_context : context
		const default_settings = {}
		super(default_settings, arg_runtime, arg_state, log_context, arg_runtime.get_logger_manager())
		
		this.is_dom   = true

		// SET NAME
		this._name = arg_state.get('name', undefined)
		if (!this._name)
		{
			this._name = 'component_' + uid()
		}

		this._is_visible = true
		this._visiblility = undefined
		
		this._rendering = new Rendering(this, arg_state.get('dom_id', this._name))
	}
	
	
	
	/**
	 * Get name.
	 * 
	 * @returns {string} - component name.
	 */
	get_name()
	{
		return this._name
	}
	
	
	
	/**
	 * Get DOM id.
	 * 
	 * @returns {string} - component DOM id.
	 */
	get_dom_id()
	{
		return this._rendering.get_dom_id()
	}
	
	
	
	/**
	 * Test DOM Element instance.
	 * 
	 * @returns {boolean}
	 */
	has_dom_element()
	{
		return this._rendering.has_dom_element()
	}
	
	
	
	/**
	 * Get DOM element.
	 * 
	 * @returns {Element}
	 */
	get_dom_element()
	{
		return this._rendering.get_dom_element()
	}
	
	
	
	/**
	 * Set DOM element.
	 * 
	 * @param {Element} arg_element - element instance.
	 * 
	 * @returns {nothing}
	 */
	set_dom_element(arg_element)
	{
		this._rendering.set_dom_element(arg_element)
	}



	/**
	 * Test if component element has given parent element.
	 * 
	 * @param {Element} arg_element - parent element to test.
	 * 
	 * @returns {boolean}
	 */
	has_dom_parent(arg_element)
	{
		const dom_elem = this.get_dom_element()
		if (!arg_element)
		{
			return !! dom_elem.parentElement
		}
		return dom_elem && dom_elem.parentElement == arg_element
	}
	


	/**
	 * Get parent element.
	 * 
	 * @returns {Element} - parent element.
	 */
	get_dom_parent()
	{
		const dom_elem = this.get_dom_element()
		return dom_elem ? dom_elem.parentElement : undefined
	}



	/**
	 * Set given parent element.
	 * 
	 * @param {Element} arg_parent_element - parent element.
	 * 
	 * @returns {nothing}
	 */
	set_dom_parent(arg_parent_element)
	{
		const dom_elem = this.get_dom_element()
		if (dom_elem && arg_parent_element)
		{
			arg_parent_element.appendChild(dom_elem)
		}
	}



	/**
	 * Set given parent element.
	 * 
	 * @param {Component} arg_component - parent element component.
	 * 
	 * @returns {nothing}
	 */
	set_dom_parent_of(arg_component)
	{
		const dom_elem = this.get_dom_element()
		if (dom_elem && arg_component && arg_component.has_dom_parent())
		{
			arg_component.get_dom_parent().appendChild(dom_elem)
		}
	}



	/**
	 * DOM element manipulation:get dom element attribute.
	 * 
	 * @param {string} arg_attr_name - dom attribute name.
	 * 
	 * @returns {string}
	 */
	get_dom_attr(arg_attr_name)
	{
		const dom_elem = this.get_dom_vnode()
		if (! dom_elem)
		{
			return undefined
		}

		return dom_elem.getAttribute ? dom_elem.getAttribute(arg_attr_name) : undefined
	}



	/**
	 * DOM element manipulation:set dom element attribute value.
	 * 
	 * @param {string} arg_attr_name - dom attribute name.
	 * @param {string} arg_attr_value - dom attribute value.
	 * 
	 * @returns {nothing}
	 */
	set_dom_attr(arg_attr_name, arg_attr_value)
	{
		const dom_elem = this.get_dom_vnode()
		if (! dom_elem)
		{
			return
		}

		if (dom_elem.setAttribute)
		{
			dom_elem.setAttribute(arg_attr_name, arg_attr_value)
		}
	}



	/**
	 * DOM element manipulation:get dom element text.
	 * 
	 * @returns {string}
	 */
	get_dom_text()
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem)
		{
			return undefined
		}

		return this._get_dom_text(dom_elem)
	}



	/**
	 * DOM element manipulation:set dom element text value.
	 * 
	 * @param {string} arg_text_value - dom text value.
	 * 
	 * @returns {nothing}
	 */
	set_dom_text(arg_text_value)
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem)
		{
			return
		}

		const node_type = dom_elem.nodeType

		if ( node_type === 1 || node_type === 9 || node_type === 11 )
		{
			dom_elem.textContent = '' + arg_text_value
		}
	}



	/**
	 * DOM element manipulation:clear dom element text value.
	 * 
	 * @returns {nothing}
	 */
	clear_dom_text()
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem)
		{
			return
		}

		const node_type = dom_elem.nodeType

		if ( node_type === 1 || node_type === 9 || node_type === 11 )
		{
			dom_elem.textContent = ''
		}
	}



	_get_dom_text(arg_element)
	{
		const node_type = arg_element.nodeType

		if ( node_type === 1 || node_type === 9 || node_type === 11 )
		{
			if ( T.isString(arg_element.type) && arg_element.type == 'textarea' && T.isString(arg_element.value) )
			{
				return arg_element.value
			}
			
			if ( T.isString(arg_element.textContent) )
			{
				return arg_element.textContent
			}

			// LOOP ON CHILDREN
			let child = arg_element.firstChild
			let str = ''
			while(child)
			{
				str += this._get_dom_text(child)
				child = child.nextSibling
			}

			return str
		}

		if ( node_type === 3 || node_type === 4 )
		{
			return arg_element.node_value
		}

		return undefined
	}



	/**
	 * DOM element manipulation:get dom element value.
	 * 
	 * @returns {string}
	 */
	get_dom_value()
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem || ! ('value' in dom_elem) )
		{
			return undefined
		}

		return dom_elem.value
	}



	/**
	 * DOM element manipulation:set dom element value.
	 * 
	 * @param {string} arg_value - dom value.
	 * 
	 * @returns {nothing}
	 */
	set_dom_value(arg_value)
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem || ! ('value' in dom_elem) )
		{
			return
		}

		dom_elem.value = arg_value
	}



	/**
	 * DOM element manipulation:clear dom element value.
	 * 
	 * @returns {nothing}
	 */
	clear_dom_value()
	{
		const dom_elem = this.get_dom_element()
		if (! dom_elem || ! ('value' in dom_elem) )
		{
			return
		}

		dom_elem.value = ''
	}
	
	
	
	/**
	 * Mount dom event handler.
	 * 
	 * @{string}   arg_dom_event - dom event name.
	 * @{string}   arg_dom_selector - dom selector string ('tag_name.class1.class2').
	 * @{function} arg_handler - handler function f(component, event name, selection, event, target).
	 * @{any}      arg_data - handler datas, default undefined (optional).
	 * @{boolean}  arg_debug - trace flag, default true (optional).
	 * 
	 * @returns {nothing}
	 */
	on_dom_event(arg_dom_event, arg_dom_selector, arg_handler, arg_data=undefined, arg_debug=true)
	{
		this._rendering.on_dom_event(arg_dom_event, arg_dom_selector, arg_handler, arg_data, arg_debug)
	}
	
	
	
	/**
	 * Test DOM Virtual Node.
	 * 
	 * @returns {boolean}
	 */
	has_dom_vnode()
	{
		return this._rendering.has_dom_vnode()
	}
	
	
	
	/**
	 * Get DOM Virtual Node.
	 * 
	 * @returns {VNode}
	 */
	get_dom_vnode()
	{
		return this._rendering.get_dom_vnode()
	}
	
	
	
	/**
	 * Set DOM Virtual Node.
	 * 
	 * @param {VNode} arg_vnode - VNode instance.
	 * 
	 * @returns {nothing}
	 */
	set_dom_vnode(arg_vnode)
	{
		this._rendering.set_dom_vnode(arg_vnode)
	}



	/**
	 * Get visibility.
	 * 
	 * @param {boolean} arg_check - if true, check style display value.
	 * 
	 * @returns {boolean}
	 */
	is_visible(arg_check=false)
	{
		if (arg_check)
		{
			const dom_elem = this.get_dom_element()
			this._is_visible = dom_elem && dom_elem.style.display != 'none'
		}
		return this._is_visible
	}



	/**
	 * Show component.
	 * 
	 * @returns {nothing}
	 */
	show()
	{
		const dom_elem = this.get_dom_element()
		if (dom_elem)
		{
			dom_elem.style.display = this._visiblility ? this._visiblility : 'block'
			
			// console.log(context + ':show:this._visiblility=%s, dom_elem.style.display=%s', this._visiblility, dom_elem.style.display)

			this._is_visible = true
		}
	}



	/**
	 * Hide component.
	 * 
	 * @returns {nothing}
	 */
	hide()
	{
		const dom_elem = this.get_dom_element()
		if (dom_elem)
		{
			this._visiblility = dom_elem.style.display == 'none' ? undefined : dom_elem.style.display
			dom_elem.style.display = 'none'
			this._is_visible = false
		}
	}



	/**
	 * Toggle component visbility.
	 * 
	 * @returns {nothing}
	 */
	toggle()
	{
		const dom_elem = this.get_dom_element()
		if (dom_elem)
		{
			if (this._is_visible)
			{
				this.hide()
			} else {
				this.show()
			}
		}
	}
}