UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

299 lines (256 loc) 9.16 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2009 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Sebastian Werner (wpbasti) * Fabian Jakobs (fjakobs) * Christian Hagendorn (chris_schmidt) ************************************************************************ */ /** * Contains methods to compute a position for any object which should * be positioned relative to another object. */ qx.Class.define("qx.util.placement.Placement", { extend : qx.core.Object, construct : function() { this.base(arguments); this.__defaultAxis = qx.util.placement.DirectAxis; }, properties : { /** * The axis object to use for the horizontal placement */ axisX : { check: "Class" }, /** * The axis object to use for the vertical placement */ axisY : { check: "Class" }, /** * Specify to which edge of the target object, the object should be attached */ edge : { check: ["top", "right", "bottom", "left"], init: "top" }, /** * Specify with which edge of the target object, the object should be aligned */ align : { check: ["top", "right", "bottom", "left", "center", "middle"], init: "right" } }, statics : { __instance : null, /** * DOM and widget independent method to compute the location * of an object to make it relative to any other object. * * @param size {Map} With the keys <code>width</code> and <code>height</code> * of the object to align * @param area {Map} Available area to position the object. Has the keys * <code>width</code> and <code>height</code>. Normally this is the parent * object of the one to align. * @param target {Map} Location of the object to align the object to. This map * should have the keys <code>left</code>, <code>top</code>, <code>right</code> * and <code>bottom</code>. * @param offsets {Map} Map with all offsets for each direction. * Comes with the keys <code>left</code>, <code>top</code>, * <code>right</code> and <code>bottom</code>. * @param position {String} Alignment of the object on the target, any of * "top-left", "top-center", "top-right", "bottom-left", "bottom-center", "bottom-right", * "left-top", "left-middle", "left-bottom", "right-top", "right-middle", "right-bottom". * @param modeX {String} Horizontal placement mode. Valid values are: * <ul> * <li><code>direct</code>: place the object directly at the given * location.</li> * <li><code>keep-align</code>: if parts of the object is outside of the visible * area it is moved to the best fitting 'edge' and 'alignment' of the target. * It is guaranteed the the new position attaches the object to one of the * target edges and that that is aligned with a target edge.</li> * <li>best-fit</li>: If parts of the object are outside of the visible * area it is moved into the view port ignoring any offset, and position * values. * </ul> * @param modeY {String} Vertical placement mode. Accepts the same values as * the 'modeX' argument. * @return {Map} A map with the final location stored in the keys * <code>left</code> and <code>top</code>. */ compute: function(size, area, target, offsets, position, modeX, modeY) { this.__instance = this.__instance || new qx.util.placement.Placement(); var splitted = position.split("-"); var edge = splitted[0]; var align = splitted[1]; if (qx.core.Environment.get("qx.debug")) { if (align === "center" || align === "middle") { var expected = "middle"; if (edge === "top" || edge === "bottom") { expected = "center"; } qx.core.Assert.assertEquals(expected, align, "Please use '" + edge + "-" + expected + "' instead!"); } } this.__instance.set({ axisX: this.__getAxis(modeX), axisY: this.__getAxis(modeY), edge: edge, align: align }); return this.__instance.compute(size, area, target, offsets); }, __direct : null, __keepAlign : null, __bestFit : null, /** * Get the axis implementation for the given mode * * @param mode {String} One of <code>direct</code>, <code>keep-align</code> or * <code>best-fit</code> * @return {qx.util.placement.AbstractAxis} */ __getAxis : function(mode) { switch(mode) { case "direct": this.__direct = this.__direct || qx.util.placement.DirectAxis; return this.__direct; case "keep-align": this.__keepAlign = this.__keepAlign || qx.util.placement.KeepAlignAxis; return this.__keepAlign; case "best-fit": this.__bestFit = this.__bestFit || qx.util.placement.BestFitAxis; return this.__bestFit; default: throw new Error("Invalid 'mode' argument!'"); } } }, members : { __defaultAxis : null, /** * DOM and widget independent method to compute the location * of an object to make it relative to any other object. * * @param size {Map} With the keys <code>width</code> and <code>height</code> * of the object to align * @param area {Map} Available area to position the object. Has the keys * <code>width</code> and <code>height</code>. Normally this is the parent * object of the one to align. * @param target {Map} Location of the object to align the object to. This map * should have the keys <code>left</code>, <code>top</code>, <code>right</code> * and <code>bottom</code>. * @param offsets {Map} Map with all offsets for each direction. * Comes with the keys <code>left</code>, <code>top</code>, * <code>right</code> and <code>bottom</code>. * @return {Map} A map with the final location stored in the keys * <code>left</code> and <code>top</code>. */ compute : function(size, area, target, offsets) { if (qx.core.Environment.get("qx.debug")) { this.assertObject(size, "size"); this.assertNumber(size.width, "size.width"); this.assertNumber(size.height, "size.height"); this.assertObject(area, "area"); this.assertNumber(area.width, "area.width"); this.assertNumber(area.height, "area.height"); this.assertObject(target, "target"); this.assertNumber(target.top, "target.top"); this.assertNumber(target.right, "target.right"); this.assertNumber(target.bottom, "target.bottom"); this.assertNumber(target.left, "target.left"); this.assertObject(offsets, "offsets"); this.assertNumber(offsets.top, "offsets.top"); this.assertNumber(offsets.right, "offsets.right"); this.assertNumber(offsets.bottom, "offsets.bottom"); this.assertNumber(offsets.left, "offsets.left"); } var axisX = this.getAxisX() || this.__defaultAxis; var left = axisX.computeStart( size.width, {start: target.left, end: target.right}, {start: offsets.left, end: offsets.right}, area.width, this.__getPositionX() ); var axisY = this.getAxisY() || this.__defaultAxis; var top = axisY.computeStart( size.height, {start: target.top, end: target.bottom}, {start: offsets.top, end: offsets.bottom}, area.height, this.__getPositionY() ); return { left: left, top: top }; }, /** * Get the position value for the horizontal axis * * @return {String} the position */ __getPositionX : function() { var edge = this.getEdge(); var align = this.getAlign(); if (edge == "left") { return "edge-start"; } else if (edge == "right") { return "edge-end"; } else if (align == "left") { return "align-start"; } else if (align == "center") { return "align-center"; } else if (align == "right") { return "align-end"; } }, /** * Get the position value for the vertical axis * * @return {String} the position */ __getPositionY : function() { var edge = this.getEdge(); var align = this.getAlign(); if (edge == "top") { return "edge-start"; } else if (edge == "bottom") { return "edge-end"; } else if (align == "top") { return "align-start"; } else if (align == "middle") { return "align-center"; } else if (align == "bottom") { return "align-end"; } } }, destruct : function() { this._disposeObjects('__defaultAxis'); } });