blazy-8.x-2.x-dev/js/src/plugin/blazy.dom.js
js/src/plugin/blazy.dom.js
/** * @file * Provides CSS DOM methods which can replaced by Cash, or alike when available. * * Warning! Do not call or use any of the internal methods except for internal * usages. This file is separated to be removed when Cash is available, and * adoptable, or when core has one. * * @internal * This is an internal part of the Blazy system and should only be used by * blazy-related code in Blazy module, or its sub-modules. * This file is an experiment, and subject to removal when Cash lands, or * similar vanilla alternative is available at core. The rule is don't load * anything unless required by the page. Another reason for components. * It is extending dBlazy as a separate plugin to mimick jQuery CSS method. * * @todo https://caniuse.com/dom-manip-convenience * Includes: ChildNode.before, ChildNode.after, ChildNode.replaceWith, * ParentNode.prepend, and ParentNode.append. */ (function ($, _win, _doc) { 'use strict'; var PROTO_SOME = Array.prototype.some; var ADD = 'add'; var REMOVE = 'remove'; var CLASS = 'class'; var WIDTH = 'width'; var HEIGHT = 'height'; var AFTER = 'after'; var BEFORE = 'before'; var BEGIN = 'begin'; var END = 'end'; var U_TOP = 'Top'; var U_LEFT = 'Left'; var U_HEIGHT = 'Height'; var U_WIDTH = 'Width'; var SCROLL = 'scroll'; function css(els, props, vals) { var me = this; var _undefined = $.isUnd(vals); var _obj = $.isObj(props); var _getter = !_obj && _undefined; // Getter. if (_getter && $.isStr(props)) { // @todo figure out multi-element getters. Ok for now, as hardly multiple. var el = $.toElm(els); // @todo re-check common integer. var arr = [WIDTH, HEIGHT, 'top', 'right', 'bottom', 'left']; var result = $.computeStyle(el, props); var num = $.toInt(result, 0); return arr.indexOf(props) === -1 ? result : num; } var chainCallback = function (el) { if (!$.isElm(el)) { return _getter ? '' : me; } var setVal = function (val, prop) { // Setter. if ($.isFun(val)) { val = val(); } if ($.contains(prop, '-') || $.isVar(prop)) { prop = $.camelCase(prop); } el.style[prop] = $.isStr(val) ? val : val + 'px'; }; // Passing a key-value pair object means setting multiple attributes once. if (_obj) { $.each(props, setVal); } // Since a css value null makes no sense, assumes nullify. else if ($.isNull(vals)) { $.each($.toArray(props), function (prop) { el.style.removeProperty(prop); }); } else { // Else a setter. if ($.isStr(props)) { setVal(vals, props); } } }; return $.chain(els, chainCallback); } function offset(el) { var rect = $.rect(el); return { top: (rect.top || 0) + _doc.body[SCROLL + U_TOP], left: (rect.left || 0) + _doc.body[SCROLL + U_LEFT] }; } function width(el, val) { return css(el, WIDTH, val); } function height(el, val) { return css(el, HEIGHT, val); } function outerDim(el, withMargin, prop) { var result = 0; if ($.isElm(el)) { result = el['offset' + prop]; if (withMargin) { var style = $.computeStyle(el); var margin = function (pos) { return $.toInt(style['margin' + pos], 0); }; if (prop === U_HEIGHT) { result += margin(U_TOP) + margin('Bottom'); } else { result += margin(U_LEFT) + margin('Right'); } } } return result; } function outerWidth(el, withMargin) { return outerDim(el, withMargin, U_WIDTH); } function outerHeight(el, withMargin) { return outerDim(el, withMargin, U_HEIGHT); } /** * Insert Element or string into a position relative to a target element. * * To minimize confusions with native insertAdjacent[Element|HTML]. * * <!-- beforebegin --> * <p> * <!-- afterbegin --> * foo * <!-- beforeend --> * </p> * <!-- afterend --> * * @param {Element} target * The target Element. * @param {Element|string} el * The element or string to insert. * @param {string} position * The position or placement. * * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML */ function insert(target, el, position) { // @todo recheck DocumentFragment if needed. if ($.isElm(target)) { var suffix = $.isElm(el) ? 'Element' : 'HTML'; target['insertAdjacent' + suffix](position, el); } } function after(target, el) { insert(target, el, AFTER + END); } // Node.insertBefore(), similar to beforebegin, with different arguments. function before(target, el) { insert(target, el, BEFORE + BEGIN); } // Node.appendChild(), same effect as beforeend. function append(target, el) { if ($.isElm(target)) { if ($.isElm(el)) { target.appendChild(el); } else { insert(target, el, BEFORE + END); } } } function prepend(target, el) { insert(target, el, AFTER + BEGIN); } function clone(els, deep) { if ($.isUnd(deep)) { deep = true; } var chainCallback = function (el) { return $.isElm(el) && el.cloneNode(deep); }; return $.chain(els, chainCallback); } // @todo refactor and remove after migration: $.css = css; $.offset = offset; $.clone = clone; $.after = after; $.before = before; $.append = append; $.prepend = prepend; $.width = width; $.height = height; $.outerWidth = outerWidth; $.outerHeight = outerHeight; var objs = { // @todo multiple css values once. css: function (prop, val) { return css(this, prop, val); }, hasAttr: function (name) { var me = this; return PROTO_SOME.call(me, function (el) { return $.hasAttr(el, name); }); }, attr: function (attr, defValue, withDefault) { var me = this; if ($.isNull(defValue)) { return me.removeAttr(attr, withDefault); } return $.attr(me, attr, defValue, withDefault); }, removeAttr: function (attr, prefix) { return $.removeAttr(this, attr, prefix); }, hasClass: function (name) { var me = this; return PROTO_SOME.call(me, function (el) { return $.hasClass(el, name); }); }, toggleClass: function (name, op) { return $.toggleClass(this, name, op); }, addClass: function (name) { return this.toggleClass(name, ADD); }, removeClass: function (name) { var me = this; return arguments.length ? me.toggleClass(name, REMOVE) : me.attr(CLASS, ''); }, empty: function () { return $.empty(this); }, first: function (el) { return $.isUnd(el) ? this[0] : el; }, after: function (el) { return after(this[0], el); }, before: function (el) { return before(this[0], el); }, append: function (el) { return append(this[0], el); }, prepend: function (el) { return prepend(this[0], el); }, remove: function () { this.each($.remove); }, closest: function (selector) { return $.closest(this[0], selector); }, equal: function (selector) { return $.equal(this[0], selector); }, find: function (selector, asArray) { return $.find(this[0], selector, asArray); }, findAll: function (selector) { return $.findAll(this[0], selector); // @todo multiple sources for multiple targets. // return this.each(function (el) { // els.push(findAll(el, selector)); // }); }, clone: function (deep) { return clone(this, deep); }, computeStyle: function (prop) { return $.computeStyle(this[0], prop); }, offset: function () { return offset(this[0]); }, parent: function (selector) { return $.parent(this[0], selector); }, prev: function (selector) { return $.prev(this[0], selector); }, next: function (selector) { return $.next(this[0], selector); }, index: function (parents) { return $.index(this[0], parents); }, width: function (val) { return width(this[0], val); }, height: function (val) { return height(this[0], val); }, outerWidth: function (withMargin) { return outerWidth(this[0], withMargin); }, outerHeight: function (withMargin) { return outerHeight(this[0], withMargin); }, on: function (eventName, selector, cb, params, isCustom) { return $.on(this, eventName, selector, cb, params, isCustom, ADD); }, off: function (eventName, selector, cb, params, isCustom) { return $.off(this, eventName, selector, cb, params, isCustom, REMOVE); }, one: function (eventName, cb, isCustom) { return $.one(this, eventName, cb, isCustom); }, trigger: function (eventName, details, param) { return $.trigger(this, eventName, details, param); } }; // Merge prototypes. $.fn.extend(objs); })(dBlazy, this, this.document);