Source: dom.js

var bb = bb || {};

/**
 * DOM helpers
 * @namespace bb.dom
 */
bb.dom = new bb.feature({

	/**
	 * Creates and returns a new debounced version of the passed function which will postpone its execution until after {wait} milliseconds have elapsed since the last time it was invoked. 
	 * @param {function}  - Executable function to postpone
	 * @param {number} wait - How much time to postpone execution. Omit to execute immediately.
	 * @return {function} - Returns a new debounced version of the passed function
	 * @member bb.dom
	 */
	debounce: function (func, wait) { 
		var timeout, self = this;
		return function () {
			var context = self, args = arguments;
			if (wait == null) {
				return func.apply(context, args);
			}
			if (!timeout) {
				timeout = setTimeout(function() {
					timeout = null;
					func.apply(context, args);
				}, wait);
			}
			return timeout;
		};
	},


	/**
	 * Finds child element matching provided selector
	 * @param {string} selector - Selector has limitations based on the browser support.
	 * @param {boolean} all - Flag to find all matching elements. Otherwise fist found element is returned.
	 * @return {element|array|undefined} - Found element or array of elements 
	 */
	find: function(selector, all) {
		if (!this.querySelector) {bb.warn('Find should be used with DOM elements'); return;}
		return all && this.querySelectorAll(selector) || this.querySelector(selector);
	},

	/**
	 * Clear node
	 * @param {element=} element - Optional element to clear. Otherwise current execution context will be used.
	 */ 
	empty: function (element) {
		element = element || this;
		while (element.firstChild && element.firstChild.parentNode) { element.firstChild.parentNode.removeChild(element.firstChild); }
	},

	/**
	 * Fire an event
	 * @param {string} name - Event name to fire
	 * @param {object=} params - Details to attach
	 * @return {object} - Returns created event
	 */ 
	fire: function(name, params) {
		var event = new CustomEvent(name, {detail: params});
		this.dispatchEvent(event);
		return event;
	},

	/**
	 * Add event listener
	 * @param {string} name - Name of the event to handle
	 * @param {function} fn - Event handler
	 */
	listen: function(name, fn) { // Can't use "on" becuase it already booked by component
		this.addEventListener(name, fn);
	},

	/**
	 * Set or get attribute of an element
	 * @param {string} name - Name of the attribute
	 * @param {string=} value - Value of the attribute
	 */
	attr: function(name, value) {
		if (value === undefined) {
			return this.getAttribute(name);
		}
		this.setAttribute(name, value);
	},

	/**
	 * Get the HTML contents of the element or set the HTML contents
	 * @param {string=} str - HTML contents 
	 */
	html: function(str, undefined) {
		if (str === undefined) {
			return this.innerHTML;
		}
		this.innerHTML = str;
	},

	/**
	 * Creates a new HTMLElement with provided contents
	 * @param {string} html - HTML contents
	 * @param {string=} tag - Optional tag of the element to create
	 */
	parse: function(html, tag) {
		var el = document.createElement(tag || 'div');
		el.innerHTML = html;
		return el;
	}
});