UNPKG

libdom

Version:

Lean Browser Library for typical DOM operations

668 lines (523 loc) 15.9 kB
'use strict'; import { string, number, array, object, each, trim } from "libcore"; import { get as getModule } from "./chain.js"; import DETECTED from "./detect.js"; import { ERROR, stylize as stringStylize, addWord, removeWord } from "./string.js"; import { is, helper } from "./dom.js"; import { formatColor } from "./color.js"; var PADDING_BOTTOM = 'paddingBottom', PADDING_TOP = 'paddingTop', PADDING_LEFT = 'paddingLeft', PADDING_RIGHT = 'paddingRight', OFFSET_LEFT = 'offsetLeft', OFFSET_TOP = 'offsetTop', OFFSET_WIDTH = 'offsetWidth', OFFSET_HEIGHT = 'offsetHeight', CLIENT_WIDTH = 'clientWidth', CLIENT_HEIGHT = 'clientHeight', COLOR_RE = /[Cc]olor$/, //DIMENSION_RE = /width|height|(margin|padding).*|border.+(Width|Radius)/, EM_OR_PERCENT_RE = /%|em/, CSS_MEASUREMENT_RE = /^([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)(em|px|\%|pt|vh|vw|cm|ex|in|mm|pc|vmin)$/, WIDTH_RE = /width/i, NUMBER_RE = /\d/, BOX_RE = /(top|bottom|left|right|width|height)$/, DIMENSION_RE = /([Tt]op|[Bb]ottom|[Ll]eft|[Rr]ight|[wW]idth|[hH]eight|Size|Radius)$/, IE_ALPHA_OPACITY_RE = /\(opacity\=([0-9]+)\)/i, IE_ALPHA_OPACITY_TEMPLATE = 'alpha(opacity=$opacity)', IE_ALPHA_OPACITY_TEMPLATE_RE = /\$opacity/, GET_OPACITY = opacityNotSupported, SET_OPACITY = opacityNotSupported, SET_STYLE = styleManipulationNotSupported, GET_STYLE = styleManipulationNotSupported, COMPUTED_STYLE = computedStyleNotSupported, ERROR_INVALID_DOM = ERROR[1101], ERROR_INVALID_CLASSNAMES = ERROR[1113], DEFAULT_COLOR_UNIT = 'hex', SLICE = Array.prototype.slice; var CSS_INFO; function applyStyle() { /* jshint validthis: true */ return arguments.length > 1 ? // setter stylize.apply(this, arguments) : // getter stylify.apply(this, arguments); } function onStyleElement(value, name) { var isNumber = number(value), isScalar = isNumber || string(value), /* jshint validthis: true */ elementStyle = this[0], set = SET_STYLE, applied = false; name = stringStylize(name); // opacity if (name === 'opacity') { if (!isScalar) { // remove IE style opacity set(elementStyle, 'filter', null); } else { SET_OPACITY(elementStyle, value); applied = true; } } // dimension else if (isNumber && DIMENSION_RE.test(name)) { value = '' + value + 'px'; } // color else if (isNumber && COLOR_RE.test(name)) { value = formatColor(value, DEFAULT_COLOR_UNIT); } // non-scalar value is "unset" if (!isScalar) { value = null; } set(elementStyle, name, value); elementStyle = null; } function parseCSSText(str) { var trimString = trim, pairs = str.split(';'), c = -1, l = pairs.length, result = {}; var pair, name, value; for (; l--;) { pair = pairs[++c].split(':'); name = trimString(pair[0]); value = trimString(pair.slice(1).join(':')); if (name && value) { result[name] = value; } } return result; } function styleManipulationNotSupported() { throw new Error(ERROR[2001]); } /** * Style info */ function computedStyleNotSupported() { throw new Error(ERROR[2002]); } function w3cGetCurrentStyle(element, ruleNames) { var camel = stringStylize, isString = string; var style, c, l, name, value, values, access; if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } style = global.getComputedStyle(element); values = {}; if (!array(ruleNames)) { ruleNames = SLICE.call(arguments, 1); } for (c = -1, l = ruleNames.length; l--;) { name = ruleNames[++c]; if (isString(name)) { access = camel(name); switch (access) { case 'opacity': value = GET_OPACITY(style); break; default: value = style[access]; } values[name] = value; } } style = null; return values; } function ieGetCurrentStyle(element, ruleNames) { var dimensionRe = DIMENSION_RE, boxRe = BOX_RE, isString = string, camel = stringStylize, getOpacity = GET_OPACITY, pixelSize = ieGetPixelSize; var style, c, l, name, value, access, fontSize, values, dimension; if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } style = element.currentStyle; fontSize = false; dimension = false; values = {}; if (!array(ruleNames)) { ruleNames = SLICE.call(arguments, 1); } for (c = -1, l = ruleNames.length; l--;) { name = ruleNames[++c]; if (isString(name)) { access = camel(name); switch (true) { case access === 'opacity': value = getOpacity(style); break; case boxRe.test(access): if (!dimension) { dimension = ieGetPositionStyle(element, style); } value = dimension[access] + 'px'; break; case dimensionRe.test(access) && style[access] !== 'auto': if (fontSize === false) { fontSize = pixelSize(element, style, 'fontSize', null); } value = pixelSize(element, style, access, fontSize) + 'px'; break; case access === 'float': value = style.styleFloat; break; default: value = style[access]; } values[name] = value; } } style = value = null; return values; } function ieGetPixelSize(element, style, property, fontSize) { var sizeWithSuffix = style[property], size = parseFloat(sizeWithSuffix), suffix = sizeWithSuffix.split(NUMBER_RE)[0]; var parent; switch (suffix) { case 'in': return size * 96; case 'pt': return size * 96 / 72; case 'em': case '%': if (!fontSize) { parent = element.parentElement; fontSize = EM_OR_PERCENT_RE.test(suffix) && parent ? ieGetPixelSize(parent, parent.currentStyle, 'fontSize', null) : 16; parent = null; } return suffix === 'em' ? size * fontSize : size / 100 * (property === 'fontSize' ? fontSize : WIDTH_RE.test(property) ? element[CLIENT_WIDTH] : element[CLIENT_HEIGHT]); default: return size; } } function ieGetPositionStyle(element, style) { var parent = element.offsetParent, parentStyle = parent.currentStyle, ieAdjust = DETECTED.browser.ieVersion < 9, parse = parseFloat, ptop = PADDING_TOP, pleft = PADDING_LEFT, pbottom = PADDING_BOTTOM, pright = PADDING_RIGHT, cwidth = CLIENT_WIDTH, cheight = CLIENT_HEIGHT, left = element[OFFSET_LEFT], top = element[OFFSET_TOP], right = parent[cwidth] - element[OFFSET_WIDTH], bottom = parent[cheight] - element[OFFSET_HEIGHT], width = element[cwidth], height = element[cheight]; var node, nodeStyle; switch (style.position) { case 'relative': left -= (parse(parentStyle[pleft]) || 0); top -= (parse(parentStyle[ptop]) || 0); if (ieAdjust) { node = element.parentNode; for (; node !== parent; node = node.parentNode) { nodeStyle = node.currentStyle; if (nodeStyle.position === 'static') { left -= (parse(nodeStyle.paddingLeft) || 0) + (parse(nodeStyle.borderLeftWidth) || 0); top -= (parse(nodeStyle.paddingTop) || 0) + (parse(nodeStyle.borderTopWidth) || 0); } } if (parent === element.ownerDocument.body) { left -= parse(parentStyle.marginLeft) || 0; top -= parse(parentStyle.marginTop) || 0; } } /* falls through */ case 'absolute': case 'fixed': left -= (parse(parentStyle.borderLeftWidth) || 0); top -= (parse(parentStyle.borderTopWidth) || 0); } right -= left; bottom -= top; width -= (parse(style[pleft]) || 0) + (parse(style[pright]) || 0); height -= (parse(style[ptop]) || 0) + (parse(style[pbottom]) || 0); parent = parentStyle = null; return { left: left, top: top, right: right, bottom: bottom, width: width, height: height }; } /** * opacity */ function opacityNotSupported() { throw new Error(ERROR[2006]); } function ieGetOpacity(style) { var M = Math, opacityRe = IE_ALPHA_OPACITY_RE, filter = style.filter; var m; if (string(filter) && opacityRe.test(filter)) { m = filter.match(opacityRe); m = parseFloat(m[1]); return M.max(1, M.min(100, number(m) ? m : 100)) / 100; } return 1; } function ieSetOpacity(style, opacity) { var M = Math; if (string(opacity)) { opacity = parseFloat(opacity); } if (number(opacity)) { style.filter = IE_ALPHA_OPACITY_TEMPLATE. replace(IE_ALPHA_OPACITY_TEMPLATE_RE, M.min(100, M.max(0, M.round(opacity * 100) )).toString(10)); } } function w3cGetOpacity(style) { var opacity = parseFloat(style.opacity); return number(opacity) ? opacity : 1; } function w3cSetOpacity(style, opacity) { var M = Math; if (string(opacity)) { opacity = parseFloat(opacity); } if (number(opacity)) { style.opacity = M.min(1, M.max(0, opacity)).toFixed(2); } } /** * Style manipulation */ function w3cSetStyleValue(style, name, value) { if (value === null) { style.removeProperty(name); } else { style[name] = value; } } function w3cGetStyleValue(style, name) { return style.getPropertyValue(name); } function ieSetStyleValue(style, name, value) { if (value === null) { style.removeAttribute(name); } else { style[name] = value; } } function ieGetStyleValue(style, name) { return style.getAttribute(name); } /** * DOM Helpers */ // register DOM Helpers helper('className', addClass); helper('style', applyStyle); CSS_INFO = DETECTED && DETECTED.css; if (CSS_INFO) { COMPUTED_STYLE = CSS_INFO.w3cStyle ? w3cGetCurrentStyle : CSS_INFO.ieStyle ? ieGetCurrentStyle : computedStyleNotSupported; if (CSS_INFO.setattribute) { SET_STYLE = ieSetStyleValue; GET_STYLE = ieGetStyleValue; } else if (CSS_INFO.setproperty) { SET_STYLE = w3cSetStyleValue; GET_STYLE = w3cGetStyleValue; } if (CSS_INFO.opacity) { GET_OPACITY = w3cGetOpacity; SET_OPACITY = w3cSetOpacity; } else if (CSS_INFO.filterOpacity) { GET_OPACITY = ieGetOpacity; SET_OPACITY = ieSetOpacity; } if (CSS_INFO.alphaColor) { DEFAULT_COLOR_UNIT = 'rgba'; } } export { COMPUTED_STYLE as computedStyle, DEFAULT_COLOR_UNIT as colorUnit }; export { BOX_RE as boxRe, DIMENSION_RE as dimensionRe, COLOR_RE as colorRe }; export function addClass(element, classNames) { var isString = string; if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } if (isString(classNames)) { classNames = [classNames]; } if (!array(classNames)) { throw new Error(ERROR_INVALID_CLASSNAMES); } element.className = addWord(element.className || '', classNames); return getModule(); } export function removeClass(element, classNames) { var isString = string; if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } if (isString(classNames)) { classNames = [classNames]; } if (!array(classNames)) { throw new Error(ERROR_INVALID_CLASSNAMES); } element.className = removeWord(element.className, classNames); return getModule(); } export function stylize(element, rules, value) { var context; if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } if (string(rules)) { if (arguments.length > 2) { context = {}; context[rules] = value; rules = context; } else { rules = parseCSSText(rules); } } if (!object(rules)) { throw new Error(ERROR[1141]); } context = [element.style]; each(rules, onStyleElement, context, true); context = context[0] = null; return getModule(); } export function stylify(element) { if (!is(element, 1)) { throw new Error(ERROR_INVALID_DOM); } return parseCSSText(element.style.cssText); } export function validUnitValue(value) { // direct value test switch (value) { case null: case false: case 'auto': case 'inherit': return true; } // test type switch (typeof value) { case 'number': return isFinite(value); case 'string': return CSS_MEASUREMENT_RE.test(value); } return false; } export function unitValue(value) { var isFiniteNumber = isFinite; var len, matched; // direct value test switch (value) { case null: case false: case 'auto': case 'inherit': return value; } switch (typeof value) { case 'number': if (isFiniteNumber(value)) { return value; } break; case 'string': len = value.length; matched = value.match(CSS_MEASUREMENT_RE); if (matched) { // return as is if (matched[2] !== 'px') { return value; } value = matched[1]; } value = 1 * value; if (isFiniteNumber(value)) { return value; } } return false; }