Source: dom.js

/**
 * HTML elements low-level handling.
 *
 * @module stb/dom
 * @author Stanislav Kalashnik <sk@infomir.eu>
 * @license GNU GENERAL PUBLIC LICENSE Version 3
 */

'use strict';

/* eslint no-unused-vars: 0 */

/**
 * DOM manipulation module
 */
var dom = {};


/**
 * Create a new HTML element.
 *
 * @param {string} tagName mandatory tag name
 * @param {Object|null} [attrList] element attributes
 * @param {...*} [content] element content (primitive value/values or other nodes)
 * @return {Node|null} HTML element or null on failure
 *
 * @example
 * dom.tag('table');
 * dom.tag('div', {}, 'some text');
 * dom.tag('div', {className:'top'}, dom.tag('span'), dom.tag('br'));
 * dom.tag('link', {rel:'stylesheet', type:'text/css', href:'http://some.url/'});
 */
dom.tag = function ( tagName, attrList, content ) {
	var node = null,
		i, name;

	// minimal param is given
	if ( tagName ) {
		// empty element
		node = document.createElement(tagName);

		// optional attribute list is given
		if ( attrList && typeof attrList === 'object' ) {
			for ( name in attrList ) {
				// extend a new node with the given attributes
				node[name] = attrList[name];
			}
		}

		// content (arguments except the first two)
		for ( i = 2; i < arguments.length; i++ ) {
			// some data is given
			if ( arguments[i] ) {
				// regular HTML tag or plain data
				node.appendChild(
					typeof arguments[i] === 'object' ?
					arguments[i] :
					document.createTextNode(arguments[i])
				);
			}
		}

	}

	return node;
};


/**
 * Create a new DocumentFragment filled with the given non-empty elements if any.
 *
 * @param {...*} [node] fragment content (primitive value/values or other nodes)
 * @return {DocumentFragment} new placeholder
 *
 * @example
 * // gives an empty fragment element
 * dom.fragment();
 * // gives a fragment element with 3 div element inside
 * dom.fragment(dom.tag('div'), div2, div3);
 * // mixed case
 * dom.fragment('some text', 123, div3);
 */
dom.fragment = function ( node ) {
	// prepare placeholder
	var i, fragment = document.createDocumentFragment();

	// walk through all the given elements
	for ( i = 0; i < arguments.length; i++ ) {
		node = arguments[i];
		// some data is given
		if ( node ) {
			// regular HTML tag or plain data
			fragment.appendChild(typeof node === 'object' ? node : document.createTextNode(node));
		}
	}

	return fragment;
};


/**
 * Add the given non-empty data (HTML element/text or list) to the destination element.
 *
 * @param {Node} tagDst element to receive children
 * @param {...*} [content] element content (primitive value/values or other nodes)
 * @return {Node|null} the destination element - owner of all added data
 *
 * @example
 * // simple text value
 * add(some_div, 'Hello world');
 * // single DOM Element
 * add(some_div, some_other_div);
 * // DOM Element list
 * add(some_div, div1, div2, div3);
 * // mixed case
 * add(some_div, div1, 'hello', 'world');
 */
dom.add = function ( tagDst, content ) {
	var i;

	// valid HTML tag as the destination
	if ( tagDst instanceof Node ) {
		// append all except the first one
		for ( i = 1; i < arguments.length; i++ ) {
			// some data is given
			if ( arguments[i] ) {
				// regular HTML tag or plain data
				tagDst.appendChild(
					typeof arguments[i] === 'object' ?
					arguments[i] :
					document.createTextNode(arguments[i])
				);
			}
		}
		return tagDst;
	}

	return null;
};


/**
 * Remove the given elements from the DOM.
 *
 * @param {...Node} [nodes] element to be removed
 * @return {boolean} operation status (true - all given elements removed)
 *
 * @example
 * dom.remove(document.querySelector('div.test'));
 * dom.remove(div1, div2, div3);
 */
dom.remove = function ( nodes ) {
	var count = 0,  // amount of successfully removed nodes
		i;

	// walk through all the given elements
	for ( i = 0; i < arguments.length; i++ ) {
		// valid non-empty tag
		if ( arguments[i] && arguments[i].parentNode ) {
			if ( arguments[i].parentNode.removeChild(arguments[i]) === arguments[i] ) {
				count++;
			}
		}
	}

	return arguments.length > 0 && count === arguments.length;
};


// public
module.exports = dom;