- Styled button a little bit

- Added test for textbox
- Added baseWidget and inputWidget classes
- Implemented attribute system
- Implemented statustext as a test for the attribute system
- Ported csv_split function to JS
- Implemented system for the legacy options
- Added function for iterating over the widget tree
This commit is contained in:
Andreas Stöckel 2011-08-10 14:36:31 +00:00
parent 858279ad84
commit 68c7a5550e
24 changed files with 1685 additions and 159 deletions

View File

@ -0,0 +1,118 @@
/**
* eGroupWare eTemplate2 - JS Widget base class
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id: et2_widget.js 36021 2011-08-07 13:43:46Z igel457 $
*/
"use strict";
/*egw:uses
jquery.jquery;
lib/tooltip.js;
et2_widget;
*/
/**
* Class which manages the DOM node itself. The simpleWidget class is derrived
* from et2_DOMWidget and implements the getDOMNode function. A setDOMNode
* function is provided, which attatches the given node to the DOM if possible.
*/
var et2_baseWidget = et2_DOMWidget.extend({
attributes: {
"statustext": {
"name": "Tooltip",
"type": "string",
"description": "Tooltip which is shown for this element"
}
},
init: function() {
this._super.apply(this, arguments);
this.node = null;
this.statustext = "";
this._tooltipElem = null;
},
detatchFromDOM: function() {
// Detach this node from the tooltip node
if (this._tooltipElem)
{
egw_global_tooltip.unbindFromElement(this._tooltipElem);
this._tooltipElem = null;
}
this._super.apply(this, arguments);
},
attachToDOM: function() {
this._super.apply(this,arguments);
// Update the statustext
this.set_statustext(this.statustext);
},
setDOMNode: function(_node) {
if (_node != this.node)
{
// Deatch the old node from the DOM
this.detatchFromDOM();
// Set the new DOM-Node
this.node = _node;
// Attatch the DOM-Node to the tree
return this.attachToDOM();
}
return false;
},
getDOMNode: function() {
return this.node;
},
getTooltipElement: function() {
return this.getDOMNode();
},
set_statustext: function(_value) {
// Don't execute the code below, if no tooltip will be attached/detached
if (_value == "" && !this._tooltipElem)
{
return;
}
this.statustext = _value;
//Get the domnode the tooltip should be attached to
var elem = $j(this.getTooltipElement());
if (elem)
{
//If a tooltip is already attached to the element, remove it first
if (this._tooltipElem)
{
egw_global_tooltip.unbindFromElement(this._tooltipElem);
this._tooltipElem = null;
}
if (_value && _value != '')
{
egw_global_tooltip.bindToElement(elem, _value);
this._tooltipElem = elem;
}
}
}
});

37
etemplate/js/et2_box.js Normal file
View File

@ -0,0 +1,37 @@
/**
* eGroupWare eTemplate2 - JS Box object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
et2_baseWidget;
*/
/**
* Class which implements the hbox and vbox tag
*/
var et2_box = et2_baseWidget.extend({
init: function(_parent, _type) {
this._super.apply(this, arguments);
this.div = $j(document.createElement("div"))
.addClass("et2_" + _type);
this.setDOMNode(this.div[0]);
}
});
et2_register_widget(et2_box, ["hbox", "vbox"]);

View File

@ -14,20 +14,31 @@
/*egw:uses
jquery.jquery;
et2_widget;
et2_baseWidget;
*/
/**
* Class which implements the "button" XET-Tag
*/
var et2_button = et2_DOMWidget.extend({
var et2_button = et2_baseWidget.extend({
attributes: {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button"
}
},
init: function(_parent) {
this._super.apply(this, arguments);
this.label = "";
this.btn = $j(document.createElement("button"))
.addClass("et2_button");
this._super.apply(this, arguments);
this.label = "";
this.setDOMNode(this.btn[0]);
},
set_label: function(_value) {
@ -37,10 +48,6 @@ var et2_button = et2_DOMWidget.extend({
this.btn.text(_value);
}
},
getDOMNode: function() {
return this.btn[0];
}
});

View File

@ -10,27 +10,6 @@
* @version $Id$
*/
function et2_debug(_level, _msg)
{
if (typeof console != "undefined")
{
if (_level == "log" && typeof console.log == "function")
{
console.log(_msg);
}
if (_level == "warn" && typeof console.warn == "function")
{
console.warn(_msg);
}
if (_level == "error" && typeof console.error == "function")
{
console.error(_msg);
}
}
}
/**
* IE Fix for array.indexOf
*/
@ -46,4 +25,302 @@ if (typeof Array.prototype.indexOf == "undefined")
};
}
/**
* ET2_DEBUGLEVEL specifies which messages are printed to the console. Decrease
* the value of ET2_DEBUGLEVEL to get less messages.
*/
var ET2_DEBUGLEVEL = 0;
function et2_debug(_level, _msg)
{
if (typeof console != "undefined")
{
if (_level == "log" && ET2_DEBUGLEVEL >= 4 &&
typeof console.log == "function")
{
console.log(_msg);
}
if (_level == "info" && ET2_DEBUGLEVEL >= 3 &&
typeof console.info == "function")
{
console.info(_msg);
}
if (_level == "warn" && ET2_DEBUGLEVEL >= 2 &&
typeof console.warn == "function")
{
console.warn(_msg);
}
if (_level == "error" && ET2_DEBUGLEVEL >= 1 &&
typeof console.error == "function")
{
console.error(_msg);
}
}
}
/**
* Array with all types supported by the et2_checkType function.
*/
var et2_validTypes = ["boolean", "string", "float", "integer", "any"];
/**
* Object whith default values for the above types. Do not specify array or
* objects inside the et2_typeDefaults object, as this instance will be shared
* between all users of it.
*/
var et2_typeDefaults = {
"boolean": false,
"string": "",
"float": 0.0,
"integer": 0,
"any": null
};
/**
* Checks whether the given value is of the given type. Strings are converted
* into the corresponding type. The (converted) value is returned. All supported
* types are listed in the et2_validTypes array.
*/
function et2_checkType(_val, _type)
{
function _err() {
throw("'" + _val + "' is not of specified _type '" + _type + "'");
}
// If the type is "any" simply return the value again
if (_type == "any")
{
return _val;
}
// If the type is boolean, check whether the given value is exactly true or
// false. Otherwise check whether the value is the string "true" or "false".
if (_type == "boolean")
{
if (_val === true || _val === false)
{
return _val;
}
var lcv = _val.toLowerCase();
if (lcv === "true" || lcv === "false" || lcv === "")
{
return _val === "true";
}
_err();
}
// Check whether the given value is of the type "string"
if (_type == "string")
{
if (typeof _val == "string")
{
return _val;
}
_err();
}
// Check whether the value is already a number, otherwise try to convert it
// to one.
if (_type == "float")
{
if (typeof _val == "number")
{
return _val;
}
if (!isNaN(_val))
{
return parseFloat(_val);
}
_err();
}
// Check whether the value is an integer by comparing the result of
// parseInt(_val) to the value itself.
if (_type == "integer")
{
if (parseInt(_val) == _val)
{
return parseInt(_val);
}
_err();
}
// We should never come here
throw("Invalid type identifier supplied.");
}
/**
* Validates the given attribute with the given id. The validation checks for
* the existance of a human name, a description, a type and a default value.
* If the human name defaults to the given id, the description defaults to an
* empty string, the type defaults to any and the default to the corresponding
* type default.
*/
function et2_validateAttrib(_id, _attrib)
{
// Default ignore to false.
if (typeof _attrib["ignore"] == "undefined")
{
_attrib["ignore"] = false
}
// Break if "ignore" is set to true.
if (_attrib.ignore)
{
return;
}
if (typeof _attrib["name"] == "undefined")
{
_attrib["name"] = _id;
et2_debug("log", "Human name ('name'-Field) for attribute '" +
_id + "' has not been supplied, set to '" + _id + "'");
}
if (typeof _attrib["description"] == "undefined")
{
_attrib["description"] = "";
et2_debug("log", "Description for attribute '" +
_id + "' has not been supplied");
}
if (typeof _attrib["type"] == "undefined")
{
_attrib["type"] = "any";
}
else
{
if (et2_validTypes.indexOf(_attrib["type"]) < 0)
{
et2_debug("error", "Invalid type for attribute '" + _id +
"' supplied.");
}
}
// Set the defaults
if (typeof _attrib["default"] == "undefined")
{
_attrib["default"] = et2_typeDefaults[_attrib["type"]];
}
}
/**
* Equivalent to the PHP array_values function
*/
function et2_arrayValues(_arr)
{
var result = [];
for (var key in _arr)
{
if (parseInt(key) == key)
{
result.push(_arr[key]);
}
}
return result;
}
/**
* Equivalent to the PHP substr function, partly take from phpjs, licensed under
* the GPL.
*/
function et2_substr (str, start, len) {
var end = str.length;
if (start < 0)
{
start += end;
}
end = typeof len === 'undefined' ? end : (len < 0 ? len + end : len + start);
return start >= str.length || start < 0 || start > end ? "" : str.slice(start, end);
}
/**
* Split a $delimiter-separated options string, which can contain parts with
* delimiters enclosed in $enclosure. Ported from class.boetemplate.inc.php
*
* Examples:
* - et2_csvSplit('"1,2,3",2,3') === array('1,2,3','2','3')
* - et2_csvSplit('1,2,3',2) === array('1','2,3')
* - et2_csvSplit('"1,2,3",2,3',2) === array('1,2,3','2,3')
* - et2_csvSplit('"a""b,c",d') === array('a"b,c','d') // to escape enclosures double them!
*
* @param string _str
* @param int _num=null in how many parts to split maximal, parts over this
* number end up (unseparated) in the last part
* @param string _delimiter=','
* @param string _enclosure='"'
* @return array
*/
function et2_csvSplit(_str, _num, _delimiter, _enclosure)
{
// Default the parameters
if (typeof _num == "undefined")
{
_num == null;
}
if (typeof _delimiter == "undefined")
{
_delimiter = ",";
}
if (typeof _enclosure == "undefined")
{
_enclosure = '"';
}
// If the _enclosure string does not occur in the string, simply use the
// split function
if (_str.indexOf(_enclosure) == -1)
{
return _num === null ? _str.split(_delimiter) :
_str.split(_delimiter, _num);
}
// Split the string at the delimiter and join it again, when a enclosure is
// found at the beginning/end of a part
var parts = _str.split(_delimiter);
for (var n = 0; typeof parts[n] != "undefined"; n++)
{
var part = parts[n];
if (part.charAt(0) === _enclosure)
{
var m = n;
while (typeof parts[m + 1] != "undefined" && parts[n].substr(-1) !== _enclosure)
{
parts[n] += _delimiter + parts[++m];
delete(parts[m]);
}
parts[n] = et2_substr(parts[n].replace(
new RegExp(_enclosure + _enclosure, 'g'), _enclosure), 1 , -1);
n = m;
}
}
// Rebuild the array index
parts = et2_arrayValues(parts);
// Limit the parts to the given number
if (_num !== null && _num > 0 && _num < parts.length && parts.length > 0)
{
parts[_num - 1] = parts.slice(_num - 1, parts.length).join(_delimiter);
parts = parts.slice(0, _num);
}
return parts;
}

View File

@ -14,24 +14,82 @@
/*egw:uses
jquery.jquery;
et2_widget;
et2_baseWidget;
*/
/**
* Class which implements the "description" XET-Tag
*/
var et2_description = et2_DOMWidget.extend({
var et2_description = et2_baseWidget.extend({
attributes: {
"value": {
"name": "Caption",
"type": "string",
"description": "Displayed text"
},
/**
* Options converted from the "options"-attribute.
*/
"font_style": {
"name": "Font Style",
"type": "string",
"description": "Style may be a compositum of \"b\" and \"i\" which " +
" renders the text bold and/or italic."
},
"href": {
"name": "Link Target",
"type": "string",
"description": "Link URL, empty if you don't wan't to display a link."
},
"activate_links": {
"name": "Replace URLs",
"type": "boolean",
"default": false,
"description": "If set, URLs in the text are automatically replaced " +
"by links"
},
"label_for": {
"name": "Label for widget",
"type": "string",
"description": "Marks the text as label for the given widget."
},
"extra_link_target": {
"name": "Link target",
"type": "string",
"default": "_self",
"description": "Link target descriptor"
},
"extra_link_popup": {
"name": "Popup",
"type": "string",
"description": "???"
},
"extra_link_title": {
"name": "Link Title",
"type": "string",
"description": "Link title which is displayed on mouse over."
}
},
legacyOptions: ["font_style", "href", "activate_links", "label_for",
"extra_link_target", "extra_link_popup", "extra_link_title"],
init: function(_parent) {
this._super.apply(this, arguments);
this.value = "";
this.font_style = "";
this.span = $j(document.createElement("span"))
.addClass("et2_label");
this._super.apply(this, arguments);
this.value = "";
this.setDOMNode(this.span[0]);
},
set_value: function(_value) {
if (_value != this.value)
set_value: function(_value, _force) {
if (_value != this.value || _force)
{
this.value = _value;
@ -39,8 +97,14 @@ var et2_description = et2_DOMWidget.extend({
}
},
getDOMNode: function() {
return this.span[0];
set_font_style: function(_value) {
if (_value != this.font_style)
{
this.font_style = _value;
this.span.toggleClass("et2_bold", _value.indexOf("b") >= 0);
this.span.toggleClass("et2_italic", _value.indexOf("i") >= 0);
}
}
});

View File

@ -98,12 +98,6 @@ var et2_grid = et2_DOMWidget.extend({
};
},
_readAttrWithDefault: function(_node, _name, _default) {
var val = _node.getAttribute(_name);
return (val === null) ? _default : val;
},
_getCell: function(_cells, _x, _y) {
if ((0 <= _y) && (_y < _cells.length))
{
@ -132,10 +126,10 @@ var et2_grid = et2_DOMWidget.extend({
var colDataEntry = this._getColDataEntry();
if (nodeName == "column")
{
colDataEntry["width"] = this._readAttrWithDefault(node, "width", "auto");
colDataEntry["class"] = this._readAttrWithDefault(node, "class", "");
colDataEntry["align"] = this._readAttrWithDefault(node, "align", "");
colDataEntry["span"] = this._readAttrWithDefault(node, "span", "1");
colDataEntry["width"] = et2_readAttrWithDefault(node, "width", "auto");
colDataEntry["class"] = et2_readAttrWithDefault(node, "class", "");
colDataEntry["align"] = et2_readAttrWithDefault(node, "align", "");
colDataEntry["span"] = et2_readAttrWithDefault(node, "span", "1");
}
else
{
@ -149,10 +143,10 @@ var et2_grid = et2_DOMWidget.extend({
var rowDataEntry = this._getRowDataEntry();
if (nodeName == "row")
{
rowDataEntry["height"] = this._readAttrWithDefault(node, "height", "auto");
rowDataEntry["class"] = this._readAttrWithDefault(node, "class", "");
rowDataEntry["valign"] = this._readAttrWithDefault(node, "valign", "");
rowDataEntry["span"] = this._readAttrWithDefault(node, "span", "1");
rowDataEntry["height"] = et2_readAttrWithDefault(node, "height", "auto");
rowDataEntry["class"] = et2_readAttrWithDefault(node, "class", "");
rowDataEntry["valign"] = et2_readAttrWithDefault(node, "valign", "");
rowDataEntry["span"] = et2_readAttrWithDefault(node, "span", "1");
}
else
{

View File

@ -12,6 +12,10 @@
"use strict";
/*egw:uses
et2_common;
*/
/**
* Usage of the JS inheritance system
* ----------------------------------
@ -84,10 +88,10 @@
};
/**
* The addInterfaceStuff function adds all interface functions the class has
* The addInterfaceFunctions function adds all interface functions the class has
* to implement to the class prototype.
*/
function addInterfaceStuff(prototype, interfaces)
function addInterfaceFunctions(prototype, interfaces)
{
// Remember all interface functions in the prototype
var ifaces = ((typeof prototype["_ifacefuncs"] == "undefined") ? [] :
@ -107,7 +111,7 @@
}
else
{
throw("Interfaces must be instanceof Interface!");
throw("Interfaces must be instance of Interface!");
}
}
@ -144,6 +148,118 @@
}
};
function addAttributeFunctions(prototype, _super)
{
var attributes = prototype.attributes;
// Add the old attributes to the new ones. If the attributes already
// exist, they are merged.
for (var key in _super.attributes)
{
var attrib = _super.attributes[key];
if (typeof attributes[key] == "undefined")
{
// In the case that the old attribute has no equivalent in the
// new class, simply create a reference to the old one.
attributes[key] = attrib;
}
else
{
// Otherwise merge the two attribute descriptors.
for (var key2 in attrib)
{
if (typeof attributes[key][key2] == "undefined")
{
attributes[key][key2] = attrib[key2];
}
}
}
}
// Validate the attributes
for (var key in attributes)
{
et2_validateAttrib(key, attributes[key]);
}
/**
* The initAttributes function sets the attributes to their default
* values. The attributes are not overwritten, which means, that the
* default is only set, if either a setter exists or this[propName] does
* not exist yet.
*/
prototype.initAttributes = function() {
for (var key in this.attributes)
{
if (!this.attributes[key].ignore)
{
this.setAttribute(key, this.attributes[key]["default"],
false);
}
}
this._attrsInitialized = true;
}
/**
* The setAttribute function sets the attribute with the given name to
* the given value. _override defines, whether this[_name] will be set,
* if this key already exists. _override defaults to true. A warning
* is issued if the attribute does not exist.
*/
prototype.setAttribute = function(_name, _value, _override) {
if (typeof this.attributes[_name] != "undefined")
{
if (!this.attributes[_name].ignore)
{
if (typeof _override == "undefined")
{
_override = true;
}
var val = et2_checkType(_value, this.attributes[_name].type);
if (typeof this["set_" + _name] == "function")
{
this["set_" + _name](val);
}
else if (_override || typeof this[_name] == "undefined")
{
this[_name] = val;
}
}
}
else
{
et2_debug("warn", "Attribute '" + _name + "' does not exist!");
}
}
/**
* Returns the value of the given attribute. If the property does not
* exist, an error message is issued.
*/
prototype.getAttribute = function(_name) {
if (typeof this.attributes[_name] != "undefined" &&
!this.attributes[_name].ignore)
{
if (typeof this["get_" + _name] == "function")
{
return this["get_" + _name]();
}
else
{
return this[_name];
}
}
else
{
et2_error("error", "Attribute '" + _name + "' does not exist!");
}
}
};
function classExtend(interfaces, prop) {
if (typeof prop == "undefined")
@ -158,6 +274,11 @@
interfaces = [interfaces];
}
if (typeof prop.attributes == "undefined")
{
prop.attributes = {};
}
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
@ -199,7 +320,11 @@
// Add the interface functions and the "implements" function to the
// prototype
addInterfaceStuff(prototype, interfaces);
addInterfaceFunctions(prototype, interfaces);
// Merge the attributes and create the functions corresponding to the
// attributes
addAttributeFunctions(prototype, _super);
// The dummy class constructor
function Class() {
@ -221,6 +346,12 @@
{
this.init.apply(this, arguments);
}
// Initialize the attributes
if (typeof this._attrsInitialized == "undefined")
{
this.initAttributes();
}
}
}
@ -243,5 +374,8 @@
// is an array which defines a set of interfaces the object has to
// implement. An interface is simply an object with named functions.
Class.extend = classExtend;
// The base class has no attributes
Class.attributes = {};
}).call(window);

View File

@ -0,0 +1,94 @@
/**
* eGroupWare eTemplate2 - JS Widget base class
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id: et2_widget.js 36021 2011-08-07 13:43:46Z igel457 $
*/
"use strict";
/*egw:uses
jquery.jquery;
et2_baseWidget;
*/
/**
* Interface for all widgets which support returning a value
*/
var et2_IInput = new Interface({
/**
* getValue has to return the value of the input widget
*/
getValue: function() {},
/**
* Is dirty returns true if the value of the widget has changed since it
* was loaded.
*/
isDirty: function() {},
/**
* Causes the dirty flag to be reseted.
*/
resetDirty: function() {}
});
/**
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
* interface. When derriving from this class, call setDOMNode with an input
* DOMNode.
*/
var et2_inputWidget = et2_simpleWidget.extend(et2_IInput, {
attributes: {
"value": {
"name": "Value",
"description": "The value of the widget",
"type": "string",
"default": ""
}
},
init: function() {
this._super.apply(this, arguments);
this._oldValue = "";
},
set_value: function(_value) {
this._oldValue = _value;
if (this.node)
{
$j(this.node).val(_value);
}
},
get_value: function() {
return this.getValue();
},
getValue: function() {
if (this.node)
{
return $j(this.node).val();
}
return this._oldValue;
},
isDirty: function() {
return this._oldValue != this.getValue();
},
resetDirty: function() {
this._oldValue = this.getValue();
}
});

178
etemplate/js/et2_tabs.js Normal file
View File

@ -0,0 +1,178 @@
/**
* eGroupWare eTemplate2 - JS Tabs object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
et2_widget;
*/
/**
* Class which implements the tabbox-tag
*/
var et2_tabbox = et2_DOMWidget.extend({
init: function(_parent, _type) {
// Create the outer tabbox container
this.container = $j(document.createElement("div"))
.addClass("et2_tabbox");
// Create the upper container for the tab flags
var cntr = $j(document.createElement("div"))
.addClass("et2_flags")
.appendTo(this.container);
this.flagContainer = $j(document.createElement("span"))
.addClass("et2_tabflagcntr")
.appendTo(cntr);
$j(document.createElement("span"))
.addClass("et2_tabspacer")
.appendTo(cntr);
// Create the lower tab container
this.tabContainer = $j(document.createElement("div"))
.addClass("et2_tabs")
.appendTo(this.container);
this._super.apply(this, arguments);
this.tabData = [];
},
destroy: function(_parent, _type) {
this._super.apply(this, arguments);
this.container = null;
this.flagContainer = null;
this.tabData = [];
},
_readTabs: function(tabData, tabs) {
et2_filteredNodeIterator(tabs, function(node, nodeName) {
if (nodeName == "tab")
{
tabData.push({
"label": et2_readAttrWithDefault(node, "label", "Tab"),
"widget": null,
"contentDiv": null,
"flagDiv": null
});
}
else
{
throw("Error while parsing: Invalid tag '" + nodeName +
"' in tabs tag");
}
}, this);
},
_readTabPanels: function(tabData, tabpanels) {
var i = 0;
et2_filteredNodeIterator(tabpanels, function(node, nodeName) {
if (i < tabData.length)
{
// Create the widget corresponding to the given node
tabData[i].widget = this.createElementFromNode(node,
nodeName);
}
else
{
throw("Error while reading tabpanels tag, too many widgets!");
}
i++;
}, this);
},
loadFromXML: function(_node) {
// Get the tabs and tabpanels tags
var tabsElems = et2_directChildrenByTagName(_node, "tabs");
var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels");
if (tabsElems.length == 1 && tabpanelsElems.length == 1)
{
var tabs = tabsElems[0];
var tabpanels = tabpanelsElems[0];
var tabData = [];
// Parse the "tabs" tag
this._readTabs(tabData, tabs);
// Read and create the widgets defined in the "tabpanels"
this._readTabPanels(tabData, tabpanels);
// Create the tab DOM-Nodes
this.createTabs(tabData)
}
else
{
throw("Error while parsing tabbox, none or multiple tabs or tabpanels tags!");
}
},
createTabs: function(tabData) {
this.tabData = tabData;
this.tabContainer.empty();
this.flagContainer.empty();
for (var i = 0; i < this.tabData.length; i++)
{
// Add a spacer to the flag container
$j(document.createElement("span"))
.addClass("et2_flagspacer")
.text("-")
.appendTo(this.flagContainer);
var entry = this.tabData[i];
entry.flagDiv = $j(document.createElement("span"))
.addClass("et2_tabflag")
.text(entry.label)
.appendTo(this.flagContainer);
entry.contentDiv = $j(document.createElement("div"))
.addClass("et2_tabcntr")
.hide()
.appendTo(this.tabContainer);
// Let the widget appear on its corresponding page
entry.widget.onSetParent();
}
},
getDOMNode: function(_sender) {
if (_sender == this)
{
return this.container[0];
}
else
{
for (var i = 0; i < this.tabData.length; i++)
{
if (this.tabData[i].widget == _sender)
{
return this.tabData[i].contentDiv[0];
}
}
return null;
}
}
});
et2_register_widget(et2_tabbox, ["tabbox"]);

View File

@ -26,6 +26,23 @@
*/
var et2_template = et2_DOMWidget.extend({
attributes: {
"template": {
},
"group": {
},
"version": {
"name": "Version",
"type": "string",
"description": "Version of the template"
},
"lang": {
"name": "Language",
"type": "string",
"description": "Language the template is written in"
}
},
/**
* Initializes this template widget as a simple container.
*/
@ -34,6 +51,7 @@ var et2_template = et2_DOMWidget.extend({
this.isProxied = false;
this.div = document.createElement("div");
this.id = "";
this._super.apply(this, arguments);
},

View File

@ -14,33 +14,56 @@
/*egw:uses
jquery.jquery;
et2_widget;
et2_inputWidget;
*/
/**
* Class which implements the "textbox" XET-Tag
*/
var et2_textbox = et2_DOMWidget.extend({
var et2_textbox = et2_baseWidget.extend({
init: function(_parent) {
this.input = $j(document.createElement("input"))
.addClass("et2_input");
this._super.apply(this, arguments);
this.label = "";
},
set_value: function(_value) {
if (_value != this.value)
{
this.label = _value;
this.input.attr("value", _value);
attributes: {
"multiline": {
"name": "multiline",
"type": "boolean",
"default": false,
"description": "If true, the textbox is a multiline edit field."
}
},
getDOMNode: function() {
return this.input[0];
init: function(_parent) {
this._super.apply(this, arguments);
this.input = null;
this.createInputWidget();
},
createInputWidget: function() {
if (this.multiline)
{
this.input = $j(document.createElement("textarea"));
}
else
{
this.input = $j(document.createElement("input"));
}
this.input.addClass("et2_textbox");
this.setDOMNode(this.input[0]);
},
set_multiline: function(_value) {
if (_value != this.multiline)
{
this.multiline = _value;
this.createInputWidget();
// Write all settings again
this.update();
}
}
});

View File

@ -14,6 +14,7 @@
/*egw:uses
jquery.jquery;
lib/tooltip.js;
et2_xml;
et2_common;
et2_inheritance;
@ -53,6 +54,26 @@ function et2_register_widget(_constructor, _types)
*/
var et2_widget = Class.extend({
attributes: {
"id": {
"name": "ID",
"type": "string",
"description": "Unique identifier of the widget"
},
/**
* Ignore the "span" property by default - it is read by the grid and
* other widgets.
*/
"span": {
"ignore": true
}
},
// Set the legacyOptions array to the names of the properties the "options"
// attribute defines.
legacyOptions: [],
/**
* The init function is the constructor of the widget. When deriving new
* classes from the widget base class, always call this constructor unless
@ -82,7 +103,6 @@ var et2_widget = Class.extend({
}
this._children = [];
this.id = "";
this.type = _type;
// The supported widget classes array defines a whitelist for all widget
@ -148,12 +168,12 @@ var et2_widget = Class.extend({
_obj._children[i].clone(this, _obj._children[i].type);
}
// Copy all properties for which a setter function exists
for (var key in _obj)
// Copy all properties
for (var key in _obj.attributes)
{
if (key != "id" && typeof this["set_" + key] == "function")
if (!_obj.attributes[key].ignore && key != "id")
{
this["set_" + key](_obj[key]);
this.setAttribute(key, _obj.getAttribute(key));
}
}
},
@ -265,6 +285,31 @@ var et2_widget = Class.extend({
return null;
},
/**
* Function which allows iterating over the complete widget tree.
*
* @param _callback is the function which should be called for each widget
* @param _context is the context in which the function should be executed
* @param _type is an optional parameter which specifies a class/interface
* the elements have to be instanceOf.
*/
iterateOver: function(_callback, _context, _type) {
if (typeof _type == "undefined")
{
_type = et2_widget;
}
if (this.instanceOf(_type))
{
_callback.call(_context, this);
}
for (var i = 0; i < this._children.length; i++)
{
this._children[i].iterateOver(_callback, _context, _type);
}
},
isOfSupportedWidgetClass: function(_obj)
{
for (var i = 0; i < this.supportedWidgetClasses.length; i++)
@ -336,12 +381,19 @@ var et2_widget = Class.extend({
loadAttributes: function(_attrs) {
for (var i = 0; i < _attrs.length; i++)
{
var attr = _attrs[i];
// Check whether a setter exists for the given attribute
if (typeof this["set_" + attr.name] == "function" && attr.name.charAt(0) != "_")
if (_attrs[i].name == "options")
{
this["set_" + attr.name](attr.value);
// Parse the legacy options
var splitted = et2_csvSplit(_attrs[i].value);
for (var i = 0; i < splitted.length && i < this.legacyOptions.length; i++)
{
this.setAttribute(this.legacyOptions[i], splitted[i]);
}
}
else
{
this.setAttribute(_attrs[i].name, _attrs[i].value);
}
}
},
@ -357,13 +409,14 @@ var et2_widget = Class.extend({
* update function of all child nodes.
*/
update: function() {
// Go through every property of this object and check whether a
// corresponding setter function exists. If yes, it is called.
for (var key in this)
for (var key in this.attributes)
{
if (typeof this["set_" + key] == "function" && key.charAt(0) != "_")
if (!this.attributes[key].ignore && key != "id")
{
this["set_" + key](this[key]);
this.setAttribute(key, this.getAttribute(key));
}
}
@ -372,19 +425,8 @@ var et2_widget = Class.extend({
{
this._children[i].update();
}
},
get_id: function() {
return this.id;
},
set_id: function(_value) {
this.id = _value;
},
get_type: function() {
return this.type;
}
});
/**
@ -392,7 +434,13 @@ var et2_widget = Class.extend({
*/
var et2_IDOMNode = new Interface({
/**
* Returns the DOM-Node of the current widget.
* Returns the DOM-Node of the current widget. The return value has to be
* a plain DOM node. If you want to return an jQuery object as you receive
* it with
*
* obj = $j(node);
*
* simply return obj[0];
*
* @param _sender The _sender parameter defines which widget is asking for
* the DOMNode.depending on that, the widget may return different nodes.
@ -417,19 +465,32 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
*/
init: function(_parent, _type) {
this.parentNode = null;
this.visible = true;
this._attachSet = {
"node": null,
"parent": null
};
// Call the inherited constructor
this._super.apply(this, arguments);
},
/**
* Detatches the node from the DOM and clears all references to the parent
* node or the dom node of this widget.
*/
destroy: function() {
this.detatchFromDOM();
this.parentNode = null;
this._attachSet = {};
this._super();
},
/**
* Automatically tries to attach this node to the parent widget.
*/
onSetParent: function() {
// Check whether the parent implements the et2_IDOMNode interface. If
// yes, grab the DOM node and create our own.
@ -438,16 +499,53 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
}
},
/**
* Detaches the widget from the DOM tree, if it had been attached to the
* DOM-Tree using the attachToDOM method.
*/
detatchFromDOM: function() {
if (this.parentNode)
if (this._attachSet.node && this._attachSet.parent)
{
var node = this.getDOMNode(this);
if (node)
{
this.parentNode.removeChild(node);
this.parentNode = null;
}
// Remove the current node from the parent node
this._attachSet.parent.removeChild(this._attachSet.node);
// Reset the "attachSet"
this._attachSet = {
"node": null,
"parent": null
};
return true;
}
return false;
},
/**
* Attaches the widget to the DOM tree. Fails if the widget is already
* attached to the tree or no parent node or no node for this widget is
* defined.
*/
attachToDOM: function() {
// Attach the DOM node of this widget (if existing) to the new parent
var node = this.getDOMNode(this);
if (node && this.parentNode &&
(node != this._attachSet.node ||
this.parentNode != this._attachSet.parent))
{
this.parentNode.appendChild(node);
// Store the currently attached nodes
this._attachSet = {
"node": node,
"parent": this.parentNode
};
return true;
}
return false;
},
/**
@ -462,51 +560,77 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
this.parentNode = _node;
// Attach the DOM node of this widget (if existing) to the new parent
var node = this.getDOMNode(this);
if (node && this.parentNode)
{
this.parentNode.appendChild(node);
}
// And attatch the element to the DOM tree
this.attachToDOM();
}
},
/**
* Returns the parent node.
*/
getParentDOMNode: function() {
return this.parentNode;
},
/**
* Sets the id of the DOM-Node.
*/
set_id: function(_value) {
this._super(_value);
this.id = _value;
var node = this.getDOMNode(this);
if (node)
{
node.setAttribute("id", _value);
if (_value != "")
{
node.setAttribute("id", _value);
}
else
{
node.removeAttribute("id");
}
}
}
});
/**
* Container object for not-yet supported widgets
* Common container object
*/
var et2_placeholder = et2_DOMWidget.extend({
var et2_container = et2_simpleWidget.extend({
init: function() {
// Create the placeholder div
this.placeDiv = $j(document.createElement("span"))
.addClass("et2_placeholder");
this._super.apply(this, arguments);
this.setDOMNode(document.createElement("div"));
}
});
/**
* Container object for not-yet supported widgets
*/
var et2_placeholder = et2_baseWidget.extend({
init: function() {
this._super.apply(this, arguments);
// The attrNodes object will hold the DOM nodes which represent the
// values of this object
this.attrNodes = {};
this._super.apply(this, arguments);
// Create the placeholder div
this.placeDiv = $j(document.createElement("span"))
.addClass("et2_placeholder");
var headerNode = $j(document.createElement("span"))
.text(this.type)
.addClass("et2_caption");
$j(this.placeDiv).append(headerNode);
.addClass("et2_caption")
.appendTo(this.placeDiv);
this.setDOMNode(this.placeDiv[0]);
},
loadAttributes: function(_attrs) {
@ -523,37 +647,6 @@ var et2_placeholder = et2_DOMWidget.extend({
this.attrNodes[attr.name].text(attr.name + "=" + attr.value);
}
},
getDOMNode: function() {
return this.placeDiv[0];
}
});
/**
* Common container object
*/
var et2_container = et2_DOMWidget.extend({
init: function() {
this.div = document.createElement("div");
this._super.apply(this, arguments);
},
getDOMNode: function() {
return this.div;
}
});
/**
* Interface for all widgets which support returning a value
*/
var et2_IValue = new Interface({
getValue: function() {}
});

View File

@ -114,3 +114,11 @@ function et2_filteredNodeIterator(_node, _callback, _context)
}
}
function et2_readAttrWithDefault(_node, _name, _default)
{
var val = _node.getAttribute(_name);
return (val === null) ? _default : val;
}

180
etemplate/js/lib/tooltip.js Normal file
View File

@ -0,0 +1,180 @@
/**
* eGroupware2 UI - Tooltip JS
*
* This javascript file contains the JavaScript code for the etemplate2 tooltip
*
* @link http://www.egroupware.org
* @author Andreas Stoeckel (as@stylite.de)
* @version $Id: tooltip.js 31497 2010-07-22 09:50:11Z igel457 $
*/
/* Tooltip handling */
function egw_tooltip()
{
this.tooltip_div = null;
this.current_elem = null;
this.time_delta = 100;
this.show_delta = 0;
this.show_delay = 800;
this.x = 0;
this.y = 0;
}
egw_tooltip.prototype.bindToElement = function(_elem, _text)
{
if (_text != '')
{
var self = this;
_elem.bind('mouseenter.tooltip', function(e) {
if (_elem != self.current_elem)
{
//Prepare the tooltip
self._prepare(_text);
self.current_elem = _elem;
self.show_delta = 0;
self.x = e.clientX;
self.y = e.clientY;
window.setTimeout(function() {self.showHintTimeout();}, self.time_delta);
}
return false;
});
_elem.bind('mouseleave.tooltip', function() {
self.current_elem = null;
self.show_delta = 0;
//self.hide();
if (self.tooltip_div)
{
self.tooltip_div.fadeOut(100);
}
});
_elem.bind('mousemove.tooltip', function(e) {
//Calculate the distance the mouse took since the last call of mousemove
var dx = self.x - e.clientX;
var dy = self.y - e.clientY;
var movedist = Math.sqrt(dx * dx + dy * dy);
//Block appereance of the tooltip on fast movements (with small movedistances)
if (movedist > 2)
self.show_delta = 0;
self.x = e.clientX;
self.y = e.clientY;
});
}
}
egw_tooltip.prototype.unbindFromElement = function(_elem)
{
_elem.unbind('mouseenter.tooltip');
_elem.unbind('mouseleave.tooltip');
_elem.unbind('mousemove.tooltip');
}
egw_tooltip.prototype.showHintTimeout = function()
{
if (this.current_elem != null)
{
this.show_delta += this.time_delta;
if (this.show_delta < this.show_delay)
{
//Repeat the call of timeout
var self = this;
window.setTimeout(function() {self.showHintTimeout();}, this.time_delta);
}
else
{
this.show_delta = 0;
this.show();
}
}
}
egw_tooltip.prototype._prepare = function(_text)
{
var self = this;
//Remove the old tooltip
if (this.tooltip_div)
this.hide();
//Generate the tooltip div, set it's text and append it to the body tag
this.tooltip_div = $j(document.createElement('div'));
this.tooltip_div.hide();
this.tooltip_div.append(_text);
this.tooltip_div.addClass("egw_tooltip");
$j('body').append(this.tooltip_div);
//The tooltip should automatically hide when the mouse comes over it
this.tooltip_div.mouseenter(function() {
self.hide();
});
}
egw_tooltip.prototype.show = function()
{
if (this.tooltip_div)
{
//Calculate the cursor_rectangle - this is a space the tooltip might
//not overlap with
var cursor_rect = {
left: (this.x - 8),
top: (this.y - 8),
right: (this.x + 8),
bottom: (this.y + 8)
};
//Calculate how much space is left on each side of the rectangle
var window_width = $j(document).width();
var window_height = $j(document).height();
var space_left = {
left: (cursor_rect.left),
top: (cursor_rect.top),
right: (window_width - cursor_rect.right),
bottom: (window_height - cursor_rect.bottom)
};
//Get the width and the height of the tooltip
var tooltip_width = this.tooltip_div.width();
if (tooltip_width > 300) tooltip_width = 300;
var tooltip_height = this.tooltip_div.height();
if (space_left.right < tooltip_width) {
this.tooltip_div.css('left', cursor_rect.left - tooltip_width);
} else if (space_left.left >= tooltip_width) {
this.tooltip_div.css('left', cursor_rect.right);
} else {
this.tooltip_div.css('left', cursor_rect.right);
this.tooltip_div.css('max-width', space_left.right);
}
if (space_left.bottom < tooltip_height) {
this.tooltip_div.css('top', cursor_rect.top - tooltip_height);
} else if (space_left.top >= tooltip_height) {
this.tooltip_div.css('top', cursor_rect.bottom);
} else {
this.tooltip_div.css('top', cursor_rect.bottom);
this.tooltip_div.css('max-height', space_left.bottom);
}
this.tooltip_div.fadeIn(100);
}
}
egw_tooltip.prototype.hide = function()
{
if (this.tooltip_div)
{
this.tooltip_div.remove();
this.tooltip_div = null;
}
}
//A reference to the current tooltip object
window.egw_global_tooltip = new egw_tooltip();

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<overlay>
<vbox>
<description value="This is only a test" options="bi" />
<description value="This is only a test" font_style="italic" statustext="This is only an italic label!"/>
</vbox>
</overlay>

View File

@ -25,15 +25,16 @@
<columns>
<column>
<label value="Northwest"/>
<label value="Northeast"/>
<label value="Southwest"/>
</column>
<label value="Equator"/>
<column>
<label value="Northeast"/>
<label value="Southwest"/>
<label value="Southeast"/>
</column>
</columns>
<rows>
<row/>
<row/>
@ -121,12 +122,12 @@
<columns>
<column>
<label value="Northwest"/>
<label value="Northeast"/>
<label value="Southwest"/>
</column>
<label value="Equator"/>
<column>
<label value="Northeast"/>
<label value="Southwest"/>
<label value="Southeast"/>
</column>
</columns>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<overlay>
<tabbox>
<tabs>
<tab label="Test1" />
<tab label="Test2" />
<tab label="Test3" />
<tab label="Test4" />
</tabs>
<tabpanels>
<label value="This is the content of tab 1"/>
<label value="This is the content of tab 2"/>
<label value="This is the content of tab 3"/>
<label value="This is the content of tab 4"/>
</tabpanels>
</tabbox>
</overlay>

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<overlay>
<textbox value="This is a single line textbox." statustext="Write something here!"/>
<textbox multiline="true" value="This is a multi line textbox."/>
<template id="testbox">
<textbox value="This is a single line textbox." statustext="And something else here!"/>
<textbox multiline="true" value="This is a multi line textbox."/>
</template>
<template id="testbox"/>
</overlay>

View File

@ -145,7 +145,7 @@
</grid>
</row>
<row class="row" disabled="@ts_viewtype">
<description value="Unitprice" options=",,ts_unitprice"/>
<description value="Unitprice" options=",,,ts_unitprice"/>
<grid>
<columns>
<column disabled="@pm_integration=none"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="128"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="Neues Dokument 1">
<defs
id="defs4">
<linearGradient
id="linearGradient3755">
<stop
style="stop-color:#ffffff;stop-opacity:0.52499998;"
offset="0"
id="stop3757" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3759" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3755"
id="linearGradient3761"
x1="15.357142"
y1="948.78522"
x2="15.357142"
y2="1021.8951"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="88.135153"
inkscape:cy="46.311537"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="823"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-924.36218)">
<rect
style="color:#000000;fill:url(#linearGradient3761);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.50000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="32"
height="128"
x="0"
y="924.36218" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="128"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="gradient02.svg"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/etemplate/js/test/gfx/gradient02.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<linearGradient
id="linearGradient3755">
<stop
style="stop-color:#ffffff;stop-opacity:0.52499998;"
offset="0"
id="stop3757" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3759" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3755"
id="linearGradient3761"
x1="15.357142"
y1="948.78522"
x2="15.357142"
y2="1021.8951"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-32,-1976.7244)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="88.135153"
inkscape:cy="46.311537"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="823"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-924.36218)">
<rect
style="color:#000000;fill:url(#linearGradient3761);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="32"
height="128"
x="-32"
y="-1052.3622"
transform="scale(-1,-1)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -11,7 +11,10 @@
<script src="../et2_description.js"></script>
<script src="../et2_grid.js"></script>
<script src="../et2_button.js"></script>
<script src="../et2_box.js"></script>
<script src="../et2_textbox.js"></script>
<!--<script src="../et2_tabs.js"></script>-->
<script src="../lib/tooltip.js"></script>
<style type="text/css">
body {
font-family: Lucida Grande, sans-serif;
@ -75,6 +78,86 @@
color: #101050;
font-size: 10pt;
}
button.et2_button {
background-color: #E0E0E0;
background-image: url(gfx/gradient01.png);
background-position: center;
background-repeat: repeat-x;
border: 1px solid silver;
color: #101010;
cursor: pointer;
margin: 5px;
padding: 3px;
text-align: center;
font-size: 9pt;
text-shadow: 1px 1px #E0E0E0;
}
button.et2_button:hover {
color: #050505;
border: 1px solid gray;
background-color: #D0D0EE;
}
button.et2_button:active {
background-image: url(gfx/gradient02.png);
background-color: #D0D0E0;
}
button.et2_button:focus {
border: 1px solid #2c3d6f;
color: #202d52;
outline: none;
}
div.et2_hbox {
display: inline;
}
.et2_tabflag {
display: inline-block;
margin-right: 5px;
height: 20px;
padding: 5px;
cursor: pointer;
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: silver;
}
.et2_tabflag.active {
border-bottom: 1px solid white;
}
.et2_textbox {
resize: none;
}
.et2_bold {
font-weight: bold;
}
.et2_italic {
font-style: italic;
}
.et2_vbox div, .et2_vbox span {
display: block;
}
.egw_tooltip
{
position: fixed;
border: 1px solid #897f51;
padding: 3px;
background-color: #FDF9DB;
max-width: 300px;
color: black;
}
</style>
</head>
<body>
@ -84,6 +167,9 @@
<a href="#" onclick="open_xet('et2_test_timesheet_edit.xet');">Timesheet edit dialog</a>
<a href="#" onclick="open_xet('et2_test_template.xet');">Template proxy test</a>
<a href="#" onclick="open_xet('et2_test_grid.xet');">Grid test</a>
<a href="#" onclick="open_xet('et2_test_tabbox.xet');">Tabs test</a>
<a href="#" onclick="open_xet('et2_test_textbox.xet');">Textbox test</a>
<a href="#" onclick="open_xet('et2_test_description.xet');">Description test</a>
</div>
<div class="header">ETemplate2 container:</div>
<div id="container"></div>