constructors/custom-elements.js

// jshint -W083
// jshint -W002
// jshint -W088
// jshint -W014


((function () {
	if (!('_elements' in module.cache())) {
	  module.cache()._elements = {};
	}
	if (!('_elementsKeys' in module.cache())) {
	  module.cache()._elementsKeys = [];
	}
	if (!('_pendingList' in module.cache())) {
	  module.cache()._pendingList = {};
	}
	var target = document.body;
	var _elements = module.cache()._elements;
	var _elementsKeys   = module.cache()._elementsKeys;
	var _pendingList = module.cache()._pendingList;
	var trackNodes  = function (nodeList, cbName, attrs) {
		if (nodeList.length) {
			var i, tagName, er;
			for (i=0;i<nodeList.length;i++) {
				if (nodeList[i].nodeType === Node.ELEMENT_NODE) {
					tagName = nodeList[i].tagName.toLowerCase();
					if (_elementsKeys.indexOf(tagName) !== -1) {
						if (typeof(_elements[tagName][cbName]) === "function") {
							try {
								// console.log({tagName, cbName});
								_elements[tagName][cbName].apply(nodeList[i], []);
							} catch (er) {
								console.error(er);
							}
						}
					} else if (tagName in _pendingList) {
						if (typeof(_pendingList[tagName]) === "function") {
							try {
								((_pendingList[tagName])());
							} catch (er) {
								console.error(er);
							}
						}
						delete _pendingList[tagName];
					}

					var _found = false;
					if (cbName === "__onAttrChange" || cbName === "__onContentChange") {
						var _current = nodeList[i], _currentTagName = nodeList[i].tagName.toLowerCase();
						while (_current && !_found) {
							if (_elementsKeys.indexOf(_currentTagName) !== -1) {
								_found = true;
							}
							_current = _current.parentElement;
							if (_current) {
								_currentTagName = _current.tagName.toLowerCase()
							}
						}

						if (!_found) return;
					} else {
						_found = true;
					}

					if (_found) {
						// console.log({ cbName, nodes: nodeList[i].childNodes });
						trackNodes(nodeList[i].childNodes, cbName, attrs);
					}
				} else if (nodeList[i].nodeType === Node.DOCUMENT_NODE) {
					trackNodes(nodeList[i].childNodes, cbName, attrs);
				} else if (nodeList[i].nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
					trackNodes(nodeList[i].childNodes, cbName, attrs);
				}
			}
		}
	};
	var observer = new MutationObserver(function(mutations) {
		mutations.forEach(function(mutation) {
			var attrs = [];
			if (mutation.type === "childList") {
				trackNodes(mutation.addedNodes, "__onInit", attrs);
				trackNodes([mutation.target], "__onContentChange", attrs);
				trackNodes(mutation.removedNodes, "__onRemove", attrs);
			} else if (mutation.type === "attributes") {
				trackNodes([mutation.target], "__onContentChange", attrs);
				trackNodes([mutation.target], "__onAttrChange", [mutation.attributeName, mutation.oldValue]);
			}
			// console.log(mutation.type, mutation);
		});
	});
	// configuration of the observer:
	var config = { attributes: true, childList: true, subtree: true, attributeOldValue: true };

	// pass in the target node, as well as the observer options
	observer.observe(target, config);

	// later, you can stop observing
	// observer.disconnect();




  if (typeof(document.createElement('span').methods) !== "object") {
	/**
	 * @memberof HTMLElement#
	 * @var {Object<string,function>} methods
	 */;
	Object.defineProperty(HTMLElement.prototype, 'methods', {
		get	: function () {
			var tagName = this.tagName.toLowerCase();
			var node = this;
			var method = function (name) {
				return function () {
					return _elements[tagName][name].apply(node, arguments);
				};
			};
			var methods = {};
			if (tagName in _elements) {
				for (i in _elements[tagName]) {
					if (typeof(_elements[tagName][i]) === "function") {
						methods[i] = method(i);
					} else {
						;((function (name) {
						  Object.defineProperty(methods, name, {
							enumerable : true,
							configurable : true,
							get : function () {
							  return _elements[tagName][name];
							},
							set : function (v) {
							  _elements[tagName][name] = v;
							}
						  });
						})(i + ''));
					}
				}
			}
			return methods;
		},
		set	: function (methods) {
			var tagName = this.tagName.toLowerCase();
			if (methods && typeof(methods) === "object") {
				var i;
				if (!(tagName in _elements)) {
					_elements[tagName]  = {};
					_elementsKeys.push(tagName);
				}
				for (i in methods) {
					_elements[tagName][i]	= methods[i];
				}
			}
		},
		enumerable: true,
		configurable: false
	});
  }



	var _private = {
		registerMethods : function (nodeName, prototypeMethods) {
			if (
				nodeName && typeof(nodeName) === "string"
				&& prototypeMethods && typeof(prototypeMethods) === "object"
			) {
				var tagName = nodeName.toLowerCase();
				_elements[tagName] = prototypeMethods;
				_elementsKeys.push(tagName);
				_private._initCreatedNodes(tagName);
			}
		},
		_initCreatedNodes   : function (nodeName) {
			var tagName = nodeName.toLowerCase();
			if (typeof(_elements[tagName].__onInit) === "function") {
				var list = document.getElementsByTagName(nodeName);
				var er;
				for (var i = 0; i < list.length; i++ ) {
					try {
						_elements[tagName].__onInit.apply(list[i],  []);
					} catch(er) {
						console.error(er);
					}
				}
			}
		}
	};

	var _methods = {
		registerMethods : _private.registerMethods,
		lazyLoadModules : function (list) {
			var moduleName, tagName, cb;
			if (typeof(list) === "object" && list) {
				for (moduleName in list) {
					tagName = moduleName.toLowerCase();
					cb = undefined;
					if (typeof(list[tagName]) === "string") {
						cb  = ((function (moduleName) {
							return function () {
								Application.require(moduleName, function () {});
							};
						})(list[tagName] + ""));
					} else if (typeof(list[tagName]) === "function") {
						cb  = list[tagName];
					}
					if (typeof(cb) === "function") {
						if (document.getElementsByTagName(tagName).length) {
							try {
								cb();
							} catch (er) {
								console.error(er);
							}
						} else {
							_pendingList[tagName]   = cb;
						}
					}
				}
			}
		}
	};

	module.exports  = _methods;
})());