changed et2_core_inheritance.js to implement ClassWithAttributes extending Class from egw_inheritance and changed et2 objects to use ClassWithAttributes when required (also fixed lots of IDE warnings / added docu)

This commit is contained in:
Ralf Becker 2014-03-20 09:40:37 +00:00
parent f517b5786f
commit ac18b6cc8d
20 changed files with 295 additions and 571 deletions

View File

@ -503,7 +503,7 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode,
* *
* @augments Class * @augments Class
*/ */
var et2_surroundingsMgr = Class.extend( var et2_surroundingsMgr = ClassWithAttributes.extend(
{ {
/** /**
* Constructor * Constructor

View File

@ -14,7 +14,7 @@
/*egw:uses /*egw:uses
et2_core_common; et2_core_common;
et2_core_inheritance; egw_inheritance;
et2_core_phpExpressionCompiler; et2_core_phpExpressionCompiler;
*/ */

View File

@ -1,5 +1,5 @@
/** /**
* EGroupware eTemplate2 - JS code for implementing inheritance * EGroupware eTemplate2 - JS code for implementing inheritance with attributes
* *
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate * @package etemplate
@ -14,360 +14,19 @@
/*egw:uses /*egw:uses
et2_core_common; et2_core_common;
egw_inheritance;
*/ */
/** var ClassWithAttributes = Class.extend(
* Usage of the JS inheritance system {
* ----------------------------------
*
* To create a class write
*
* MyClass = Class.extend([interfaces, ] functions);
*
* where "interfaces" is a single interface or an array of interfaces and
* functions an object containing the functions the class implements.
*
* An interface has to be created in the following way:
*
* var IBreathingObject = new Interface({
* breath: function() {}
* });
*
* var Human = Class.extend(IBreathingObject, {
* walk: function() {
* console.log("Walking");
* },
* speak: function(_words) {
* console.log(_words);
* }
* });
*
* As "Human" does not implement the function "breath", "Human" is treated as
* abstract. Trying to create an instance of "Human" will throw an exception.
* However
*
* Human.prototype.implements(IBreathingObject);
*
* will return true. Lets create a specific class of "Human":
*
* var ChuckNorris = Human.extend({
* breath: function() {
* console.log("Chuck Norris does not breath, he holds air hostage.");
* },
* speak: function(_words) {
* console.warn("Chuck Norris says:");
* this._super(_words);
* }
* });
*/
// The following code is mostly taken from
// http://ejohn.org/blog/simple-javascript-inheritance/
// some parts were slightly changed for better understanding. Added possiblity
// to use interfaces.
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false;
/**
* Turn this to "true" to track creation and destruction of elements
*/
var getMem_freeMem_trace = false;
var tracedObjects = {};
// Check whether "function decompilation" works - fnTest is normally used to
// check whether a
var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// Base "Class" for interfaces - needed to check whether an object is an
// interface
this.Interface = function(fncts) {
for (var key in fncts)
{
this[key] = fncts[key];
}
};
/**
* The addInterfaceFunctions function adds all interface functions the class has
* to implement to the class prototype.
*/
function addInterfaceFunctions(prototype, interfaces)
{
// Remember all interface functions in the prototype
var ifaces = ((typeof prototype["_ifacefuncs"] == "undefined") ? [] :
prototype["_ifacefuncs"]);
prototype["_ifacefuncs"] = [];
for (var i = 0; i < interfaces.length; i++)
{
var iface = interfaces[i];
if (iface instanceof Interface)
{
for (var key in iface)
{
prototype["_ifacefuncs"].push(key);
}
}
else
{
throw("Interfaces must be instance of Interface!");
}
}
for (var i = 0; i < ifaces.length; i++)
{
prototype["_ifacefuncs"].push(ifaces[i]);
}
};
function addAttributeFunctions(prototype, _super)
{
function _copyMerge(_new, _old)
{
var result = {};
// Copy the new object
if (typeof _new != "undefined")
{
for (var key in _new)
{
result[key] = _new[key];
}
}
// Merge the old object
for (var key in _old)
{
if (typeof result[key] == "undefined")
{
result[key] = _old[key];
}
}
return result;
}
var attributes = {};
// Copy the old attributes
for (var key in prototype.attributes)
{
attributes[key] = _copyMerge({}, prototype.attributes[key]);
}
// Add the old attributes to the new ones. If the attributes already
// exist, they are merged.
for (var key in _super.attributes)
{
var _old = _super.attributes[key];
var _new = {};
attributes[key] = _copyMerge(attributes[key], _old);
}
// Validate the attributes
for (var key in attributes)
{
et2_validateAttrib(key, attributes[key]);
}
prototype.attributes = attributes;
};
function classExtend(interfaces, prop) {
if (typeof prop == "undefined")
{
prop = interfaces;
interfaces = [];
}
// If a single interface is given, encapsulate it in an array
if (!(interfaces instanceof Array))
{
interfaces = [interfaces];
}
if (typeof prop.attributes == "undefined")
{
prop.attributes = {};
}
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function and check whether
// the function actually uses "_super" - the RegExp test function
// silently converts the funciton prop[name] to a string.
if (typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]))
{
prototype[name] = (function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]);
}
else
{
prototype[name] = prop[name];
}
}
// Add the interface functions and the "implements" function to the
// prototype
addInterfaceFunctions(prototype, interfaces);
// Merge the attributes and create the functions corresponding to the
// attributes
addAttributeFunctions(prototype, _super);
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing)
{
// Check whether the object implements all interface functions
for (var i = 0; i < this._ifacefuncs.length; i++)
{
var func = this._ifacefuncs[i];
if (!(typeof this[func] == "function"))
{
throw("Trying to create abstract object, interface " +
"function '" + func + "' not implemented.");
}
}
// Do some tracing of the getMem_freeMem_trace is activated
if (getMem_freeMem_trace)
{
this.__OBJ_UID = "obj_" + egw.uid();
var className = this.className();
tracedObjects[this.__OBJ_UID] = {
"created": new Date().getTime(),
"class": className
}
egw.debug("log", "*" + this.__OBJ_UID + " (" + className + ")");
}
if (this.init)
{
this.init.apply(this, arguments);
}
}
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = classExtend;
return Class;
};
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class. The first parameter
// 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.prototype.attributes = {};
// Add the basic functions
/**
* Destructor function - it calls "destroy" if it has been defined and then
* deletes all keys of this element, so that any access to this element will
* eventually throw an exception, making it easier to hunt down memory leaks.
*/
Class.prototype.free = function() {
if (this.destroy)
{
this.destroy();
}
// Trace the freeing of the object
if (getMem_freeMem_trace)
{
delete(tracedObjects[this.__OBJ_UID]);
egw.debug("log", "-" + this.__OBJ_UID);
}
// Delete every object entry
for (var key in this)
{
delete(this[key]);
}
// Don't raise an exception when attempting to free an element multiple
// times.
this.free = function() {};
};
// Some debug functions for memory leak hunting
if (getMem_freeMem_trace)
{
/**
* Prints a list of all objects UIDs which have not been freed yet.
*/
Class.prototype.showTrace = function() {
console.log(tracedObjects);
},
/**
* VERY slow - for debugging only!
*/
Class.prototype.className = function() {
for (var key in window)
{
if (key.substr(0, 3) == "et2" && this.constructor == window[key])
{
return key;
}
}
return "?";
}
}
/** /**
* Returns the value of the given attribute. If the property does not * Returns the value of the given attribute. If the property does not
* exist, an error message is issued. * exist, an error message is issued.
*
* @param {string} _name
* @return {*}
*/ */
Class.prototype.getAttribute = function(_name) { getAttribute: function(_name) {
if (typeof this.attributes[_name] != "undefined" && if (typeof this.attributes[_name] != "undefined" &&
!this.attributes[_name].ignore) !this.attributes[_name].ignore)
{ {
@ -384,15 +43,19 @@
{ {
egw.debug("error", this, "Attribute '" + _name + "' does not exist!"); egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
} }
}; },
/** /**
* The setAttribute function sets the attribute with the given name to * The setAttribute function sets the attribute with the given name to
* the given value. _override defines, whether this[_name] will be set, * the given value. _override defines, whether this[_name] will be set,
* if this key already exists. _override defaults to true. A warning * if this key already exists. _override defaults to true. A warning
* is issued if the attribute does not exist. * is issued if the attribute does not exist.
*
* @param {string} _name
* @param {*} _value
* @param {boolean} _override
*/ */
Class.prototype.setAttribute = function(_name, _value, _override) { setAttribute: function(_name, _value, _override) {
if (typeof this.attributes[_name] != "undefined") if (typeof this.attributes[_name] != "undefined")
{ {
if (!this.attributes[_name].ignore) if (!this.attributes[_name].ignore)
@ -419,16 +82,16 @@
{ {
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!"); egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
} }
}; },
/** /**
* generateAttributeSet sanitizes the given associative array of attributes * generateAttributeSet sanitizes the given associative array of attributes
* (by passing each entry to "et2_checkType" and checking for existance of * (by passing each entry to "et2_checkType" and checking for existance of
* the attribute) and adds the default values to the associative array. * the attribute) and adds the default values to the associative array.
* *
* @param _attrs is the associative array containing the attributes. * @param {object} _attrs is the associative array containing the attributes.
*/ */
Class.prototype.generateAttributeSet = function(_attrs) { generateAttributeSet: function(_attrs) {
// Sanity check and validation // Sanity check and validation
for (var key in _attrs) for (var key in _attrs)
@ -466,15 +129,17 @@
} }
return _attrs; return _attrs;
}; },
/** /**
* The initAttributes function sets the attributes to their default * The initAttributes function sets the attributes to their default
* values. The attributes are not overwritten, which means, that the * values. The attributes are not overwritten, which means, that the
* default is only set, if either a setter exists or this[propName] does * default is only set, if either a setter exists or this[propName] does
* not exist yet. * not exist yet.
*
* @param {object} _attrs is the associative array containing the attributes.
*/ */
Class.prototype.initAttributes = function(_attrs) { initAttributes: function(_attrs) {
for (var key in _attrs) for (var key in _attrs)
{ {
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined)) if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
@ -482,38 +147,14 @@
this.setAttribute(key, _attrs[key], false); this.setAttribute(key, _attrs[key], false);
} }
} }
}; },
/** _validate_attributes: function(attributes)
* The implements function can be used to check whether the object {
* implements the given interface. // Validate the attributes
*/ for (var key in attributes)
Class.prototype.implements = function(_iface) {
for (var key in _iface)
{ {
if (this._ifacefuncs.indexOf(key) < 0) et2_validateAttrib(key, attributes[key]);
{
return false;
}
} }
return true; }
}; });
/**
* The instanceOf function can be used to check for both - classes and
* interfaces. Please don't change the case of this function as this
* affects IE and Opera support.
*/
Class.prototype.instanceOf = function(_obj) {
if (_obj instanceof Interface)
{
return this.implements(_obj);
}
else
{
return this instanceof _obj;
}
};
}).call(window);

View File

@ -24,11 +24,11 @@ var et2_IDOMNode = new Interface({
* Returns the DOM-Node of the current widget. The return value has to be * 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 * a plain DOM node. If you want to return an jQuery object as you receive
* it with * it with
* *
* obj = $j(node); * obj = $j(node);
* *
* simply return obj[0]; * simply return obj[0];
* *
* @param _sender The _sender parameter defines which widget is asking for * @param _sender The _sender parameter defines which widget is asking for
* the DOMNode. Depending on that, the widget may return different nodes. * the DOMNode. Depending on that, the widget may return different nodes.
* This is used in the grid. Normally the _sender parameter can be omitted * This is used in the grid. Normally the _sender parameter can be omitted
@ -58,19 +58,19 @@ var et2_IInput = new Interface({
* Causes the dirty flag to be reseted. * Causes the dirty flag to be reseted.
*/ */
resetDirty: function() {}, resetDirty: function() {},
/** /**
* Checks the data to see if it is valid, as far as the client side can tell. * Checks the data to see if it is valid, as far as the client side can tell.
* Return true if it's not possible to tell on the client side, because the server * Return true if it's not possible to tell on the client side, because the server
* will have the chance to validate also. * will have the chance to validate also.
* *
* The messages array is to be populated with everything wrong with the data, * The messages array is to be populated with everything wrong with the data,
* so don't stop checking after the first problem unless it really makes sense * so don't stop checking after the first problem unless it really makes sense
* to ignore other problems. * to ignore other problems.
* *
* @param {String[]} messages List of messages explaining the failure(s). * @param {String[]} messages List of messages explaining the failure(s).
* messages should be fairly short, and already translated. * messages should be fairly short, and already translated.
* *
* @return {boolean} True if the value is valid (enough), false to fail * @return {boolean} True if the value is valid (enough), false to fail
*/ */
isValid: function(messages) {} isValid: function(messages) {}
@ -101,7 +101,7 @@ var et2_ISubmitListener = new Interface({
/** /**
* Called whenever the template gets submitted. Return false if you want to * Called whenever the template gets submitted. Return false if you want to
* stop submission. * stop submission.
* *
* @param _values contains the values which will be sent to the server. * @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted. * Listeners may change these values before they get submitted.
*/ */
@ -119,6 +119,8 @@ var et2_IDetachedDOM = new Interface({
* Creates a list of attributes which can be set when working in the * Creates a list of attributes which can be set when working in the
* "detached" mode. The result is stored in the _attrs array which is provided * "detached" mode. The result is stored in the _attrs array which is provided
* by the calling code. * by the calling code.
*
* @param {array} _attrs
*/ */
getDetachedAttributes: function(_attrs) {}, getDetachedAttributes: function(_attrs) {},

View File

@ -102,9 +102,9 @@ function et2_createWidget(_name, _attrs, _parent)
/** /**
* The et2 widget base class. * The et2 widget base class.
* *
* @augments Class * @augments ClassWithAttributes
*/ */
var et2_widget = Class.extend( var et2_widget = ClassWithAttributes.extend(
{ {
attributes: { attributes: {
"id": { "id": {

View File

@ -28,7 +28,7 @@
* header, etc.) and contains the root container: an instance of * header, etc.) and contains the root container: an instance of
* et2_dataview_view_grid, which can be accessed using the "grid" property of * et2_dataview_view_grid, which can be accessed using the "grid" property of
* this object. * this object.
* *
* @augments Class * @augments Class
*/ */
var et2_dataview = Class.extend({ var et2_dataview = Class.extend({
@ -55,7 +55,9 @@ var et2_dataview = Class.extend({
/** /**
* Constructor for the grid container * Constructor for the grid container
* @param object _parentNode is the DOM-Node into which the grid view will be inserted *
* @param {DOMElement} _parentNode is the DOM-Node into which the grid view will be inserted
* @param {egw} _egw
* @memberOf et2_dataview * @memberOf et2_dataview
*/ */
init: function(_parentNode, _egw) { init: function(_parentNode, _egw) {
@ -158,7 +160,7 @@ var et2_dataview = Class.extend({
resize: function(_w, _h) { resize: function(_w, _h) {
// Not fully initialized yet... // Not fully initialized yet...
if (!this.columnMgr) return; if (!this.columnMgr) return;
if (this.width != _w) if (this.width != _w)
{ {
this.width = _w; this.width = _w;
@ -192,7 +194,7 @@ var et2_dataview = Class.extend({
/** /**
* Recalculates the stylesheets which determine the column visibility and * Recalculates the stylesheets which determine the column visibility and
* width. * width.
* *
* @param setDefault boolean Allow admins to save current settings as default for all users * @param setDefault boolean Allow admins to save current settings as default for all users
*/ */
updateColumns: function(setDefault) { updateColumns: function(setDefault) {
@ -297,11 +299,11 @@ var et2_dataview = Class.extend({
this.visibleColumnCount++; this.visibleColumnCount++;
// Update the visibility of the column // Update the visibility of the column
this.egw.css("." + col.tdClass, this.egw.css("." + col.tdClass,
"display: table-cell; " + "display: table-cell; " +
"!important;"); "!important;");
// Ugly browser dependant code - each browser seems to treat the // Ugly browser dependant code - each browser seems to treat the
// right (collapsed) border of the row differently // right (collapsed) border of the row differently
var subBorder = 0; var subBorder = 0;
var subHBorder = 0; var subHBorder = 0;
@ -336,12 +338,12 @@ var et2_dataview = Class.extend({
// Write the width of the header columns // Write the width of the header columns
var headerWidth = Math.max(0, (col.width - this.headerBorderWidth - subHBorder)); var headerWidth = Math.max(0, (col.width - this.headerBorderWidth - subHBorder));
this.egw.css(".egwGridView_outer ." + col.divClass, this.egw.css(".egwGridView_outer ." + col.divClass,
"width: " + headerWidth + "px;"); "width: " + headerWidth + "px;");
// Write the width of the body-columns // Write the width of the body-columns
var columnWidth = Math.max(0, (col.width - this.columnBorderWidth - subBorder)); var columnWidth = Math.max(0, (col.width - this.columnBorderWidth - subBorder));
this.egw.css(".egwGridView_grid ." + col.divClass, this.egw.css(".egwGridView_grid ." + col.divClass,
"width: " + columnWidth + "px;"); "width: " + columnWidth + "px;");
totalWidth += col.width; totalWidth += col.width;
@ -486,7 +488,7 @@ var et2_dataview = Class.extend({
* Reads the scrollbar width * Reads the scrollbar width
*/ */
_getScrollbarWidth: function(_table) { _getScrollbarWidth: function(_table) {
// Create a temporary td and two divs, which are inserted into the // Create a temporary td and two divs, which are inserted into the
// DOM-Tree. The outer div has a fixed size and "overflow" set to auto. // DOM-Tree. The outer div has a fixed size and "overflow" set to auto.
// When the second div is inserted, it will be forced to display a scrollbar. // When the second div is inserted, it will be forced to display a scrollbar.
var div_inner = $j(document.createElement("div")) var div_inner = $j(document.createElement("div"))

View File

@ -49,7 +49,7 @@ var et2_dataview_controller = Class.extend({
* requesting action links for a row. The row data, the index of the row and * requesting action links for a row. The row data, the index of the row and
* the uid are passed as parameters to the function. * the uid are passed as parameters to the function.
* uid is passed to the function. * uid is passed to the function.
* @param _context is the context in which the _rowCallback and the * @param _context is the context in which the _rowCallback and the
* _linkCallback are called. * _linkCallback are called.
* @param _actionObjectManager is the object that manages the action * @param _actionObjectManager is the object that manages the action
* objects. * objects.
@ -64,7 +64,7 @@ var et2_dataview_controller = Class.extend({
this._rowCallback = _rowCallback; this._rowCallback = _rowCallback;
this._linkCallback = _linkCallback; this._linkCallback = _linkCallback;
this._context = _context; this._context = _context;
// Initialize list of child controllers // Initialize list of child controllers
this._children = []; this._children = [];
@ -91,7 +91,7 @@ var et2_dataview_controller = Class.extend({
this._makeIndexVisible, this._makeIndexVisible,
this this
); );
// Record the child // Record the child
if(this._parentController != null) if(this._parentController != null)
{ {
@ -106,7 +106,7 @@ var et2_dataview_controller = Class.extend({
// Clear the selection timeout // Clear the selection timeout
this._clearTimer(); this._clearTimer();
// Remove the child from the child list // Remove the child from the child list
if(this._parentController != null) if(this._parentController != null)
{ {
@ -125,7 +125,7 @@ var et2_dataview_controller = Class.extend({
* The update function queries the server for changes in the currently * The update function queries the server for changes in the currently
* managed index range -- those changes are then merged into the current * managed index range -- those changes are then merged into the current
* view without a complete rebuild of every row. * view without a complete rebuild of every row.
* *
* @param {boolean} clear Skip the fancy stuff, dump everything and start again. * @param {boolean} clear Skip the fancy stuff, dump everything and start again.
* Completely clears the grid and selection. * Completely clears the grid and selection.
*/ */
@ -147,7 +147,7 @@ var et2_dataview_controller = Class.extend({
// Free selection manager // Free selection manager
this._selectionMgr.clear(); this._selectionMgr.clear();
// Clear the map // Clear the map
this._indexMap = {} this._indexMap = {}
// Update selection manager, it uses this by reference // Update selection manager, it uses this by reference
@ -211,7 +211,7 @@ var et2_dataview_controller = Class.extend({
{ {
// Skip any extra keys // Skip any extra keys
if(typeof data[key] != "object" || data[key] == null || typeof data[key][uid_key] == "undefined") continue; if(typeof data[key] != "object" || data[key] == null || typeof data[key][uid_key] == "undefined") continue;
// Add to row / uid map // Add to row / uid map
var entry = this._getIndexEntry(idx++); var entry = this._getIndexEntry(idx++);
entry.uid = data[key][uid_key]+""; entry.uid = data[key][uid_key]+"";
@ -252,7 +252,7 @@ var et2_dataview_controller = Class.extend({
/** /**
* Returns the row information of the passed node, or null if not available * Returns the row information of the passed node, or null if not available
* *
* @param {DOMNode} node * @param {DOMNode} node
* @return {string|false} UID, or false if not found * @return {string|false} UID, or false if not found
*/ */
@ -260,7 +260,7 @@ var et2_dataview_controller = Class.extend({
// Whatever the node, find a TR // Whatever the node, find a TR
var row_node = $j(node).closest('tr'); var row_node = $j(node).closest('tr');
var row = false var row = false
// Check index map - simple case // Check index map - simple case
var indexed = this._getIndexEntry(row_node.index()); var indexed = this._getIndexEntry(row_node.index());
if(indexed && indexed.row && indexed.row.getDOMNode() == row_node[0]) if(indexed && indexed.row && indexed.row.getDOMNode() == row_node[0])
@ -280,7 +280,7 @@ var et2_dataview_controller = Class.extend({
} }
} }
} }
// Check children // Check children
for(var i = 0; !row && i < this._children.length; i++) for(var i = 0; !row && i < this._children.length; i++)
{ {
@ -612,7 +612,7 @@ var et2_dataview_controller = Class.extend({
{ {
$j(tr).addClass("subentry"); $j(tr).addClass("subentry");
$j("td:first",tr).children("div").last().addClass("level_" + d + " indentation"); $j("td:first",tr).children("div").last().addClass("level_" + d + " indentation");
if(this.entry.idx == 0) if(this.entry.idx == 0)
{ {
// Set the CSS for the level - required so columns line up // Set the CSS for the level - required so columns line up
@ -811,7 +811,7 @@ var et2_dataview_controller = Class.extend({
_fetchCallback: function (_response) { _fetchCallback: function (_response) {
this.self._lastModification = _response.lastModification; this.self._lastModification = _response.lastModification;
// Do nothing if _response.order evaluates to false // Do nothing if _response.order evaluates to false
if (!_response.order) if (!_response.order)
{ {
@ -834,7 +834,7 @@ var et2_dataview_controller = Class.extend({
for(var i = this.start; i < this.start + order.length; i++) for(var i = this.start; i < this.start + order.length; i++)
delete this.self._queue[i]; delete this.self._queue[i];
} }
// Get the current index map for the updated region // Get the current index map for the updated region
var idxMap = this.self._getIndexMapping(this.start, order.length); var idxMap = this.self._getIndexMapping(this.start, order.length);
@ -860,11 +860,11 @@ var et2_dataview_controller = Class.extend({
// Now it's OK to invalidate, if it wasn't before // Now it's OK to invalidate, if it wasn't before
this.self._grid.doInvalidate = true; this.self._grid.doInvalidate = true;
// Update the total element count in the grid // Update the total element count in the grid
this.self._grid.setTotalCount(_response.total); this.self._grid.setTotalCount(_response.total);
this.self._selectionMgr.setTotalCount(_response.total); this.self._selectionMgr.setTotalCount(_response.total);
// Schedule an invalidate, in case total is the same // Schedule an invalidate, in case total is the same
this.self._grid.invalidate(); this.self._grid.invalidate();
}, },
@ -892,7 +892,7 @@ var et2_dataview_controller = Class.extend({
this._context, this._context,
{}, {},
0, 0,
"" ""
); );
} }
this._selectionMgr.registerRow("",0,placeholder.get(0), links); this._selectionMgr.registerRow("",0,placeholder.get(0), links);

View File

@ -24,14 +24,14 @@
* manage an external action object interface for each visible row and proxy all * manage an external action object interface for each visible row and proxy all
* state changes between an dummy action object, that does no selection handling, * state changes between an dummy action object, that does no selection handling,
* and the external action object interface. * and the external action object interface.
* *
* @augments Class * @augments Class
*/ */
var et2_dataview_selectionManager = Class.extend( var et2_dataview_selectionManager = Class.extend(
{ {
/** /**
* Constructor * Constructor
* *
* @param _parent * @param _parent
* @param _indexMap * @param _indexMap
* @param _actionObjectManager * @param _actionObjectManager
@ -214,10 +214,10 @@ var et2_dataview_selectionManager = Class.extend(
this.resetSelection(); this.resetSelection();
this._selectAll = true; this._selectAll = true;
// Tell action manager to do all // Tell action manager to do all
this._actionObjectManager.setAllSelected(true); this._actionObjectManager.setAllSelected(true);
// Update the selection // Update the selection
for (var key in this._registeredRows) for (var key in this._registeredRows)
{ {
@ -519,7 +519,7 @@ var et2_dataview_selectionManager = Class.extend(
// Query all unknown ranges from the server // Query all unknown ranges from the server
for (var i = 0; i < queryRanges.length; i++) for (var i = 0; i < queryRanges.length; i++)
{ {
this._queryRangeCallback.call(this._context, queryRanges[i], this._queryRangeCallback.call(this._context, queryRanges[i],
function (_order) { function (_order) {
for (var j = 0; j < _order.length; j++) for (var j = 0; j < _order.length; j++)
{ {

View File

@ -26,10 +26,10 @@ var ET2_COL_VISIBILITY_ALWAYS_NOSELECT = 3;
/** /**
* Class which stores the data of a single column. * Class which stores the data of a single column.
* *
* @augments Class * @augments Class
*/ */
var et2_dataview_column = Class.extend({ var et2_dataview_column = ClassWithAttributes.extend({
attributes: { attributes: {
"id": { "id": {
@ -72,7 +72,7 @@ var et2_dataview_column = Class.extend({
/** /**
* Constructor * Constructor
* *
* @param _attrs * @param _attrs
* @memberOf et2_dataview_column * @memberOf et2_dataview_column
*/ */
@ -114,7 +114,7 @@ var et2_dataview_column = Class.extend({
set_visibility: function(_value) { set_visibility: function(_value) {
// If visibility is always, don't turn it off // If visibility is always, don't turn it off
if(this.visibility == ET2_COL_VISIBILITY_ALWAYS || this.visibility == ET2_COL_VISIBILITY_ALWAYS_NOSELECT) return; if(this.visibility == ET2_COL_VISIBILITY_ALWAYS || this.visibility == ET2_COL_VISIBILITY_ALWAYS_NOSELECT) return;
if(_value === true) if(_value === true)
{ {
this.visibility = ET2_COL_VISIBILITY_VISIBLE; this.visibility = ET2_COL_VISIBILITY_VISIBLE;
@ -167,6 +167,8 @@ var et2_dataview_columns = Class.extend({
/** /**
* Set the total width of the header row * Set the total width of the header row
*
* @param {(string|number)} _width
*/ */
setTotalWidth: function(_width) { setTotalWidth: function(_width) {
if (_width != this.totalWidth && _width > 0) if (_width != this.totalWidth && _width > 0)
@ -178,6 +180,8 @@ var et2_dataview_columns = Class.extend({
/** /**
* Returns the index of the colum with the given id * Returns the index of the colum with the given id
*
* @param {string} _id
*/ */
getColumnIndexById: function(_id) { getColumnIndexById: function(_id) {
for (var i = 0; i < this.columns.length; i++) for (var i = 0; i < this.columns.length; i++)
@ -192,6 +196,8 @@ var et2_dataview_columns = Class.extend({
/** /**
* Returns the column with the given id * Returns the column with the given id
*
* @param {string} _id
*/ */
getColumnById: function(_id) { getColumnById: function(_id) {
var idx = this.getColumnIndexById(_id); var idx = this.getColumnIndexById(_id);
@ -200,6 +206,8 @@ var et2_dataview_columns = Class.extend({
/** /**
* Returns the width of the column with the given index * Returns the width of the column with the given index
*
* @param {number} _idx
*/ */
getColumnWidth: function(_idx) { getColumnWidth: function(_idx) {
if (this.totalWidth > 0 && _idx >= 0 && _idx < this.columns.length) if (this.totalWidth > 0 && _idx >= 0 && _idx < this.columns.length)
@ -262,6 +270,8 @@ var et2_dataview_columns = Class.extend({
/** /**
* Sets a column visiblity set * Sets a column visiblity set
*
* @param {object} _set
*/ */
setColumnVisibilitySet: function(_set) { setColumnVisibilitySet: function(_set) {
for (var k in _set) for (var k in _set)
@ -331,7 +341,7 @@ var et2_dataview_columns = Class.extend({
} }
} }
remRelWidth -= fixedTotal / tw; remRelWidth -= fixedTotal / tw;
// Check whether the width of columns with relative width is larger than their // Check whether the width of columns with relative width is larger than their
// maxWidth // maxWidth
var done; var done;
@ -419,13 +429,13 @@ var et2_dataview_columns = Class.extend({
this.columnWidths.push(w); this.columnWidths.push(w);
usedTotal += w; usedTotal += w;
} }
// Deal with any accumulated rounding errors // Deal with any accumulated rounding errors
if(usedTotal != tw) if(usedTotal != tw)
{ {
var column, columnIndex; var column, columnIndex;
var remaining_width = (usedTotal - tw); var remaining_width = (usedTotal - tw);
// Pick the first relative column and use it // Pick the first relative column and use it
for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++) for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++)
{ {
@ -452,7 +462,7 @@ var et2_dataview_columns = Class.extend({
for(var i = 0; i < this.columns.length; i++) for(var i = 0; i < this.columns.length; i++)
{ {
var col = this.columns[i]; var col = this.columns[i];
col.fixedWidth -= Math.round(this.columnWidths[i] / tw * remaining_width) col.fixedWidth -= Math.round(this.columnWidths[i] / tw * remaining_width);
this.columnWidths[i] = Math.max(0, Math.min(col.fixedWidth,tw)); this.columnWidths[i] = Math.max(0, Math.min(col.fixedWidth,tw));
} }
} }

View File

@ -10,7 +10,7 @@
* @version $Id$ * @version $Id$
*/ */
"use strict" "use strict";
/*egw:uses /*egw:uses
jquery.jquery; jquery.jquery;
@ -30,10 +30,10 @@
* A container does not know where it resides inside the grid, or whether it is * A container does not know where it resides inside the grid, or whether it is
* currently visible or not -- this information is efficiently managed by the * currently visible or not -- this information is efficiently managed by the
* et2_dataview_grid container. * et2_dataview_grid container.
* *
* @augments Class * @augments Class
*/ */
var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable, var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
{ {
/** /**
* Initializes the container object. * Initializes the container object.
@ -77,6 +77,9 @@ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
* Sets the "destroyCallback" -- the given function gets called whenever * Sets the "destroyCallback" -- the given function gets called whenever
* the container is destroyed. This instance is passed as an parameter to * the container is destroyed. This instance is passed as an parameter to
* the callback. * the callback.
*
* @param {function} _callback
* @param {object} _context
*/ */
setDestroyCallback: function(_callback, _context) { setDestroyCallback: function(_callback, _context) {
this._destroyCallback = _callback; this._destroyCallback = _callback;
@ -182,6 +185,8 @@ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
/** /**
* Removes a certain node from the container * Removes a certain node from the container
*
* @param {DOMElement} _node
*/ */
removeNode: function(_node) { removeNode: function(_node) {
// Get the index of the node in the nodes array // Get the index of the node in the nodes array
@ -296,6 +301,8 @@ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
/** /**
* Sets the top of the element. * Sets the top of the element.
*
* @param {number} _value
*/ */
setTop: function(_value) { setTop: function(_value) {
this._top = _value; this._top = _value;
@ -303,6 +310,8 @@ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
/** /**
* Sets the index of the element. * Sets the index of the element.
*
* @param {number} _value
*/ */
setIndex: function(_value) { setIndex: function(_value) {
this._index = _value; this._index = _value;
@ -355,46 +364,16 @@ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable,
} }
return true; return true;
} },
}); /**
* Returns the height of a node in pixels and zero if the element is not
/** * visible. The height is clamped to positive values.
* Returns the height of a node in pixels and zero if the element is not *
* visible. The height is clamped to positive values. * @param {DOMElement} _node
* The browser switch is placed at this position as the _nodeHeight function is */
* one of the most frequently called functions in the whole grid code and should _nodeHeight: function(_node)
* stay quite fast.
*/
/*if ($j.browser.mozilla)
{
et2_dataview_container.prototype._nodeHeight = function(_node)
{
var height = 0;
// Firefox sometimes provides fractional pixel values - we are
// forced to use those - we can obtain the fractional pixel height
// by using the window.getComputedStyle function
var compStyle = getComputedStyle(_node, null);
if (compStyle)
{
var styleHeightStr = compStyle.getPropertyValue("height");
height = parseFloat(styleHeightStr.substr(0,
styleHeightStr.length - 2));
if (isNaN(height) || height < 1)
{
height = 0;
}
}
return height;
}
}
else
{*/
et2_dataview_container.prototype._nodeHeight = function(_node)
{ {
return _node.offsetHeight; return _node.offsetHeight;
}; }
//} });

View File

@ -19,15 +19,15 @@
*/ */
/** /**
* The row provider contains prototypes (full clonable dom-trees) * The row provider contains prototypes (full clonable dom-trees)
* for all registered row types. * for all registered row types.
* *
* @augments Class * @augments Class
*/ */
var et2_dataview_rowProvider = Class.extend( var et2_dataview_rowProvider = Class.extend(
{ {
/** /**
* *
* @param _outerId * @param _outerId
* @param _columnIds * @param _columnIds
* @memberOf et2_dataview_rowProvider * @memberOf et2_dataview_rowProvider
@ -57,6 +57,10 @@ var et2_dataview_rowProvider = Class.extend(
* Returns a clone of the prototype with the given name. If the generator * Returns a clone of the prototype with the given name. If the generator
* callback function is given, this function is called if the prototype * callback function is given, this function is called if the prototype
* does not yet registered. * does not yet registered.
*
* @param {string} _name
* @param {function} _generator
* @param {object} _context
*/ */
getPrototype: function(_name, _generator, _context) { getPrototype: function(_name, _generator, _context) {
if (typeof this._prototypes[_name] == "undefined") if (typeof this._prototypes[_name] == "undefined")

View File

@ -21,7 +21,7 @@
* Object which resizes an inner node to the maximum extend of an outer node * Object which resizes an inner node to the maximum extend of an outer node
* (without creating a scrollbar) - it achieves that by performing some very * (without creating a scrollbar) - it achieves that by performing some very
* nasty and time consuming calculations. * nasty and time consuming calculations.
* *
* @augments Class * @augments Class
*/ */
var et2_dynheight = Class.extend( var et2_dynheight = Class.extend(
@ -51,6 +51,9 @@ var et2_dynheight = Class.extend(
/** /**
* Resizes the inner node. When this is done, the callback function is * Resizes the inner node. When this is done, the callback function is
* called. * called.
*
* @param {function} _callback
* @param {object} _context
*/ */
update: function(_callback, _context) { update: function(_callback, _context) {
// Check whether the inner node is actually visible - if not, don't // Check whether the inner node is actually visible - if not, don't
@ -103,7 +106,7 @@ var et2_dynheight = Class.extend(
var h = Math.max(this.minHeight, oh + ot - it - bh - var h = Math.max(this.minHeight, oh + ot - it - bh -
this.innerMargin - this.outerMargin); this.innerMargin - this.outerMargin);
this.innerNode.height(h); this.innerNode.height(h);
// Update the width // Update the width
// Some checking to make sure it doesn't overflow the width when user // Some checking to make sure it doesn't overflow the width when user
// resizes the window // resizes the window
@ -117,7 +120,7 @@ var et2_dynheight = Class.extend(
{ {
this.innerNode.width(w); this.innerNode.width(w);
} }
// Call the callback function // Call the callback function
if (typeof _callback != "undefined") if (typeof _callback != "undefined")
{ {
@ -129,6 +132,9 @@ var et2_dynheight = Class.extend(
/** /**
* Function used internally which collects all DOM-Nodes which are located * Function used internally which collects all DOM-Nodes which are located
* below this element. * below this element.
*
* @param {DOMElement} _node
* @param {number} _bottom
*/ */
_collectBottomNodes: function(_node, _bottom) { _collectBottomNodes: function(_node, _bottom) {
// Calculate the bottom position of the inner node // Calculate the bottom position of the inner node

View File

@ -19,16 +19,19 @@
*/ */
/** /**
* The row provider contains prototypes (full clonable dom-trees) * The row provider contains prototypes (full clonable dom-trees)
* for all registered row types. * for all registered row types.
* *
* @augments Class * @augments Class
*/ */
var et2_nextmatch_rowProvider = Class.extend( var et2_nextmatch_rowProvider = ClassWithAttributes.extend(
{ {
/** /**
* Creates the nextmatch row provider. * Creates the nextmatch row provider.
* *
* @param {et2_nextmatch_rowProvider} _rowProvider
* @param {function} _subgridCallback
* @param {object} _context
* @memberOf et2_nextmatch_rowProvider * @memberOf et2_nextmatch_rowProvider
*/ */
init: function (_rowProvider, _subgridCallback, _context) { init: function (_rowProvider, _subgridCallback, _context) {
@ -77,7 +80,7 @@ var et2_nextmatch_rowProvider = Class.extend(
// interface or do not support all attributes listed in the et2_IDetachedDOM // interface or do not support all attributes listed in the et2_IDetachedDOM
// interface. A warning is issued for all those widgets as they heavily // interface. A warning is issued for all those widgets as they heavily
// degrade the performance of the dataview // degrade the performance of the dataview
var seperated = rowTemplate.seperated = var seperated = rowTemplate.seperated =
this._seperateWidgets(variableAttributes); this._seperateWidgets(variableAttributes);
// Remove all DOM-Nodes of all widgets inside the "remaining" slot from // Remove all DOM-Nodes of all widgets inside the "remaining" slot from
@ -169,7 +172,7 @@ var et2_nextmatch_rowProvider = Class.extend(
tr.appendChild(row); tr.appendChild(row);
// Make the row expandable // Make the row expandable
if (typeof _data.content["is_parent"] !== "undefined" if (typeof _data.content["is_parent"] !== "undefined"
&& _data.content["is_parent"]) && _data.content["is_parent"])
{ {
_row.makeExpandable(true, function () { _row.makeExpandable(true, function () {
@ -189,12 +192,12 @@ var et2_nextmatch_rowProvider = Class.extend(
* Placeholder for empty row * Placeholder for empty row
* *
* The empty row placeholder is used when there are no results to display. * The empty row placeholder is used when there are no results to display.
* This allows the user to still have a drop target, or use actions that * This allows the user to still have a drop target, or use actions that
* do not require a row ID, such as 'Add new'. * do not require a row ID, such as 'Add new'.
*/ */
_createEmptyPrototype: function() { _createEmptyPrototype: function() {
var label = this._context && this._context.options && this._context.options.settings.placeholder; var label = this._context && this._context.options && this._context.options.settings.placeholder;
var placeholder = $j(document.createElement("td")) var placeholder = $j(document.createElement("td"))
.attr("colspan",this._rowProvider.getColumnCount()) .attr("colspan",this._rowProvider.getColumnCount())
.css("height","19px") .css("height","19px")
@ -208,6 +211,8 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* Returns an array containing objects which have variable attributes * Returns an array containing objects which have variable attributes
*
* @param {et2_widget} _widget
*/ */
_getVariableAttributeSet: function(_widget) { _getVariableAttributeSet: function(_widget) {
var variableAttributes = []; var variableAttributes = [];
@ -337,6 +342,8 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* Removes to DOM code for all widgets in the "remaining" slot * Removes to DOM code for all widgets in the "remaining" slot
*
* @param {object} _rowTemplate
*/ */
_stripTemplateRow: function(_rowTemplate) { _stripTemplateRow: function(_rowTemplate) {
_rowTemplate.placeholders = []; _rowTemplate.placeholders = [];
@ -347,7 +354,7 @@ var et2_nextmatch_rowProvider = Class.extend(
// Issue a warning - widgets which do not implement et2_IDOMNode // Issue a warning - widgets which do not implement et2_IDOMNode
// are very slow // are very slow
egw.debug("warn", "Non-clonable widget '"+ entry.widget._type + "' in dataview row - this " + egw.debug("warn", "Non-clonable widget '"+ entry.widget._type + "' in dataview row - this " +
"might be slow", entry); "might be slow", entry);
// Set the placeholder for the entry to null // Set the placeholder for the entry to null
@ -374,7 +381,7 @@ var et2_nextmatch_rowProvider = Class.extend(
}, },
_nodeIndex: function(_node) { _nodeIndex: function(_node) {
if(_node.parentNode == null) if(_node.parentNode == null)
{ {
return 0; return 0;
} }
@ -391,6 +398,9 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* Returns a function which does a relative access on the given DOM-Node * Returns a function which does a relative access on the given DOM-Node
*
* @param {DOMElement} _root
* @param {DOMElement} _target
*/ */
_compileDOMAccessFunc: function(_root, _target) { _compileDOMAccessFunc: function(_root, _target) {
function recordPath(_root, _target, _path) function recordPath(_root, _target, _path)
@ -427,6 +437,8 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* Builds relative paths to the DOM-Nodes and compiles fast-access functions * Builds relative paths to the DOM-Nodes and compiles fast-access functions
*
* @param {object} _rowTemplate
*/ */
_buildNodeAccessFuncs: function(_rowTemplate) { _buildNodeAccessFuncs: function(_rowTemplate) {
for (var i = 0; i < _rowTemplate.seperated.detachable.length; i++) for (var i = 0; i < _rowTemplate.seperated.detachable.length; i++)
@ -448,6 +460,10 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* Applies additional row data (like the class) to the tr * Applies additional row data (like the class) to the tr
*
* @param {object} _data
* @param {DOMElement} _tr
* @param {object} _mgrs
*/ */
_setRowData: function (_data, _tr, _mgrs) { _setRowData: function (_data, _tr, _mgrs) {
// TODO: Implement other fields than "class" // TODO: Implement other fields than "class"
@ -479,7 +495,7 @@ var et2_nextmatch_rowProvider = Class.extend(
// If not using category (tracker, calendar list) look for sel_options in the rows // If not using category (tracker, calendar list) look for sel_options in the rows
if(!categories) categories = _mgrs["sel_options"].parentMgr.getEntry(category_location); if(!categories) categories = _mgrs["sel_options"].parentMgr.getEntry(category_location);
if(!categories) categories = _mgrs["sel_options"].getEntry("${row}["+category_location + "]"); if(!categories) categories = _mgrs["sel_options"].getEntry("${row}["+category_location + "]");
// Cache // Cache
if(categories) this.categories = categories; if(categories) this.categories = categories;
} }
@ -525,11 +541,11 @@ var et2_nextmatch_rowProvider = Class.extend(
/** /**
* @augments et2_widget * @augments et2_widget
*/ */
var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode, var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode,
{ {
/** /**
* Constructor * Constructor
* *
* @param _mgrs * @param _mgrs
* @param _row * @param _row
* @memberOf et2_nextmatch_rowWidget * @memberOf et2_nextmatch_rowWidget
@ -549,6 +565,8 @@ var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode,
/** /**
* Copies the given array manager and clones the given widgets and inserts * Copies the given array manager and clones the given widgets and inserts
* them into the row which has been passed in the constructor. * them into the row which has been passed in the constructor.
*
* @param {array} _widgets
*/ */
createWidgets: function(_widgets) { createWidgets: function(_widgets) {
// Clone the given the widgets with this element as parent // Clone the given the widgets with this element as parent
@ -557,7 +575,7 @@ var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode,
{ {
// Disabled columns might be missing widget - skip it // Disabled columns might be missing widget - skip it
if(!_widgets[i]) continue; if(!_widgets[i]) continue;
this._widgets[i] = _widgets[i].clone(this); this._widgets[i] = _widgets[i].clone(this);
this._widgets[i].loadingFinished(); this._widgets[i].loadingFinished();
// Set column alignment from widget // Set column alignment from widget
@ -570,6 +588,9 @@ var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode,
/** /**
* Returns the column node for the given sender * Returns the column node for the given sender
*
* @param {et2_widget} _sender
* @return {DOMElement}
*/ */
getDOMNode: function(_sender) { getDOMNode: function(_sender) {
for (var i = 0; i < this._widgets.length; i++) for (var i = 0; i < this._widgets.length; i++)
@ -588,11 +609,11 @@ var et2_nextmatch_rowWidget = et2_widget.extend(et2_IDOMNode,
/** /**
* @augments et2_widget * @augments et2_widget
*/ */
var et2_nextmatch_rowTemplateWidget = et2_widget.extend(et2_IDOMNode, var et2_nextmatch_rowTemplateWidget = et2_widget.extend(et2_IDOMNode,
{ {
/** /**
* Constructor * Constructor
* *
* @param _root * @param _root
* @param _row * @param _row
* @memberOf et2_nextmatch_rowTemplateWidget * @memberOf et2_nextmatch_rowTemplateWidget
@ -632,6 +653,9 @@ var et2_nextmatch_rowTemplateWidget = et2_widget.extend(et2_IDOMNode,
/** /**
* Returns the column node for the given sender * Returns the column node for the given sender
*
* @param {et2_widget} _sender
* @return {DOMElement}
*/ */
getDOMNode: function(_sender) { getDOMNode: function(_sender) {

View File

@ -88,7 +88,10 @@ function etemplate2(_container, _menuaction)
// Unique ID to prevent DOM collisions across multiple templates // Unique ID to prevent DOM collisions across multiple templates
this.uniqueId = _container.getAttribute("id") ? _container.getAttribute("id").replace('.','-') : ''; this.uniqueId = _container.getAttribute("id") ? _container.getAttribute("id").replace('.','-') : '';
// Preset the object variable /**
* Preset the object variable
* @type {et2_container}
*/
this.widgetContainer = null; this.widgetContainer = null;
} }

View File

@ -22,6 +22,7 @@
<script src="../../../phpgwapi/js/jsapi/egw_css.js"></script> <script src="../../../phpgwapi/js/jsapi/egw_css.js"></script>
<script src="../../../phpgwapi/js/jsapi/egw_debug.js"></script> <script src="../../../phpgwapi/js/jsapi/egw_debug.js"></script>
<script src="../../../phpgwapi/js/jsapi/egw_inheritance.js"></script>
<script src="../et2_core_inheritance.js"></script> <script src="../et2_core_inheritance.js"></script>
<script src="../et2_core_interfaces.js"></script> <script src="../et2_core_interfaces.js"></script>
<script src="../et2_core_common.js"></script> <script src="../et2_core_common.js"></script>
@ -98,7 +99,7 @@
data[i] = "uid_" + i; data[i] = "uid_" + i;
} }
var dataprovider = Class.extend(et2_IDataProvider, { var dataprovider = ClassWithAttributes.extend(et2_IDataProvider, {
dataFetch: function (_queriedRange, _callback, _context) { dataFetch: function (_queriedRange, _callback, _context) {
var response = { var response = {

View File

@ -58,6 +58,9 @@ window.app = {classes: {}};
* // Underscore private by convention * // Underscore private by convention
* } * }
* }); * });
*
* @class AppJS
* @augments Class
*/ */
var AppJS = Class.extend( var AppJS = Class.extend(
{ {
@ -68,11 +71,15 @@ var AppJS = Class.extend(
/** /**
* Internal reference to etemplate2 widget tree * Internal reference to etemplate2 widget tree
*
* @var {et2_container}
*/ */
et2: null, et2: null,
/** /**
* Internal reference to egw client-side api object for current app and window * Internal reference to egw client-side api object for current app and window
*
* @var {egw}
*/ */
egw: null, egw: null,

View File

@ -16,7 +16,7 @@
* This code setups the egw namespace and adds the "extend" function, which is * This code setups the egw namespace and adds the "extend" function, which is
* used by extension modules to inject their content into the egw object. * used by extension modules to inject their content into the egw object.
*/ */
(function(_parent) { (function() {
var instanceUid = 0; var instanceUid = 0;
@ -46,7 +46,7 @@
{ {
if (_cond(_arr[i])) if (_cond(_arr[i]))
{ {
_arr.splice(i, 1) _arr.splice(i, 1);
} }
} }
} }
@ -173,19 +173,20 @@
/** /**
* Creates an api instance for the given application and the given window. * Creates an api instance for the given application and the given window.
* *
* @param _egw is the global _egw instance which should be used. * @param {globalEgw} _egw is the global _egw instance which should be used.
* @param _modules is the hash map which contains references to all module * @param {object} _modules is the hash map which contains references to all module
* descriptors. * descriptors.
* @param _moduleInstances is the the object which contains the application * @param {object} _moduleInstances is the the object which contains the application
* and window specific module instances. * and window specific module instances.
* @param _list is the overall instances list, to which the module should be * @param {array} _list is the overall instances list, to which the module should be
* added. * added.
* @param _instances refers to all api instances. * @param {object} _instances is the overall instances list, to which the module should be
* @param _app is the application for which the instance should be created. * added.
* @param _wnd is the window for which the instance should be created. * @param {string} _app is the application for which the instance should be created.
* @param {DOMElement} _window is the window for which the instance should be created.
* @return {egw}
*/ */
function createEgwInstance(_egw, _modules, _moduleInstances, _list, function createEgwInstance(_egw, _modules, _moduleInstances, _list, _instances, _app, _window)
_instances, _app, _window)
{ {
// Clone the global object // Clone the global object
var instance = cloneObject(_egw); var instance = cloneObject(_egw);
@ -237,18 +238,18 @@
* Returns a egw instance for the given application and the given window. If * Returns a egw instance for the given application and the given window. If
* the instance does not exist now, the instance will be created. * the instance does not exist now, the instance will be created.
* *
* @param _egw is the global _egw instance which should be used. * @param {globalEgw} _egw is the global _egw instance which should be used.
* @param _modules is the hash map which contains references to all module * @param {object} _modules is the hash map which contains references to all module
* descriptors. * descriptors.
* @param _moduleInstances is the the object which contains the application * @param {object} _moduleInstances is the the object which contains the application
* and window specific module instances. * and window specific module instances.
* @param _list is the overall instances list, to which the module should be * @param {object} _instances is the overall instances list, to which the module should be
* added. * added.
* @param _app is the application for which the instance should be created. * @param {string} _app is the application for which the instance should be created.
* @param _wnd is the window for which the instance should be created. * @param {DOMElement} _window is the window for which the instance should be created.
* @return {egw}
*/ */
function getEgwInstance(_egw, _modules, _moduleInstances, _instances, _app, function getEgwInstance(_egw, _modules, _moduleInstances, _instances, _app, _window)
_window)
{ {
// Generate the hash key for the instance descriptor object // Generate the hash key for the instance descriptor object
var hash = _app ? _app : '~global~'; var hash = _app ? _app : '~global~';
@ -435,6 +436,8 @@
* function and/or a window object. If you specify both, the app name * function and/or a window object. If you specify both, the app name
* has to preceed the window object reference. If no window object is * has to preceed the window object reference. If no window object is
* given, the root window will be used. * given, the root window will be used.
*
* @return {egw}
*/ */
var egw = function() { var egw = function() {
@ -471,7 +474,7 @@
// Generate an API instance // Generate an API instance
return getEgwInstance(egw, modules, moduleInstances, instances, return getEgwInstance(egw, modules, moduleInstances, instances,
_app, _window); _app, _window);
} };
var globalEgw = { var globalEgw = {
@ -686,9 +689,8 @@
return { return {
'instances': instances, 'instances': instances,
'moduleInstances': moduleInstances 'moduleInstances': moduleInstances
} };
} }
}; };
// Merge the globalEgw functions into the egw object. // Merge the globalEgw functions into the egw object.

View File

@ -17,7 +17,7 @@
* ---------------------------------- * ----------------------------------
* *
* To create a class write * To create a class write
* *
* MyClass = Class.extend([interfaces, ] functions); * MyClass = Class.extend([interfaces, ] functions);
* *
* where "interfaces" is a single interface or an array of interfaces and * where "interfaces" is a single interface or an array of interfaces and
@ -28,7 +28,7 @@
* var IBreathingObject = new Interface({ * var IBreathingObject = new Interface({
* breath: function() {} * breath: function() {}
* }); * });
* *
* var Human = Class.extend(IBreathingObject, { * var Human = Class.extend(IBreathingObject, {
* walk: function() { * walk: function() {
* console.log("Walking"); * console.log("Walking");
@ -37,13 +37,13 @@
* console.log(_words); * console.log(_words);
* } * }
* }); * });
* *
* As "Human" does not implement the function "breath", "Human" is treated as * As "Human" does not implement the function "breath", "Human" is treated as
* abstract. Trying to create an instance of "Human" will throw an exception. * abstract. Trying to create an instance of "Human" will throw an exception.
* However * However
* *
* Human.prototype.implements(IBreathingObject); * Human.prototype.implements(IBreathingObject);
* *
* will return true. Lets create a specific class of "Human": * will return true. Lets create a specific class of "Human":
* *
* var ChuckNorris = Human.extend({ * var ChuckNorris = Human.extend({
@ -57,17 +57,20 @@
* }); * });
*/ */
// The following code is mostly taken from // The following code is mostly taken from
// http://ejohn.org/blog/simple-javascript-inheritance/ // http://ejohn.org/blog/simple-javascript-inheritance/
// some parts were slightly changed for better understanding. Added possiblity // some parts were slightly changed for better understanding. Added possiblity
// to use interfaces. // to use interfaces.
/* Simple JavaScript Inheritance /**
* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/ * By John Resig http://ejohn.org/
* MIT Licensed * MIT Licensed
*
* Inspired by base2 and Prototype
*/ */
// Inspired by base2 and Prototype (function()
(function(){ {
var initializing = false; var initializing = false;
/** /**
@ -78,11 +81,16 @@
var tracedObjects = {}; var tracedObjects = {};
// Check whether "function decompilation" works - fnTest is normally used to // Check whether "function decompilation" works - fnTest is normally used to
// check whether a // check whether a
var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// Base "Class" for interfaces - needed to check whether an object is an /**
// interface * Base "Class" for interfaces - needed to check whether an object is an
* interface
*
* @param {object} fncts
* @class {Interface}
*/
this.Interface = function(fncts) { this.Interface = function(fncts) {
for (var key in fncts) for (var key in fncts)
{ {
@ -93,6 +101,9 @@
/** /**
* The addInterfaceFunctions function adds all interface functions the class has * The addInterfaceFunctions function adds all interface functions the class has
* to implement to the class prototype. * to implement to the class prototype.
*
* @param {Class} prototype
* @param {array} interfaces
*/ */
function addInterfaceFunctions(prototype, interfaces) function addInterfaceFunctions(prototype, interfaces)
{ {
@ -159,24 +170,32 @@
attributes[key] = _copyMerge({}, prototype.attributes[key]); attributes[key] = _copyMerge({}, prototype.attributes[key]);
} }
if(prototype._validate_attributes)
{
prototype._validate_attributes(prototype, attributes)
}
// Add the old attributes to the new ones. If the attributes already // Add the old attributes to the new ones. If the attributes already
// exist, they are merged. // exist, they are merged.
for (var key in _super.attributes) for (var key in _super.attributes)
{ {
var _old = _super.attributes[key]; var _old = _super.attributes[key];
var _new = {};
attributes[key] = _copyMerge(attributes[key], _old); attributes[key] = _copyMerge(attributes[key], _old);
} }
if(prototype._validate_attributes)
{
prototype._validate_attributes(attributes);
}
prototype.attributes = attributes; prototype.attributes = attributes;
}; };
/**
* Create a new Class that inherits from this class. The first parameter
* is an array which defines a set of interfaces the object has to
* implement. An interface is simply an object with named functions.
*
* @param {array} interfaces
* @param {object} prop
* @return {Class}
*/
function classExtend(interfaces, prop) { function classExtend(interfaces, prop) {
if (typeof prop == "undefined") if (typeof prop == "undefined")
@ -243,7 +262,11 @@
// attributes // attributes
addAttributeFunctions(prototype, _super); addAttributeFunctions(prototype, _super);
// The dummy class constructor /**
* The dummy class constructor
*
* @constructor {Class}
*/
function Class() { function Class() {
// All construction is actually done in the init method // All construction is actually done in the init method
if (!initializing) if (!initializing)
@ -254,7 +277,7 @@
var func = this._ifacefuncs[i]; var func = this._ifacefuncs[i];
if (!(typeof this[func] == "function")) if (!(typeof this[func] == "function"))
{ {
throw("Trying to create abstract object, interface " + throw("Trying to create abstract object, interface " +
"function '" + func + "' not implemented."); "function '" + func + "' not implemented.");
} }
} }
@ -267,7 +290,7 @@
tracedObjects[this.__OBJ_UID] = { tracedObjects[this.__OBJ_UID] = {
"created": new Date().getTime(), "created": new Date().getTime(),
"class": className "class": className
} };
egw.debug("log", "*" + this.__OBJ_UID + " (" + className + ")"); egw.debug("log", "*" + this.__OBJ_UID + " (" + className + ")");
} }
@ -284,7 +307,15 @@
// Enforce the constructor to be what we expect // Enforce the constructor to be what we expect
Class.prototype.constructor = Class; Class.prototype.constructor = Class;
// And make this class extendable /**
* Create a new Class that inherits from this class. The first parameter
* is an array which defines a set of interfaces the object has to
* implement. An interface is simply an object with named functions.
*
* @param {array} interfaces
* @param {object} prop
* @return {Class}
*/
Class.extend = classExtend; Class.extend = classExtend;
return Class; return Class;
@ -293,9 +324,15 @@
// The base Class implementation (does nothing) // The base Class implementation (does nothing)
this.Class = function(){}; this.Class = function(){};
// Create a new Class that inherits from this class. The first parameter /**
// is an array which defines a set of interfaces the object has to * Create a new Class that inherits from this class. The first parameter
// implement. An interface is simply an object with named functions. * is an array which defines a set of interfaces the object has to
* implement. An interface is simply an object with named functions.
*
* @param {array} interfaces
* @param {object} prop
* @return {Class}
*/
Class.extend = classExtend; Class.extend = classExtend;
// The base class has no attributes // The base class has no attributes
@ -356,12 +393,14 @@
} }
return "?"; return "?";
} };
} }
/** /**
* The implements function can be used to check whether the object * The implements function can be used to check whether the object
* implements the given interface. * implements the given interface.
*
* @param {Class} _iface interface to check
*/ */
Class.prototype.implements = function(_iface) { Class.prototype.implements = function(_iface) {
for (var key in _iface) for (var key in _iface)
@ -378,6 +417,8 @@
* The instanceOf function can be used to check for both - classes and * The instanceOf function can be used to check for both - classes and
* interfaces. Please don't change the case of this function as this * interfaces. Please don't change the case of this function as this
* affects IE and Opera support. * affects IE and Opera support.
*
* @param {Class} _obj object to check
*/ */
Class.prototype.instanceOf = function(_obj) { Class.prototype.instanceOf = function(_obj) {
if (_obj instanceof Interface) if (_obj instanceof Interface)

View File

@ -35,8 +35,8 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() {
/** /**
* Set translation for a given application * Set translation for a given application
* *
* @param string _app * @param {string} _app
* @param object _message message => translation pairs * @param {object} _messages message => translation pairs
* @memberOf egw * @memberOf egw
*/ */
set_lang_arr: function(_app, _messages) set_lang_arr: function(_app, _messages)
@ -50,8 +50,9 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() {
/** /**
* Translate a given phrase replacing optional placeholders * Translate a given phrase replacing optional placeholders
* *
* @param string _msg message to translate * @param {string} _msg message to translate
* @param string _arg1 ... _argN * @param {...string} _arg1 ... _argN
* @return {string}
*/ */
lang: function(_msg, _arg1) lang: function(_msg, _arg1)
{ {
@ -91,10 +92,10 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() {
/** /**
* Load default langfiles for an application: common, _appname, custom * Load default langfiles for an application: common, _appname, custom
* *
* @param _window * @param {DOMElement} _window
* @param {string} _appname name of application to load translations for * @param {string} _appname name of application to load translations for
* @param {function} _callback * @param {function} _callback
* @param _context * @param {object} _context
*/ */
langRequireApp: function(_window, _appname, _callback, _context) langRequireApp: function(_window, _appname, _callback, _context)
{ {
@ -114,17 +115,17 @@ egw.extend('lang', egw.MODULE_GLOBAL, function() {
* Includes the language files for the given applications -- if those * Includes the language files for the given applications -- if those
* do not already exist, include them. * do not already exist, include them.
* *
* @param _window is the window which needs the language -- this is * @param {DOMElement} _window is the window which needs the language -- this is
* needed as the "ready" event has to be postponed in that window until * needed as the "ready" event has to be postponed in that window until
* all lang files are included. * all lang files are included.
* @param _apps is an array containing the applications for which the * @param {array} _apps is an array containing the applications for which the
* data is needed as objects of the following form: * data is needed as objects of the following form:
* { * {
* app: <APPLICATION NAME>, * app: <APPLICATION NAME>,
* lang: <LANGUAGE CODE> * lang: <LANGUAGE CODE>
* } * }
* @param function _callback called after loading, if not given ready event will be postponed instead * @param {function} _callback called after loading, if not given ready event will be postponed instead
* @param object _context for callback * @param {object} _context for callback
*/ */
langRequire: function(_window, _apps, _callback, _context) { langRequire: function(_window, _apps, _callback, _context) {
// Get the ready and the files module for the given window // Get the ready and the files module for the given window

View File

@ -339,16 +339,17 @@ egw.extend('links', egw.MODULE_GLOBAL, function() {
/** /**
* Query a title of _app/_id * Query a title of _app/_id
* *
* @param string _app * @param {string} _app
* @param string|int _id * @param {(string|int)} _id
* @param function _callback optinal callback, required if for responses from the server * @param {function} _callback optinal callback, required if for responses from the server
* @param object _context context for the callback * @param {object} _context context for the callback
* @param {boolean} _force_reload true load again from server, even if already cached
* @return string|boolean|null string with title if it exist in local cache or null if not * @return string|boolean|null string with title if it exist in local cache or null if not
*/ */
link_title: function(_app, _id, _callback, _context) link_title: function(_app, _id, _callback, _context, _force_reload)
{ {
// check if we have a cached title --> return it direct // check if we have a cached title --> return it direct
if (typeof title_cache[_app] != 'undefined' && typeof title_cache[_app][_id] != 'undefined') if (typeof title_cache[_app] != 'undefined' && typeof title_cache[_app][_id] != 'undefined' && _force_reload !== true)
{ {
if (typeof _callback == 'function') if (typeof _callback == 'function')
{ {