libdom
Version:
Lean Browser Library for typical DOM operations
790 lines (638 loc) • 21.7 kB
JavaScript
;
import {
string,
number,
object,
array
} from "libcore";
import { get as getModule } from "./chain.js";
import DETECTED from "./detect.js";
import { ERROR } from "./string.js";
import {
is as isDom,
isView,
contains,
documentViewAccess
} from "./dom.js";
import {
computedStyle,
stylize,
unitValue,
validUnitValue
} from "./css.js";
var ERROR_INVALID_ELEMENT = ERROR[1101],
ERROR_INVALID_DOM = ERROR[1102],
ERROR_INVALID_X = ERROR[1210], // Invalid Dimension [x] parameter.
ERROR_INVALID_Y = ERROR[1211], // Invalid Dimension [y] parameter
ERROR_INVALID_RIGHT = ERROR[1212], //Invalid Dimension [right] parameter.
ERROR_INVALID_BOTTOM = ERROR[1213], // Invalid Dimension [bottom] parameter
ERROR_INVALID_WIDTH = ERROR[1214], // Invalid Dimension [width] parameters
ERROR_INVALID_HEIGHT = ERROR[1215], // Invalid Dimension [height] parameters
ERROR_INVALID_PARAMETERS = ERROR[1216],
OFFSET_TOP = 'offsetTop',
OFFSET_LEFT = 'offsetLeft',
OFFSET_WIDTH = 'offsetWidth',
OFFSET_HEIGHT = 'offsetHeight',
MARGIN_TOP = 'marginTop',
MARGIN_LEFT = 'marginLeft',
SCROLL_TOP = 'scrollTop',
SCROLL_LEFT = 'scrollLeft',
BOUNDING_RECT = 'getBoundingClientRect',
DEFAULTVIEW = null,
ELEMENT_VIEW = 1,
PAGE_VIEW = 2,
USE_ZOOM_FACTOR = false,
IE_PAGE_STAT_ACCESS = 'documentElement',
boundingRect = false,
getPageScroll = null,
getOffset = null,
getSize = null,
//getBox = null,
getScreenSize = null;
var DIMENSION_INFO, IEVERSION;
function pageBox(dom) {
var M = Math,
subject = dom,
box = screen();
// page size
if (isView(subject)) {
subject = subject.document;
}
if (subject.nodeType === 9) {
subject = subject[IE_PAGE_STAT_ACCESS];
box[2] = M.max(subject.scrollWidth, box[2]);
box[3] = M.max(subject.scrollHeight, box[3]);
}
subject = null;
return box;
}
function w3cScreenSize(window) {
return [window.innerWidth, window.innerHeight];
}
function ieScreenSize(window) {
var factor = USE_ZOOM_FACTOR ? getZoomFactor(window) : 1,
subject = window.document[IE_PAGE_STAT_ACCESS],
size = [subject.clientWidth * factor,
subject.clientHeight * factor];
subject = null;
return size;
}
/**
* Element Size
*/
function rectSize(element, boundingRect) {
var M = Math,
rect = boundingRect || element[BOUNDING_RECT](),
size = [
M.max(0, rect.width || 0),
M.max(0, rect.height || 0)];
rect = null;
return size;
}
function manualSize(element) {
var M = Math;
return [
M.max(0, element[OFFSET_WIDTH] || 0),
M.max(0, element[OFFSET_HEIGHT] || 0)];
}
/**
* Element Offset
*/
function rectOffset(element, boundingRect) {
var //scrolled = getPageScroll(element.ownerDocument[DEFAULTVIEW]),
page = screen(element),
rect = boundingRect || element[BOUNDING_RECT](),
factor = DIMENSION_INFO.zoomfactor ?
getZoomFactor(global.window.document[IE_PAGE_STAT_ACCESS]) :
1,
scrollX = page[0],
scrollY = page[1],
x = rect.left * factor + scrollX,
y = rect.top * factor + scrollY,
offset = [
x,
y,
rect.right * factor - page[2],
rect.bottom * factor - page[3]];
rect = null;
return offset;
}
function manualOffset(element) {
var root = global.document[IE_PAGE_STAT_ACCESS],
body = root.body,
top = OFFSET_TOP,
left = OFFSET_LEFT,
mtop = MARGIN_TOP,
mleft = MARGIN_LEFT,
stop = SCROLL_TOP,
sleft = SCROLL_LEFT,
findStyles = [mleft, mtop],
parent = element.offsetParent,
getStyle = computedStyle,
style = getStyle(element, [findStyles]),
page = screen(element),
x = element[left],
y = element[top];
x += parseFloat(style[mleft]) || 0;
y += parseFloat(style[mtop]) || 0;
for (; parent; parent = parent.offsetParent) {
if (parent.nodeType === 1) {
style = getStyle(parent, findStyles);
x += (parent[left] || 0) +
(parent.clientLeft || 0) +
(parseFloat(style[mleft]) || 0);
y += (parent[top] || 0) +
(parent.clientTop || 0) +
(parseFloat(style[mtop]) || 0);
}
}
parent = element.parentNode;
for (; parent && parent !== body; parent = parent.parentNode) {
if (parent.nodeType === 1 && parent !== root) {
x += parent[sleft] || 0;
y += parent[stop] || 0;
}
}
root = parent = body = null;
return [
x,
y,
x + element[OFFSET_WIDTH] - page[2],
y + element[OFFSET_HEIGHT] - page[3]];
}
/**
* Page Scroll
*/
function setPageScroll(window, x, y) {
var factor = USE_ZOOM_FACTOR ? getZoomFactor(window) : 1;
window.scrollTo(x * factor, y * factor);
}
function w3cPageScrollOffset(window) {
var offset = [(window.pageXOffset || 0), (window.pageYOffset || 0)];
return offset;
}
function iePageScrollOffset(window) {
var M = Math,
subject = window.document[IE_PAGE_STAT_ACCESS],
factor = USE_ZOOM_FACTOR ? getZoomFactor(window) : 1,
offset = [M.round(subject[SCROLL_LEFT] / factor),
M.round(subject[SCROLL_TOP] / factor)];
subject = null;
return offset;
}
function getZoomFactor() {
//var rect, body;
//
//if (boundingRect) {
// body = window.document.body;
//
// // rect is only in physical pixel size in IE before version 8
// rect = body[BOUNDING_RECT]();
//
// // the zoom level is always an integer percent value
// factor = Math.round(
// (rect.right - rect.left / body[OFFSET_WIDTH]) * 100) / 100;
//}
//
//body = null;
return 1;
}
/**
* checking
*/
function isViewable(dom) {
var body, viewable;
if (isDom(dom, 1, 9)) {
if (dom.nodeType === 9) {
return PAGE_VIEW;
}
body = dom.ownerDocument.body;
viewable = (dom === body || contains(body, dom)) && ELEMENT_VIEW;
body = null;
return viewable;
}
return isView(dom) ? PAGE_VIEW : false;
}
function getTranslation(element, x, y, right, bottom, width, height, target) {
var cssValue = unitValue,
parse = parseFloat,
NUMBER = 'number',
hasLeft = false,
hasTop = hasLeft,
hasRight = hasLeft,
hasBottom = hasLeft;
var hasWidth, hasHeight, diff, currentDimension;
currentDimension = computedStyle(element,
'position',
'top',
'left',
'right',
'bottom',
'width',
'height');
if (!object(target)) {
target = {};
}
// resolve position
switch (currentDimension.position) {
case 'relative':
/* falls through */
case 'absolute':
case 'fixed':
// create position
x = cssValue(x);
y = cssValue(y);
right = cssValue(right);
bottom = cssValue(bottom);
hasLeft = x !== false;
hasTop = y !== false;
hasRight = !hasLeft && right !== false;
hasBottom = !hasBottom && bottom !== false;
if (hasLeft || hasRight || hasTop || hasBottom) {
diff = getOffset(element);
if (hasLeft) {
target.left = typeof x === NUMBER ? (
(parse(currentDimension.left) || 0) +
(x - diff[0])
) + 'px' :
x;
}
else if (hasRight) {
target.right = typeof right === NUMBER ? (
(parse(currentDimension.right) || 0) +
(right - diff[2])
) + 'px' :
right;
}
if (hasTop) {
target.top = typeof y === NUMBER ? (
(parse(currentDimension.top) || 0) +
(y - diff[1])
) + 'px' :
y;
}
else if (hasBottom) {
target.bottom = typeof right === NUMBER ? (
(parse(currentDimension.bottom) || 0) +
(bottom - diff[3])
) + 'px' :
bottom;
}
}
}
// resolve size
width = cssValue(width);
hasWidth = width !== false;
if (hasWidth) {
target.width = typeof width === NUMBER ? (
parse(currentDimension.width || 0) +
(width - element[OFFSET_WIDTH])
) + 'px' :
width;
}
height = cssValue(height);
hasHeight = height !== false;
if (hasHeight) {
target.height = typeof height === NUMBER ? (
parse(currentDimension.height || 0) +
(height - element[OFFSET_HEIGHT])
) + 'px' :
height;
}
return hasLeft || hasRight || hasTop || hasBottom ||
hasWidth || hasHeight ? target : null;
}
/**
* Accessors
*/
export
function offset(element, x, y) {
var valid = validUnitValue;
// setter
if (arguments.length > 1) {
if (isViewable(element) !== ELEMENT_VIEW) {
throw new Error(ERROR_INVALID_ELEMENT);
}
if (array(x)) {
if (x.length > 1) {
y = x[1];
x = x[0];
}
else {
throw new Error(ERROR_INVALID_PARAMETERS);
}
}
if (!valid(x)) {
throw new Error(ERROR_INVALID_X);
}
if (!valid(y)) {
throw new Error(ERROR_INVALID_Y);
}
x = getTranslation(element,
x,
y,
false,
false,
false,
false);
if (x) {
stylize(element, x);
}
return getModule();
}
// getter
switch (isViewable(element)) {
case PAGE_VIEW:
return pageBox(element).slice(0, 2);
case ELEMENT_VIEW:
return getOffset(element).slice(0, 2);
}
throw new Error(ERROR_INVALID_ELEMENT);
}
export
function size(element, width, height) {
var valid = validUnitValue;
// setter
if (arguments.length > 1) {
if (isViewable(element) !== ELEMENT_VIEW) {
throw new Error(ERROR_INVALID_ELEMENT);
}
if (array(width)) {
if (width.length > 1) {
height = width[1];
width = width[0];
}
else {
throw new Error(ERROR_INVALID_PARAMETERS);
}
}
if (!valid(width)) {
throw new Error(ERROR_INVALID_WIDTH);
}
if (!valid(height)) {
throw new Error(ERROR_INVALID_HEIGHT);
}
width = getTranslation(element,
false,
false,
false,
false,
width,
height);
if (width) {
stylize(element, width);
}
return getModule();
}
// getter
return isViewable(element) === PAGE_VIEW ?
pageBox(element).slice(2, 4) : getSize(element);
}
export
function box(element, x, y, width, height) {
var valid = validUnitValue;
var viewmode, dimension;
// setter
if (arguments.length > 1) {
if (isViewable(element) !== ELEMENT_VIEW) {
throw new Error(ERROR_INVALID_ELEMENT);
}
if (array(x)) {
if (x.length > 3) {
height = x[3];
width = x[2];
y = x[1];
x = x[0];
}
else {
throw new Error(ERROR_INVALID_PARAMETERS);
}
}
if (!valid(x)) {
throw new Error(ERROR_INVALID_X);
}
if (!valid(y)) {
throw new Error(ERROR_INVALID_Y);
}
if (!valid(width)) {
throw new Error(ERROR_INVALID_WIDTH);
}
if (!valid(height)) {
throw new Error(ERROR_INVALID_HEIGHT);
}
x = getTranslation(element,
x,
y,
false,
false,
width,
height);
if (x) {
stylize(element, x);
}
return getModule();
}
// getter
viewmode = isViewable(element);
if (viewmode === PAGE_VIEW) {
dimension = pageBox(element);
x = dimension[0];
y = dimension[1];
width = dimension[2];
height = dimension[3];
dimension = screen(element);
return [
x,
y,
width - x - dimension[2],
height - y - dimension[3],
width,
height];
}
if (viewmode !== ELEMENT_VIEW) {
throw new Error(ERROR_INVALID_ELEMENT);
}
dimension = getSize(element);
width = dimension[0];
height = dimension[1];
dimension = getOffset(element);
dimension[4] = width;
dimension[5] = height;
return dimension;
}
export
function translate(element, x, y, right, bottom, width, height, target) {
var valid = validUnitValue;
if (isViewable(element) !== ELEMENT_VIEW) {
throw new Error(ERROR_INVALID_ELEMENT);
}
// resolve parameters
if (array(x)) {
target = y;
if (x.length > 4) {
height = 5 in x ? x[5] : false;
width = 4 in x ? x[4] : false;
bottom = 3 in x ? x[3] : false;
right = 2 in x ? x[2] : false;
}
else {
height = 3 in x ? x[3] : false;
width = 2 in x ? x[2] : false;
bottom = false;
right = false;
}
y = 1 in y ? x[1] : false;
x = x[0];
}
if (!valid(x)) {
throw new Error(ERROR_INVALID_X);
}
if (!valid(y)) {
throw new Error(ERROR_INVALID_Y);
}
if (!valid(right)) {
throw new Error(ERROR_INVALID_RIGHT);
}
if (!valid(bottom)) {
throw new Error(ERROR_INVALID_BOTTOM);
}
if (!valid(width)) {
throw new Error(ERROR_INVALID_WIDTH);
}
if (!valid(height)) {
throw new Error(ERROR_INVALID_HEIGHT);
}
return getTranslation(element,
x,
y,
right,
bottom,
width,
height,
target);
}
export
function scroll(dom, x, y) {
var setter = arguments.length > 1,
isNumber = number,
stop = SCROLL_TOP,
sleft = SCROLL_LEFT;
var current, window;
// validate x and y
if (setter) {
if (!isNumber(x)) {
x = false;
}
if (!isNumber(y)) {
y = false;
}
}
switch (isViewable(dom)) {
case PAGE_VIEW:
window = isDom(dom) ?
dom[DEFAULTVIEW] : dom;
current = getPageScroll(window);
if (setter) {
setPageScroll(window,
x === false ?
current[0] : x,
y === false ?
current[1] : y);
}
else {
return current;
}
break;
case ELEMENT_VIEW:
if (setter) {
dom[sleft] = x === false ? dom[sleft] : x;
dom[stop] = y === false ? dom[stop] : y;
}
else {
return [dom[sleft], dom[stop]];
}
break;
default:
throw new Error(ERROR_INVALID_DOM);
}
}
/**
* Visibility
*/
export
function visible(element, visibility, displayed) {
var rules = null,
isString = string,
len = arguments.length,
attached = isViewable(element) === ELEMENT_VIEW;
// setter
if (len > 1) {
rules = {};
if (isString(visibility)) {
rules.visibility = visibility;
}
else if (typeof visiblity === 'boolean') {
rules.visibility = visibility ? 'visible' : 'hidden';
}
if (displayed === false) {
displayed = 'none';
}
if (isString(displayed)) {
rules.display = displayed;
}
stylize(element, rules);
return getModule();
}
// getter
if (attached) {
rules = computedStyle(element,
'display',
'visibility');
return rules.display !== 'none' && rules.visibility !== 'hidden';
}
return false;
}
/**
* Screen offset and size
*/
export
function screen(dom) {
var subject = dom;
var box, size;
if (isDom(subject, 1, 9)) {
subject = (subject.nodeType === 1 ?
subject.ownerDocument : subject)[
documentViewAccess];
}
if (!isView(subject)) {
subject = global.window;
}
box = getPageScroll(subject);
size = getScreenSize(subject);
box[2] = size[0];
box[3] = size[1];
subject = null;
return box;
}
/**
* initialize
*/
DIMENSION_INFO = DETECTED && DETECTED.dimension;
if (DIMENSION_INFO) {
// strict mode
if (!DETECTED.browser.strict) {
IE_PAGE_STAT_ACCESS = 'body';
}
USE_ZOOM_FACTOR = DIMENSION_INFO.zoomfactor;
DEFAULTVIEW = DETECTED.dom.defaultView;
IEVERSION = DETECTED.browser.ieVersion;
getPageScroll = DIMENSION_INFO.pagescroll ?
w3cPageScrollOffset :
iePageScrollOffset;
getScreenSize = DIMENSION_INFO.screensize ?
w3cScreenSize :
ieScreenSize;
boundingRect = DIMENSION_INFO.rectmethod && BOUNDING_RECT;
getOffset = boundingRect ? rectOffset : manualOffset;
getSize = boundingRect ? rectSize : manualSize;
}