@qooxdoo/framework
Version:
The JS Framework for Coders
610 lines (525 loc) • 17.8 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2011-2012 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:
* Martin Wittemann (wittemann)
* Daniel Wagner (danielwagner)
************************************************************************ */
/**
* CSS/Style property manipulation module
* @group (Core)
*/
qx.Bootstrap.define("qx.module.Css", {
statics: {
/**
* INTERNAL
*
* Returns the rendered height of the first element in the collection.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the height of a <em>non displayed</em> element
* @return {Number} The first item's rendered height
*/
_getHeight : function(force) {
var elem = this[0];
if (elem) {
if (qx.dom.Node.isElement(elem)) {
var elementHeight;
if (force) {
var stylesToSwap = {
display : "block",
position : "absolute",
visibility : "hidden"
};
elementHeight = qx.module.Css.__swap(elem, stylesToSwap, "_getHeight", this);
} else {
elementHeight = qx.bom.element.Dimension.getHeight(elem);
}
return elementHeight;
} else if (qx.dom.Node.isDocument(elem)) {
return qx.bom.Document.getHeight(qx.dom.Node.getWindow(elem));
} else if (qx.dom.Node.isWindow(elem)) {
return qx.bom.Viewport.getHeight(elem);
}
}
return null;
},
/**
* INTERNAL
*
* Returns the rendered width of the first element in the collection
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the width of a <em>non displayed</em> element
* @return {Number} The first item's rendered width
*/
_getWidth : function(force) {
var elem = this[0];
if (elem) {
if (qx.dom.Node.isElement(elem)) {
var elementWidth;
if (force) {
var stylesToSwap = {
display : "block",
position : "absolute",
visibility : "hidden"
};
elementWidth = qx.module.Css.__swap(elem, stylesToSwap, "_getWidth", this);
} else {
elementWidth = qx.bom.element.Dimension.getWidth(elem);
}
return elementWidth;
} else if (qx.dom.Node.isDocument(elem)) {
return qx.bom.Document.getWidth(qx.dom.Node.getWindow(elem));
} else if (qx.dom.Node.isWindow(elem)) {
return qx.bom.Viewport.getWidth(elem);
}
}
return null;
},
/**
* INTERNAL
*
* Returns the content height of the first element in the collection.
* This is the maximum height the element can use, excluding borders,
* margins, padding or scroll bars.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the content height of a <em>non displayed</em> element
* @return {Number} Computed content height
*/
_getContentHeight : function(force)
{
var obj = this[0];
if (qx.dom.Node.isElement(obj)) {
var contentHeight;
if (force) {
var stylesToSwap = {
position: "absolute",
visibility: "hidden",
display: "block"
};
contentHeight = qx.module.Css.__swap(obj, stylesToSwap, "_getContentHeight", this);
} else {
contentHeight = qx.bom.element.Dimension.getContentHeight(obj);
}
return contentHeight;
}
return null;
},
/**
* INTERNAL
*
* Returns the content width of the first element in the collection.
* This is the maximum width the element can use, excluding borders,
* margins, padding or scroll bars.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the content width of a <em>non displayed</em> element
* @return {Number} Computed content width
*/
_getContentWidth : function(force)
{
var obj = this[0];
if (qx.dom.Node.isElement(obj)) {
var contentWidth;
if (force) {
var stylesToSwap = {
position: "absolute",
visibility: "hidden",
display: "block"
};
contentWidth = qx.module.Css.__swap(obj, stylesToSwap, "_getContentWidth", this);
} else {
contentWidth = qx.bom.element.Dimension.getContentWidth(obj);
}
return contentWidth;
}
return null;
},
/**
* Maps HTML elements to their default "display" style values.
*/
__displayDefaults : {},
/**
* Attempts tp determine the default "display" style value for
* elements with the given tag name.
*
* @param tagName {String} Tag name
* @param doc {Document?} Document element. Default: The current document
* @return {String} The default "display" value, e.g. <code>inline</code>
* or <code>block</code>
*/
__getDisplayDefault : function(tagName, doc)
{
var defaults = qx.module.Css.__displayDefaults;
if (!defaults[tagName]) {
var docu = doc || document;
var tempEl = qxWeb(docu.createElement(tagName)).appendTo(doc.body);
defaults[tagName] = tempEl.getStyle("display");
tempEl.remove();
}
return defaults[tagName] || "";
},
/**
* Swaps the given styles of the element and execute the callback
* before the original values are restored.
*
* Finally returns the return value of the callback.
*
* @param element {Element} the DOM element to operate on
* @param styles {Map} the styles to swap
* @param methodName {String} the callback functions name
* @param context {Object} the context in which the callback should be called
* @return {Object} the return value of the callback
*/
__swap : function(element, styles, methodName, context)
{
// get the current values
var currentValues = {};
for (var styleProperty in styles) {
currentValues[styleProperty] = element.style[styleProperty];
element.style[styleProperty] = styles[styleProperty];
}
var value = context[methodName]();
for (var styleProperty in currentValues) {
element.style[styleProperty] = currentValues[styleProperty];
}
return value;
},
/**
* Includes a Stylesheet file
*
* @attachStatic {qxWeb}
* @param uri {String} The stylesheet's URI
* @param doc {Document?} Document to modify
*/
includeStylesheet : function(uri, doc) {
qx.bom.Stylesheet.includeFile(uri, doc);
}
},
members :
{
/**
* Returns the rendered height of the first element in the collection.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the height of a <em>non displayed</em> element
* @return {Number} The first item's rendered height
*/
getHeight : function(force) {
return this._getHeight(force);
},
/**
* Returns the rendered width of the first element in the collection
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the width of a <em>non displayed</em> element
* @return {Number} The first item's rendered width
*/
getWidth : function(force) {
return this._getWidth(force);
},
/**
* Returns the content height of the first element in the collection.
* This is the maximum height the element can use, excluding borders,
* margins, padding or scroll bars.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the content height of a <em>non displayed</em> element
* @return {Number} Computed content height
*/
getContentHeight : function(force) {
return this._getContentHeight(force);
},
/**
* Returns the content width of the first element in the collection.
* This is the maximum width the element can use, excluding borders,
* margins, padding or scroll bars.
* @attach {qxWeb}
* @param force {Boolean?false} When true also get the content width of a <em>non displayed</em> element
* @return {Number} Computed content width
*/
getContentWidth : function(force) {
return this._getContentWidth(force);
},
/**
* Shows any elements with "display: none" in the collection. If an element
* was hidden by using the {@link #hide} method, its previous
* "display" style value will be re-applied. Otherwise, the
* default "display" value for the element type will be applied.
*
* @attach {qxWeb}
* @return {qxWeb} The collection for chaining
*/
show : function() {
this._forEachElementWrapped(function(item) {
var currentVal = item.getStyle("display");
var prevVal = item[0].$$qPrevDisp;
var newVal;
if (currentVal == "none") {
if (prevVal && prevVal != "none") {
newVal = prevVal;
}
else {
var doc = qxWeb.getDocument(item[0]);
newVal = qx.module.Css.__getDisplayDefault(item[0].tagName, doc);
}
item.setStyle("display", newVal);
item[0].$$qPrevDisp = "none";
}
});
return this;
},
/**
* Hides all elements in the collection by setting their "display"
* style to "none". The previous value is stored so it can be re-applied
* when {@link #show} is called.
*
* @attach {qxWeb}
* @return {qxWeb} The collection for chaining
*/
hide : function() {
this._forEachElementWrapped(function(item) {
var prevStyle = item.getStyle("display");
if (prevStyle !== "none") {
item[0].$$qPrevDisp = prevStyle;
item.setStyle("display", "none");
}
});
return this;
},
/**
* Returns the distance between the first element in the collection and its
* offset parent
*
* @attach {qxWeb}
* @return {Map} a map with the keys <code>left</code> and <code>top</code>
* containing the distance between the elements
*/
getPosition : function()
{
var obj = this[0];
if (qx.dom.Node.isElement(obj)) {
return qx.bom.element.Location.getPosition(obj);
}
return null;
},
/**
* Returns the computed location of the given element in the context of the
* document dimensions.
*
* Supported modes:
*
* * <code>margin</code>: Calculate from the margin box of the element (bigger than the visual appearance: including margins of given element)
* * <code>box</code>: Calculates the offset box of the element (default, uses the same size as visible)
* * <code>border</code>: Calculate the border box (useful to align to border edges of two elements).
* * <code>scroll</code>: Calculate the scroll box (relevant for absolute positioned content).
* * <code>padding</code>: Calculate the padding box (relevant for static/relative positioned content).
*
* @attach {qxWeb}
* @param mode {String?box} A supported option. See comment above.
* @return {Map} A map with the keys <code>left</code>, <code>top</code>,
* <code>right</code> and <code>bottom</code> which contains the distance
* of the element relative to the document.
*/
getOffset : function(mode) {
var elem = this[0];
if (elem && qx.dom.Node.isElement(elem)) {
return qx.bom.element.Location.get(elem, mode);
}
return null;
},
/**
* Modifies the given style property on all elements in the collection.
*
* @attach {qxWeb}
* @param name {String} Name of the style property to modify
* @param value {var} The value to apply
* @return {qxWeb} The collection for chaining
*/
setStyle : function(name, value) {
if (/\w-\w/.test(name)) {
name = qx.lang.String.camelCase(name);
}
this._forEachElement(function(item) {
qx.bom.element.Style.set(item, name, value);
});
return this;
},
/**
* Returns the value of the given style property for the first item in the
* collection.
*
* @attach {qxWeb}
* @param name {String} Style property name
* @return {var} Style property value
*/
getStyle : function(name) {
if (this[0] && qx.dom.Node.isElement(this[0])) {
if (/\w-\w/.test(name)) {
name = qx.lang.String.camelCase(name);
}
return qx.bom.element.Style.get(this[0], name);
}
return null;
},
/**
* Sets multiple style properties for each item in the collection.
*
* @attach {qxWeb}
* @param styles {Map} A map of style property name/value pairs
* @return {qxWeb} The collection for chaining
*/
setStyles : function(styles) {
for (var name in styles) {
this.setStyle(name, styles[name]);
}
return this;
},
/**
* Returns the values of multiple style properties for each item in the
* collection
*
* @attach {qxWeb}
* @param names {String[]} List of style property names
* @return {Map} Map of style property name/value pairs
*/
getStyles : function(names) {
var styles = {};
for (var i=0; i < names.length; i++) {
styles[names[i]] = this.getStyle(names[i]);
}
return styles;
},
/**
* Adds a class name to each element in the collection
*
* @attach {qxWeb}
* @param name {String} Class name
* @return {qxWeb} The collection for chaining
*/
addClass : function(name) {
this._forEachElement(function(item) {
qx.bom.element.Class.add(item, name);
});
return this;
},
/**
* Adds multiple class names to each element in the collection
*
* @attach {qxWeb}
* @param names {String[]} List of class names to add
* @return {qxWeb} The collection for chaining
*/
addClasses : function(names) {
this._forEachElement(function(item) {
qx.bom.element.Class.addClasses(item, names);
});
return this;
},
/**
* Removes a class name from each element in the collection
*
* @attach {qxWeb}
* @param name {String} The class name to remove
* @return {qxWeb} The collection for chaining
*/
removeClass : function(name) {
this._forEachElement(function(item) {
qx.bom.element.Class.remove(item, name);
});
return this;
},
/**
* Removes multiple class names from each element in the collection.
* Use {@link qx.module.Attribute#removeAttribute} to remove all classes.
*
* @attach {qxWeb}
* @param names {String[]} List of class names to remove
* @return {qxWeb} The collection for chaining
*/
removeClasses : function(names) {
this._forEachElement(function(item) {
qx.bom.element.Class.removeClasses(item, names);
});
return this;
},
/**
* Checks if the first element in the collection has the given class name
*
* @attach {qxWeb}
* @param name {String} Class name to check for
* @return {Boolean} <code>true</code> if the first item has the given class name
*/
hasClass : function(name) {
if (!this[0] || !qx.dom.Node.isElement(this[0])) {
return false;
}
return qx.bom.element.Class.has(this[0], name);
},
/**
* Returns the class name of the first element in the collection
*
* @attach {qxWeb}
* @return {String} Class name
*/
getClass : function() {
if (!this[0] || !qx.dom.Node.isElement(this[0])) {
return "";
}
return qx.bom.element.Class.get(this[0]);
},
/**
* Toggles the given class name on each item in the collection
*
* @attach {qxWeb}
* @param name {String} Class name
* @return {qxWeb} The collection for chaining
*/
toggleClass : function(name) {
var bCls = qx.bom.element.Class;
this._forEachElement(function(item) {
bCls.has(item, name) ?
bCls.remove(item, name) :
bCls.add(item, name);
});
return this;
},
/**
* Toggles the given list of class names on each item in the collection
*
* @attach {qxWeb}
* @param names {String[]} Class names
* @return {qxWeb} The collection for chaining
*/
toggleClasses : function(names) {
for (var i=0,l=names.length; i<l; i++) {
this.toggleClass(names[i]);
}
return this;
},
/**
* Replaces a class name on each element in the collection
*
* @attach {qxWeb}
* @param oldName {String} Class name to remove
* @param newName {String} Class name to add
* @return {qxWeb} The collection for chaining
*/
replaceClass : function(oldName, newName) {
this._forEachElement(function(item) {
qx.bom.element.Class.replace(item, oldName, newName);
});
return this;
}
},
defer : function(statics) {
qxWeb.$attachAll(this);
// manually attach private method which is ignored by attachAll
qxWeb.$attach({
"_getWidth" : statics._getWidth,
"_getHeight" : statics._getHeight,
"_getContentHeight" : statics._getContentHeight,
"_getContentWidth" : statics._getContentWidth
});
}
});