UNPKG

qoopido.nucleus

Version:

Lightweight, atomic and modular JavaScript utility library that strongly encourages the creation and usage of small yet flexible, reusable and easily maintainable modules.

673 lines (545 loc) 17.4 kB
/** * @use /demand/validator/isObject * @use /demand/validator/isInstanceOf * @use /demand/validator/isTypeOf * @use /demand/function/uuid * @use /demand/function/iterate * * @require ./event * @require ../hooks/css * @require ../support/method */ (function(global, document) { 'use strict'; function definition(isObject, isInstanceOf, isTypeOf, generateUuid, iterate, Event, hooksCss, supportMethod) { var //shortcuts documentBody = document.body || document.getElementsByTagName('body')[0], arrayPrototypeConcat = Array.prototype.concat, arrayPrototypeSlice = Array.prototype.slice, objectDefineProperty = Object.defineProperty, objectDefineProperties = Object.defineProperties, head = document.getElementsByTagName('head')[0], // constants NULL = null, STRING_UNDEFINED = 'undefined', STRING_STRING = 'string', STRING_CONTENTATTRIBUTE = ('textContent' in document.createElement('a')) ? 'textContent' : 'innerText', STRING_MATCHES = supportMethod('matches', documentBody) || supportMethod('matchesSelector', documentBody), // regular expressions regexMatchTag = /^<(\w+)\s*\/>$/, regexMatchSpaces = / +/g, regexMatchChildSeclector = /^\s*^/, // methods previousSibling = (!isTypeOf(head.previousElementSibling, STRING_UNDEFINED)) ? function previousSibling() { return this.previousElementSibling; } : function previousSibling() { var element = this; while(element = element.previousSibling) { if(element.nodeType === 1 ) { return element; } } }, nextSibling = (!isTypeOf(head.nextElementSibling, STRING_UNDEFINED)) ? function nextSibling() { return this.nextElementSibling; } : function nextSibling() { var element = this; while(element = element.nextSibling) { if(element.nodeType === 1 ) { return element; } } }, // flags // general storage & objects listener = {}, supportsPassiveListener = false; (function() { var noop = function() {}, event = '' + (+new Date()), options; try { options = Object.defineProperty({}, 'passive', { get: function() { supportsPassiveListener = true; } }); global.addEventListener(event, noop, options); global.removeEventListener(event, noop, options); } catch(error) {} /* eslint-disable-line no-empty */ }()); function processOptions(options) { if(supportsPassiveListener || typeof options === 'boolean') { return options; } return options && options.capture; } function emitEvent(type, detail) { var self = this, event; event = document.createEvent('CustomEvent'); event.initCustomEvent(type, type !== 'load' && type !== 'resize', type !== 'load' && type !== 'resize', detail); self.node.dispatchEvent(event); } function resolveElement(element) { if(typeof element === 'string') { try { if(regexMatchTag.test(element)) { element = document.createElement(element.replace(regexMatchTag, '$1').toLowerCase()); } else { element = document.querySelector(element); } } catch(exception) { element = NULL; } } if(!element) { throw new Error('Element could not be resolved'); } return element; } function resolveArguments(parameters) { return arrayPrototypeConcat.apply([], arrayPrototypeSlice.call(parameters)).join(' ').split(regexMatchSpaces); } function matchesSelector(event, selector) { var i = 0, pointer; for(; pointer = event.path[i]; i++) { if(pointer[STRING_MATCHES] && pointer[STRING_MATCHES](selector)) { event.isDelegate = true; event.currentTarget = pointer; return true; } if(pointer === event.currentTarget) { break; } } return false; } function getSiblings(pointer, method, selector, limit, strict) { var multiple = !(limit && limit === 1), siblings = multiple ? [] : false; strict = multiple ? false : strict; while(pointer = method.call(pointer)) { if(pointer.nodeType === 1) { if(!selector || pointer[STRING_MATCHES](selector)) { if(multiple) { siblings.push(pointer); } else { return pointer; } } if(strict) { break; } } } return siblings; } function getParents(pointer, selector, limit, strict) { var multiple = !(limit && limit === 1), parents = multiple ? [] : false; strict = multiple ? false : strict; while(pointer = pointer.parentNode) { if(pointer.nodeType === 1) { if(!selector || pointer[STRING_MATCHES](selector)) { if(multiple) { parents.push(pointer); } else { return pointer; } } if(strict) { break; } } } return parents } function DomElement(element, attributes, styles) { var self = this, uuid; element = resolveElement(element); uuid = element.uuid; if(!uuid) { uuid = generateUuid(); listener[uuid] = {}; objectDefineProperty(element, 'uuid', { value: uuid }); } objectDefineProperties(self, { uuid: { value: uuid }, type: { value: element === global ? '#window' : element.nodeName }, node: { value: element } }); if(isObject(attributes)) { self.setAttributes(attributes); } if(isObject(styles)) { self.setStyles(styles); } return self; } DomElement.prototype = { /* only for reference uuid: NULL, type: NULL, node: NULL */ clone: function() { return new DomElement(this.node.cloneNode(true)); }, focus: function() { this.node.focus(); return this; }, blur: function() { this.node.blur(); return this; }, getPosition: function() { var bbox = this.node.getBoundingClientRect(); return { left: bbox.left + documentBody.scrollLeft, top: bbox.top + documentBody.scrollTop }; }, getOffset: function(viewport) { var node = this.node, bbox = viewport ? node.getBoundingClientRect() : null; return { left: viewport ? bbox.left : node.offsetLeft, top: viewport ? bbox.top : node.offsetTop }; }, getWidth: function(includeMargin) { var node = this.node, width = node.offsetWidth, style; if(includeMargin) { style = getComputedStyle(node); width += parseInt(style.marginLeft) + parseInt(style.marginRight); } return width; }, getHeight: function(includeMargin) { var node = this.node, height = node.offsetHeight, style; if(includeMargin) { style = getComputedStyle(node); height += parseInt(style.marginTop) + parseInt(style.marginBottom); } return height; }, getContent: function(getHtml) { var node = this.node; return getHtml ? node.innerHTML : node[STRING_CONTENTATTRIBUTE]; }, getAttribute: function(attribute) { var self = this; if(isTypeOf(attribute, STRING_STRING)) { return self.node.getAttribute(attribute); } }, getAttributes: function() { var self = this, result = {}, attributes = resolveArguments(arguments), i = 0, attribute; for(; attribute = attributes[i]; i++) { result[attribute] = self.node.getAttribute(attribute); } return result; }, getStyle: function(property) { var self = this; if(isTypeOf(property, STRING_STRING)) { return hooksCss.process('get', self.node, property); } }, getStyles: function() { var self = this, result = {}, properties = resolveArguments(arguments), i = 0, property; for(; property = properties[i]; i++) { result[property] = hooksCss.process('get', self.node, property); } return result; }, getSiblingBefore: function(selector, strict) { return getSiblings(this.node, previousSibling, selector, 1, strict); }, getSiblingAfter: function(selector, strict) { return getSiblings(this.node, nextSibling, selector, 1, strict); }, getSiblings: function(selector) { return this.getSiblingsBefore(selector).concat(this.getSiblingsAfter(selector)); }, getSiblingsBefore: function(selector) { return getSiblings(this.node, previousSibling, selector); }, getSiblingsAfter: function(selector) { return getSiblings(this.node, nextSibling, selector); }, getChildren: function(selector) { var self = this.node, uuid, matches, i, match; if(!selector) { matches = []; for(i = 0; match = self.childNodes[i]; i++) { if(match.nodeType === 1) { matches.push(match); } } } else if(regexMatchChildSeclector.test(selector)) { uuid = self.uuid; self.setAttribute('nucleus-uuid', uuid); selector = '[nucleus-uuid="' + uuid + '"] ' + selector; matches = arrayPrototypeSlice.call(self.parentNode.querySelectorAll(selector)); self.removeAttribute('nucleus-uuid'); } else { matches = arrayPrototypeSlice.call(self.querySelectorAll(selector)); } return matches; }, getParent: function(selector, strict) { return getParents(this.node, selector, 1, strict); }, getParents: function(selector) { return getParents(this.node, selector); }, hasChild: function(child) { var node = this.node; return node !== child && node.contains(child.node || child); }, hasClass: function(name) { return (name) ? (new RegExp('(?:^|\\s)' + name + '(?:\\s|$)')).test(this.node.className) : false; }, isVisible: function() { var self = this, node = self.node; return !((node.offsetWidth <= 0 && node.offsetHeight <= 0) || self.getStyle('visibility') === 'hidden' || self.getStyle('opacity') <= 0); }, setContent: function(source, isHtml) { var self = this, node = self.node; if(isHtml) { node.innerHTML = source; } else { node[STRING_CONTENTATTRIBUTE] = source; } return self; }, setAttribute: function(attribute, value) { var self = this; if(isTypeOf(attribute, STRING_STRING)) { self.node.setAttribute(attribute, value); } return self; }, setAttributes: function(attributes) { var self = this; iterate(attributes, function(attribute, value) { self.setAttribute(attribute, value); }); return self; }, removeAttribute: function(attribute) { var self = this; if(isTypeOf(attribute, STRING_STRING)) { self.node.removeAttribute(attribute); } return self; }, removeAttributes: function() { var self = this, attributes = resolveArguments(arguments), i = 0, attribute; for(; attribute = attributes[i]; i++) { self.removeAttribute(attribute); } return self; }, setStyle: function(property, value) { var self = this; if(isTypeOf(property, STRING_STRING)) { hooksCss.process('set', self.node, property, value); } return self; }, setStyles: function(properties) { var self = this; iterate(properties, function(property, value) { hooksCss.process('set', self.node, property, value); }); return self; }, removeStyle: function(property) { var self = this; if(isTypeOf(property, STRING_STRING)) { self.setStyle(property, ''); } return self; }, removeStyles: function() { var self = this, properties = resolveArguments(arguments), i = 0, property; for(; property = properties[i]; i++) { self.setStyle(property, ''); } return self; }, addClass: function(name) { var self = this; if(name && !self.hasClass(name)) { self.node.className += (self.node.className) ? ' ' + name : name; } return self; }, removeClass: function(name) { var self = this; if(name && self.hasClass(name)) { self.node.className = self.node.className.replace(new RegExp('(?:^|\\s)' + name + '(?!\\S)'), '').trim(); } return self; }, toggleClass: function(name) { var self = this; if(name) { self.hasClass(name) ? self.removeClass(name) : self.addClass(name); } return self; }, prepend: function(element) { var self = this, target = self.node; if(element) { try { element = (isInstanceOf(element, DomElement)) ? element.node : resolveElement(element); target.firstChild ? target.insertBefore(element, target.firstChild) : self.append(element); } catch(exception) { target.insertAdjacentHTML('afterBegin', element); } } return self; }, append: function(element) { var self = this, target = self.node; if(element) { try { target.appendChild((isInstanceOf(element, DomElement)) ? element.node : resolveElement(element)); } catch(exception) { target.insertAdjacentHTML('beforeEnd', element); } } return self; }, prependTo: function(target) { var self = this, node = self.node; if(target) { (target = target.node || resolveElement(target)).firstChild ? target.insertBefore(node, target.firstChild) : self.appendTo(target); } return self; }, appendTo: function(target) { var self = this; if(target) { (target.node || resolveElement(target)).appendChild(self.node); } return self; }, insertBefore: function(target) { var self = this, node = self.node; if(target) { (target = target.node || resolveElement(target)).parentNode.insertBefore(node, target); } return self; }, insertAfter: function(target) { var self = this, node = self.node; if(target) { (target = target.node || resolveElement(target)).nextSibling ? target.parentNode.insertBefore(node, target.nextSibling) : self.appendTo(target.parentNode); } return self; }, replace: function(target) { var self = this, node = self.node; if(target) { (target = target.node || resolveElement(target)).parentNode.replaceChild(node, target); } return self; }, replaceWith: function(element) { var self = this, target = self.node; if(element) { element = (isInstanceOf(element, DomElement)) ? element.node : resolveElement(element); target.parentNode.replaceChild(element, target); } return self; }, detach: function() { var self = this, node = self.node; node.parentNode && node.parentNode.removeChild(node); return self; }, on: function(events) { var self = this, selector = (arguments.length === 4 || typeof arguments[1] === 'string') ? arguments[1] : NULL, fn = (arguments.length === 4 || typeof arguments[2] === 'function') ? arguments[2] : arguments[1], options = processOptions((arguments.length > 3) ? arguments[3] : arguments[2]), uuid = fn.uuid || (fn.uuid = generateUuid()), i = 0, event; events = events.split(regexMatchSpaces); for(; (event = events[i]); i++) { var id = event + '-' + uuid, handler = function(event) { event = new Event(event); if(!event.isPropagationStopped) { event.uuid = generateUuid(); if(!selector || matchesSelector(event, selector)) { fn.call(event.currentTarget, event, event.originalEvent.detail); } } }; handler.type = event; listener[self.uuid][id] = handler; self.node.addEventListener(event, handler, options); } return self; }, one: function(events) { var self = this, selector = (arguments.length === 5 || typeof arguments[1] === 'string') ? arguments[1] : NULL, fn = (arguments.length === 5 || typeof arguments[2] === 'function') ? arguments[2] : arguments[1], options = processOptions((arguments.length > 3) ? arguments[3] : arguments[2]), each = ((arguments.length > 4) ? arguments[4] : arguments[3]) !== false, handler = function(event) { self.off(((each === true) ? event.type : events), handler, options); fn.call(this, event, event.originalEvent.detail); }; fn.uuid = handler.uuid = generateUuid(); if(selector) { self.on(events, selector, handler, options); } else { self.on(events, handler, options); } return self; }, off: function(events, fn, options) { var self = this, node = self.node, i = 0, event, id, handler; options = processOptions(options); events = events.split(' '); for(; event = events[i]; i++) { id = fn.uuid && event + '-' + fn.uuid || NULL; handler = id && listener[self.uuid][id] || NULL; if(handler) { node.removeEventListener(event, handler, options); delete listener[self.uuid][id]; } else { node.removeEventListener(event, fn, options); } } return self; }, emit: function(event, data) { var self = this; emitEvent.call(self, event, data); return self; } }; return DomElement; } provide([ '/demand/validator/isObject', '/demand/validator/isInstanceOf', '/demand/validator/isTypeOf', '/demand/function/uuid', '/demand/function/iterate', './event', '../hooks/css', '../support/method' ], definition); }(this, document));