UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

367 lines (366 loc) • 12 kB
/** * DevExtreme (animation/position.js) * Version: 20.1.7 * Build date: Tue Aug 25 2020 * * Copyright (c) 2012 - 2020 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var $ = require("../core/renderer"); var commonUtils = require("../core/utils/common"); var each = require("../core/utils/iterator").each; var windowUtils = require("../core/utils/window"); var window = windowUtils.getWindow(); var domAdapter = require("../core/dom_adapter"); var isWindow = require("../core/utils/type").isWindow; var extend = require("../core/utils/extend").extend; var getBoundingRect = require("../core/utils/position").getBoundingRect; var browser = require("../core/utils/browser"); var translator = require("./translator"); var support = require("../core/utils/support"); var horzRe = /left|right/; var vertRe = /top|bottom/; var collisionRe = /fit|flip|none/; var IS_SAFARI = browser.safari; var normalizeAlign = function(raw) { var result = { h: "center", v: "center" }; var pair = commonUtils.splitPair(raw); if (pair) { each(pair, function() { var w = String(this).toLowerCase(); if (horzRe.test(w)) { result.h = w } else { if (vertRe.test(w)) { result.v = w } } }) } return result }; var normalizeOffset = function(raw) { return commonUtils.pairToObject(raw) }; var normalizeCollision = function(raw) { var pair = commonUtils.splitPair(raw); var h = String(pair && pair[0]).toLowerCase(); var v = String(pair && pair[1]).toLowerCase(); if (!collisionRe.test(h)) { h = "none" } if (!collisionRe.test(v)) { v = h } return { h: h, v: v } }; var getAlignFactor = function(align) { switch (align) { case "center": return .5; case "right": case "bottom": return 1; default: return 0 } }; var inverseAlign = function(align) { switch (align) { case "left": return "right"; case "right": return "left"; case "top": return "bottom"; case "bottom": return "top"; default: return align } }; var calculateOversize = function(data, bounds) { var oversize = 0; if (data.myLocation < bounds.min) { oversize += bounds.min - data.myLocation } if (data.myLocation > bounds.max) { oversize += data.myLocation - bounds.max } return oversize }; var collisionSide = function(direction, data, bounds) { if (data.myLocation < bounds.min) { return "h" === direction ? "left" : "top" } if (data.myLocation > bounds.max) { return "h" === direction ? "right" : "bottom" } return "none" }; var initMyLocation = function(data) { data.myLocation = data.atLocation + getAlignFactor(data.atAlign) * data.atSize - getAlignFactor(data.myAlign) * data.mySize + data.offset }; var collisionResolvers = { fit: function(data, bounds) { var result = false; if (data.myLocation > bounds.max) { data.myLocation = bounds.max; result = true } if (data.myLocation < bounds.min) { data.myLocation = bounds.min; result = true } data.fit = result }, flip: function(data, bounds) { data.flip = false; if ("center" === data.myAlign && "center" === data.atAlign) { return } if (data.myLocation < bounds.min || data.myLocation > bounds.max) { var inverseData = extend({}, data, { myAlign: inverseAlign(data.myAlign), atAlign: inverseAlign(data.atAlign), offset: -data.offset }); initMyLocation(inverseData); inverseData.oversize = calculateOversize(inverseData, bounds); if (inverseData.myLocation >= bounds.min && inverseData.myLocation <= bounds.max || data.oversize > inverseData.oversize) { data.myLocation = inverseData.myLocation; data.oversize = inverseData.oversize; data.flip = true } } }, flipfit: function(data, bounds) { this.flip(data, bounds); this.fit(data, bounds) }, none: function(data) { data.oversize = 0 } }; var scrollbarWidth; var calculateScrollbarWidth = function() { var $scrollDiv = $("<div>").css({ width: 100, height: 100, overflow: "scroll", position: "absolute", top: -9999 }).appendTo($("body")); var result = $scrollDiv.get(0).offsetWidth - $scrollDiv.get(0).clientWidth; $scrollDiv.remove(); scrollbarWidth = result }; var defaultPositionResult = { h: { location: 0, flip: false, fit: false, oversize: 0 }, v: { location: 0, flip: false, fit: false, oversize: 0 } }; var calculatePosition = function(what, options) { var $what = $(what); var currentOffset = $what.offset(); var result = extend(true, {}, defaultPositionResult, { h: { location: currentOffset.left }, v: { location: currentOffset.top } }); if (!options) { return result } var my = normalizeAlign(options.my); var at = normalizeAlign(options.at); var of = $(options.of).length && options.of || window; var offset = normalizeOffset(options.offset); var collision = normalizeCollision(options.collision); var boundary = options.boundary; var boundaryOffset = normalizeOffset(options.boundaryOffset); var h = { mySize: $what.outerWidth(), myAlign: my.h, atAlign: at.h, offset: offset.h, collision: collision.h, boundaryOffset: boundaryOffset.h }; var v = { mySize: $what.outerHeight(), myAlign: my.v, atAlign: at.v, offset: offset.v, collision: collision.v, boundaryOffset: boundaryOffset.v }; if ( of .preventDefault) { h.atLocation = of .pageX; v.atLocation = of .pageY; h.atSize = 0; v.atSize = 0 } else { of = $( of ); if (isWindow( of [0])) { h.atLocation = of .scrollLeft(); v.atLocation = of .scrollTop(); h.atSize = of [0].innerWidth >= of [0].outerWidth ? of [0].innerWidth : of .width(); v.atSize = of [0].innerHeight >= of [0].outerHeight || IS_SAFARI ? of [0].innerHeight : of .height() } else { if (9 === of [0].nodeType) { h.atLocation = 0; v.atLocation = 0; h.atSize = of .width(); v.atSize = of .height() } else { var ofRect = getBoundingRect( of .get(0)); var o = getOffsetWithoutScale( of ); h.atLocation = o.left; v.atLocation = o.top; h.atSize = Math.max(ofRect.width, of .outerWidth()); v.atSize = Math.max(ofRect.height, of .outerHeight()) } } } initMyLocation(h); initMyLocation(v); var bounds = function() { var win = $(window); var windowWidth = win.width(); var windowHeight = win.height(); var left = win.scrollLeft(); var top = win.scrollTop(); var documentElement = domAdapter.getDocumentElement(); var hZoomLevel = support.touch ? documentElement.clientWidth / windowWidth : 1; var vZoomLevel = support.touch ? documentElement.clientHeight / windowHeight : 1; if (void 0 === scrollbarWidth) { calculateScrollbarWidth() } var boundaryWidth = windowWidth; var boundaryHeight = windowHeight; if (boundary) { var $boundary = $(boundary); var boundaryPosition = $boundary.offset(); left = boundaryPosition.left; top = boundaryPosition.top; boundaryWidth = $boundary.width(); boundaryHeight = $boundary.height() } return { h: { min: left + h.boundaryOffset, max: left + boundaryWidth / hZoomLevel - h.mySize - h.boundaryOffset }, v: { min: top + v.boundaryOffset, max: top + boundaryHeight / vZoomLevel - v.mySize - v.boundaryOffset } } }(); h.oversize = calculateOversize(h, bounds.h); v.oversize = calculateOversize(v, bounds.v); h.collisionSide = collisionSide("h", h, bounds.h); v.collisionSide = collisionSide("v", v, bounds.v); if (collisionResolvers[h.collision]) { collisionResolvers[h.collision](h, bounds.h) } if (collisionResolvers[v.collision]) { collisionResolvers[v.collision](v, bounds.v) } var preciser = function(number) { return options.precise ? number : Math.round(number) }; extend(true, result, { h: { location: preciser(h.myLocation), oversize: preciser(h.oversize), fit: h.fit, flip: h.flip, collisionSide: h.collisionSide }, v: { location: preciser(v.myLocation), oversize: preciser(v.oversize), fit: v.fit, flip: v.flip, collisionSide: v.collisionSide }, precise: options.precise }); return result }; var getOffsetWithoutScale = function getOffsetWithoutScale($startElement) { var $currentElement = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : $startElement; var currentElement = $currentElement.get(0); if (!currentElement || $currentElement.is("body")) { return $startElement.offset() } var transform = $currentElement.get(0).style.transform; var scale = (transform.match(/scale(.+)/) || [])[0]; currentElement.style.transform = transform.replace(scale, ""); var offset = getOffsetWithoutScale($startElement, $currentElement.parent()); currentElement.style.transform = transform; return offset }; var position = function(what, options) { var $what = $(what); if (!options) { return $what.offset() } translator.resetPosition($what, true); var offset = getOffsetWithoutScale($what); var targetPosition = options.h && options.v ? options : calculatePosition($what, options); var preciser = function(number) { return options.precise ? number : Math.round(number) }; translator.move($what, { left: targetPosition.h.location - preciser(offset.left), top: targetPosition.v.location - preciser(offset.top) }); return targetPosition }; var offset = function(element) { element = $(element).get(0); if (isWindow(element)) { return null } else { if (element && "pageY" in element && "pageX" in element) { return { top: element.pageY, left: element.pageX } } } return $(element).offset() }; if (!position.inverseAlign) { position.inverseAlign = inverseAlign } if (!position.normalizeAlign) { position.normalizeAlign = normalizeAlign } module.exports = { calculateScrollbarWidth: calculateScrollbarWidth, calculate: calculatePosition, setup: position, offset: offset };