From d310b14ecfcc1f36b8f436a39e8105ba4f901176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20St=C3=B6ckel?= Date: Mon, 5 Mar 2012 13:07:38 +0000 Subject: [PATCH] Client side API now supports modules which are instanciated per application or per window; removed et2_debug function from et2_core_common, now using corresponding API function. --- etemplate/js/et2_core_arrayMgr.js | 6 +- etemplate/js/et2_core_common.js | 57 +-- etemplate/js/et2_core_inheritance.js | 10 +- .../js/et2_core_phpExpressionCompiler.js | 2 +- etemplate/js/et2_core_widget.js | 23 +- etemplate/js/et2_dataview_model_columns.js | 2 +- .../js/et2_dataview_model_dataProvider.js | 2 +- etemplate/js/et2_dataview_view_grid.js | 4 +- .../js/et2_dataview_view_partitionNode.js | 2 +- ...dataview_view_partitionOrganizationNode.js | 4 +- .../js/et2_dataview_view_partitionTree.js | 2 +- etemplate/js/et2_dataview_view_rowProvider.js | 6 +- etemplate/js/et2_extension_nextmatch.js | 10 +- etemplate/js/et2_widget_grid.js | 4 +- etemplate/js/et2_widget_selectbox.js | 2 +- etemplate/js/et2_widget_template.js | 6 +- etemplate/js/etemplate2.js | 10 +- phpgwapi/js/jsapi/egw.js | 1 + phpgwapi/js/jsapi/egw_config.js | 2 +- phpgwapi/js/jsapi/egw_core.js | 337 ++++++++++++------ phpgwapi/js/jsapi/egw_debug.js | 70 ++++ phpgwapi/js/jsapi/egw_files.js | 153 ++++++++ phpgwapi/js/jsapi/egw_images.js | 2 +- phpgwapi/js/jsapi/egw_json.js | 325 +++++++++++++++++ phpgwapi/js/jsapi/egw_jsonq.js | 150 ++++---- phpgwapi/js/jsapi/egw_lang.js | 2 +- phpgwapi/js/jsapi/egw_links.js | 2 +- phpgwapi/js/jsapi/egw_preferences.js | 2 +- phpgwapi/js/jsapi/egw_user.js | 2 +- phpgwapi/js/jsapi/egw_utils.js | 165 +++++++++ 30 files changed, 1087 insertions(+), 278 deletions(-) create mode 100644 phpgwapi/js/jsapi/egw_debug.js create mode 100644 phpgwapi/js/jsapi/egw_files.js create mode 100644 phpgwapi/js/jsapi/egw_json.js create mode 100644 phpgwapi/js/jsapi/egw_utils.js diff --git a/etemplate/js/et2_core_arrayMgr.js b/etemplate/js/et2_core_arrayMgr.js index c911628ad0..cef2d246c8 100644 --- a/etemplate/js/et2_core_arrayMgr.js +++ b/etemplate/js/et2_core_arrayMgr.js @@ -35,7 +35,7 @@ var et2_arrayMgr = Class.extend({ // Hold a reference to the data if (typeof _data == "undefined" || !_data) { - et2_debug("error", "Invalid data passed to content array manager!"); + egw.debug("error", "Invalid data passed to content array manager!"); _data = {}; } @@ -224,7 +224,7 @@ var et2_arrayMgr = Class.extend({ catch(e) { proto.compiledExpressions[_ident] = null; - et2_debug("error", "Error while compiling PHP->JS ", e); + egw.debug("error", "Error while compiling PHP->JS ", e); } } @@ -239,7 +239,7 @@ var et2_arrayMgr = Class.extend({ } catch(e) { - et2_debug("error", e); + egw.debug("error", e); } } } diff --git a/etemplate/js/et2_core_common.js b/etemplate/js/et2_core_common.js index 61052f4427..9de0fa5554 100644 --- a/etemplate/js/et2_core_common.js +++ b/etemplate/js/et2_core_common.js @@ -27,49 +27,6 @@ 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 = 4; - -function et2_debug(_level) -{ - if (typeof console != "undefined") - { - // Get the passed parameters and remove the first entry - var args = []; - for (var i = 1; i < arguments.length; i++) - { - args.push(arguments[i]); - } - - if (_level == "log" && ET2_DEBUGLEVEL >= 4 && - typeof console.log == "function") - { - console.log.apply(console, args); - } - - if (_level == "info" && ET2_DEBUGLEVEL >= 3 && - typeof console.info == "function") - { - console.info.apply(console, args); - } - - if (_level == "warn" && ET2_DEBUGLEVEL >= 2 && - typeof console.warn == "function") - { - console.warn.apply(console, args); - } - - if (_level == "error" && ET2_DEBUGLEVEL >= 1 && - typeof console.error == "function") - { - console.error.apply(console, args); - } - } -} - /** * Array with all types supported by the et2_checkType function. */ @@ -202,7 +159,7 @@ function et2_checkType(_val, _type, _attr, _cname) function _err() { var res = et2_typeDefaults[_type]; - et2_debug("warn", "'" + _val + "' was not of specified _type '" + + egw.debug("warn", "'" + _val + "' was not of specified _type '" + _type + (_attr != null ? "' for attribute '" + _attr + "' " : "") + "and is now '" + res + "'"); @@ -270,7 +227,7 @@ function et2_checkType(_val, _type, _attr, _cname) } catch(e) { - et2_debug("error", "Error while parsing JS event handler code", e,_val); + egw.debug("error", "Error while parsing JS event handler code", e,_val); } } @@ -380,7 +337,7 @@ function et2_validateAttrib(_id, _attrib) if (typeof _attrib["name"] == "undefined") { _attrib["name"] = _id; - et2_debug("log", "Human name ('name'-Field) for attribute '" + + egw.debug("log", "Human name ('name'-Field) for attribute '" + _id + "' has not been supplied, set to '" + _id + "'"); console.debug(_attrib); } @@ -388,7 +345,7 @@ console.debug(_attrib); if (typeof _attrib["description"] == "undefined") { _attrib["description"] = ""; - et2_debug("log", "Description for attribute '" + + egw.debug("log", "Description for attribute '" + _id + "' has not been supplied"); } @@ -400,7 +357,7 @@ console.debug(_attrib); { if (et2_validTypes.indexOf(_attrib["type"]) < 0) { - et2_debug("error", "Invalid type for attribute '" + _id + + egw.debug("error", "Invalid type for attribute '" + _id + "' supplied."); } } @@ -655,7 +612,7 @@ function et2_insertLinkText(_text, _node, _target) { if(!_node) { - et2_debug("warn", "et2_insertLinkText called without node", _text, _node, _target); + egw.debug("warn", "et2_insertLinkText called without node", _text, _node, _target); return; } @@ -677,7 +634,7 @@ function et2_insertLinkText(_text, _node, _target) { if(!s.href) { - et2_debug("warn", "et2_activateLinks gave bad data", s, _node, _target); + egw.debug("warn", "et2_activateLinks gave bad data", s, _node, _target); s.href = ""; } var a = $j(document.createElement("a")) diff --git a/etemplate/js/et2_core_inheritance.js b/etemplate/js/et2_core_inheritance.js index 4f13db5882..50129a2ffd 100644 --- a/etemplate/js/et2_core_inheritance.js +++ b/etemplate/js/et2_core_inheritance.js @@ -273,7 +273,7 @@ "created": new Date().getTime(), "class": className } - et2_debug("log", "*" + this.__OBJ_UID + " (" + className + ")"); + egw.debug("log", "*" + this.__OBJ_UID + " (" + className + ")"); } if (this.init) @@ -323,7 +323,7 @@ if (getMem_freeMem_trace) { delete(tracedObjects[this.__OBJ_UID]); - et2_debug("log", "-" + this.__OBJ_UID); + egw.debug("log", "-" + this.__OBJ_UID); } // Delete every object entry @@ -382,7 +382,7 @@ } else { - et2_debug("error", this, "Attribute '" + _name + "' does not exist!"); + egw.debug("error", this, "Attribute '" + _name + "' does not exist!"); } }; @@ -417,7 +417,7 @@ } else { - et2_debug("warn", this, "Attribute '" + _name + "' does not exist!"); + egw.debug("warn", this, "Attribute '" + _name + "' does not exist!"); } }; @@ -445,7 +445,7 @@ { // Key does not exist - delete it and issue a warning delete(_attrs[key]); - et2_debug("warn", this, "Attribute '" + key + + egw.debug("warn", this, "Attribute '" + key + "' does not exist in " + _attrs.type+"!"); } } diff --git a/etemplate/js/et2_core_phpExpressionCompiler.js b/etemplate/js/et2_core_phpExpressionCompiler.js index 376da51375..818ed28c94 100644 --- a/etemplate/js/et2_core_phpExpressionCompiler.js +++ b/etemplate/js/et2_core_phpExpressionCompiler.js @@ -403,7 +403,7 @@ var js = _php_compileJSCode(_vars, syntaxTree); // Log the successfull compiling - et2_debug("log", "Compiled PHP " + _expr + " --> " + js); + egw.debug("log", "Compiled PHP " + _expr + " --> " + js); // Prepate the attributes for the function constuctor var attrs = []; diff --git a/etemplate/js/et2_core_widget.js b/etemplate/js/et2_core_widget.js index eedccd1a44..0488053be0 100644 --- a/etemplate/js/et2_core_widget.js +++ b/etemplate/js/et2_core_widget.js @@ -41,7 +41,7 @@ function et2_register_widget(_constructor, _types) // types. if (et2_registry[type]) { - et2_debug("warn", "Widget class registered for " + type + + egw.debug("warn", "Widget class registered for " + type + " will be overwritten."); } @@ -272,7 +272,7 @@ var et2_widget = Class.extend({ assign: function(_obj) { if (typeof _obj._children == "undefined") { - et2_debug("log", "Foo!"); + this.egw().debug("log", "Foo!"); } // Create a clone of all child elements of the given object @@ -345,7 +345,7 @@ var et2_widget = Class.extend({ } else { - et2_debug("error", this, "Widget is not supported by this widget class", _node); + this.egw().debug("error", this, "Widget is not supported by this widget class", _node); // throw("Widget is not supported by this widget class!"); } }, @@ -537,7 +537,7 @@ var et2_widget = Class.extend({ } else { - et2_debug("warn", "Attributes cannot be objects", this, key, data[key]); + this.egw().debug("warn", "Attributes cannot be objects", this, key, data[key]); } } } @@ -579,7 +579,7 @@ var et2_widget = Class.extend({ // Try again, but skip the fancy stuff // TODO: Figure out why the getEntry() call doesn't always work var entry = modifications.data[_node.getAttribute("id")]; - if(entry) et2_debug("warn", "getEntry("+_node.getAttribute("id")+") failed, but the data is there.", modifications, entry); + if(entry) this.egw().debug("warn", "getEntry("+_node.getAttribute("id")+") failed, but the data is there.", modifications, entry); } if(entry && entry.type) { @@ -687,8 +687,16 @@ var et2_widget = Class.extend({ return this._parent.egw(); } - // Return the global egw instance if none is given - return egw('phpgwapi'); + // Get the window this object belongs to + var wnd = null; + if (this.implements(et2_IDOMNode)) + { + var node = this.getDOMNode(); + wnd = node.ownerDocument.parentNode || node.ownerDocument.defaultView; + } + + // If we're the root object, return the phpgwapi API instance + return egw('phpgwapi', wnd); } return this._egw; @@ -831,4 +839,3 @@ var et2_widget = Class.extend({ } }); - diff --git a/etemplate/js/et2_dataview_model_columns.js b/etemplate/js/et2_dataview_model_columns.js index 893fad372e..14f4274ee6 100755 --- a/etemplate/js/et2_dataview_model_columns.js +++ b/etemplate/js/et2_dataview_model_columns.js @@ -121,7 +121,7 @@ var et2_dataview_column = Class.extend({ } else { - et2_debug("warn", "Invalid visibility option for column: ", _value); + this.egw().debug("warn", "Invalid visibility option for column: ", _value); } } }); diff --git a/etemplate/js/et2_dataview_model_dataProvider.js b/etemplate/js/et2_dataview_model_dataProvider.js index 1356e98c85..c08d9b3fa3 100644 --- a/etemplate/js/et2_dataview_model_dataProvider.js +++ b/etemplate/js/et2_dataview_model_dataProvider.js @@ -96,7 +96,7 @@ var et2_dataview_dataProvider = Class.extend(et2_IDataProvider, { if (typeof this._registeredRows[_idx] != "undefined") { - et2_debug("warn", "Overriding data row for index " + _idx); + this.egw().debug("warn", "Overriding data row for index " + _idx); } // Associate the given data row with that index diff --git a/etemplate/js/et2_dataview_view_grid.js b/etemplate/js/et2_dataview_view_grid.js index 81a9943b60..4beb8151c8 100644 --- a/etemplate/js/et2_dataview_view_grid.js +++ b/etemplate/js/et2_dataview_view_grid.js @@ -214,9 +214,9 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, { // regarding to the count of elements managed in it if (count < Math.pow(ET2_PARTITION_TREE_WIDTH, depth - 1)) { - et2_debug("info", "Rebuilding dataview partition tree"); + this.egw().debug("info", "Rebuilding dataview partition tree"); this._partitionTree.rebuild(); - et2_debug("info", "Done."); + this.egw().debug("info", "Done."); } // Reset the "treeChanged" function. diff --git a/etemplate/js/et2_dataview_view_partitionNode.js b/etemplate/js/et2_dataview_view_partitionNode.js index 43203a52b4..a1cfd572e3 100644 --- a/etemplate/js/et2_dataview_view_partitionNode.js +++ b/etemplate/js/et2_dataview_view_partitionNode.js @@ -144,7 +144,7 @@ var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight, // complete tree! if (isNaN(this._height)) { - et2_debug("error", "calculateHeight returned a NaN value!"); + this.egw().debug("error", "calculateHeight returned a NaN value!"); this._height = 0; } diff --git a/etemplate/js/et2_dataview_view_partitionOrganizationNode.js b/etemplate/js/et2_dataview_view_partitionOrganizationNode.js index 1962054d48..92dede388f 100644 --- a/etemplate/js/et2_dataview_view_partitionOrganizationNode.js +++ b/etemplate/js/et2_dataview_view_partitionOrganizationNode.js @@ -506,7 +506,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend( if (Math.abs(actualTop - calculatedTop) > 1) { - et2_debug("warn", i, "Position missmatch at idx ", idx, + egw.debug("warn", i, "Position missmatch at idx ", idx, actualTop, calculatedTop, node); } @@ -515,7 +515,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend( if (Math.abs(actualHeight - calculateHeight) > 1) { - et2_debug("warn", i, "Height missmatch at idx ", idx, + egw.debug("warn", i, "Height missmatch at idx ", idx, actualHeight, calculateHeight, node); } } diff --git a/etemplate/js/et2_dataview_view_partitionTree.js b/etemplate/js/et2_dataview_view_partitionTree.js index df79b6b50b..4cafab8b01 100644 --- a/etemplate/js/et2_dataview_view_partitionTree.js +++ b/etemplate/js/et2_dataview_view_partitionTree.js @@ -37,7 +37,7 @@ var et2_dataview_partitionTree = et2_dataview_partitionOrganizationNode.extend({ this._dataProvider = _dataProvider; this._rowProvider = _rowProvider; - et2_debug("Creating partition tree with ", this._count, + egw.debug("Creating partition tree with ", this._count, " elements of avgHeight ", this._avgHeight); // Append a spacer node to the children diff --git a/etemplate/js/et2_dataview_view_rowProvider.js b/etemplate/js/et2_dataview_view_rowProvider.js index 7a4a67b674..ebcddb7f46 100644 --- a/etemplate/js/et2_dataview_view_rowProvider.js +++ b/etemplate/js/et2_dataview_view_rowProvider.js @@ -166,7 +166,7 @@ var et2_dataview_rowProvider = Class.extend({ if (!supportsAttr) { - et2_debug("warn", "et2_IDetachedDOM widget " + + egw.debug("warn", "et2_IDetachedDOM widget " + widget._type + " does not support " + data.attribute); } @@ -203,7 +203,7 @@ var et2_dataview_rowProvider = Class.extend({ // Issue a warning - widgets which do not implement et2_IDOMNode // are very slow - et2_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); // Set the placeholder for the entry to null @@ -399,7 +399,7 @@ var et2_dataview_rowProvider = Class.extend({ nodes[j] = entry.nodeFuncs[j](_row[0]); } if(typeof nodes[0] == "undefined") - et2_debug("warn", "Missing node", entry.widget.id,nodes, entry ); + egw.debug("warn", "Missing node", entry.widget.id,nodes, entry ); // Set the array managers first entry.widget._mgrs = mgrs; diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index c436191fbd..c333ee7b07 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -247,7 +247,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { }, applyFilters: function() { - et2_debug("info", "Changing nextmatch filters to ", this.activeFilters); + this.egw().debug("info", "Changing nextmatch filters to ", this.activeFilters); // Clear the dataprovider and the dataview container - this will cause // the grid to reload. @@ -295,7 +295,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { var colName = name + (name != "" && child_names.length > 0 ? "_" : "") + child_names.join("_"); if(colName == "") { - et2_debug("info", "Unable to generate nm column name for ", _widget); + this.egw().debug("info", "Unable to generate nm column name for ", _widget); } return colName; }, @@ -417,7 +417,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { } } } else if (colMgr.columns[i].fixedWidth) { - et2_debug("info", "Could not save column width - no name", colMgr.columns[i].id); + this.egw().debug("info", "Could not save column width - no name", colMgr.columns[i].id); } } @@ -663,7 +663,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { if (!template.proxiedTemplate) { - et2_debug("error", "Error while loading definition template for" + + this.egw().debug("error", "Error while loading definition template for" + "nextmatch widget."); return; } @@ -676,7 +676,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { } else { - et2_debug("error", "Nextmatch widget expects a grid to be the " + + this.egw().debug("error", "Nextmatch widget expects a grid to be the " + "first child of the defined template."); return; } diff --git a/etemplate/js/et2_widget_grid.js b/etemplate/js/et2_widget_grid.js index 9ad97359a5..de140dcbe8 100644 --- a/etemplate/js/et2_widget_grid.js +++ b/etemplate/js/et2_widget_grid.js @@ -177,7 +177,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM], { function _readColNode(node, nodeName) { if (y >= h) { - et2_debug("warn", "Skipped grid cell in column, '" + + this.egw().debug("warn", "Skipped grid cell in column, '" + nodeName + "'"); return; } @@ -234,7 +234,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM], { function _readRowNode(node, nodeName) { if (x >= w) { - et2_debug("warn", "Skipped grid cell in row, '" + + this.egw().debug("warn", "Skipped grid cell in row, '" + nodeName + "'"); return; } diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js index 75116638ab..bb84c47107 100644 --- a/etemplate/js/et2_widget_selectbox.js +++ b/etemplate/js/et2_widget_selectbox.js @@ -190,7 +190,7 @@ var et2_selectbox = et2_inputWidget.extend({ { if(jQuery("option[value='"+_value+"']", this.input).attr("selected", true).length == 0) { - et2_debug("warning", "Tried to set value that isn't an option", this, _value); + this.egw().debug("warning", "Tried to set value that isn't an option", this, _value); //console.trace(); } } diff --git a/etemplate/js/et2_widget_template.js b/etemplate/js/et2_widget_template.js index ede315af81..20b52ef9d9 100644 --- a/etemplate/js/et2_widget_template.js +++ b/etemplate/js/et2_widget_template.js @@ -67,10 +67,14 @@ var et2_template = et2_DOMWidget.extend({ if (this.id != "") { + // Get the window this object belongs to + var node = this.getDOMNode(); + var wnd = node.ownerDocument.parentNode || node.ownerDocument.defaultView; + // Set the api instance to the first part of the name of the // template var splitted = this.id.split('.'); - this.setApiInstance(egw(splitted[0])); + this.setApiInstance(egw(splitted[0], wnd)); this.createProxy(); } diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index 991890a2fb..2ef4598b72 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -119,7 +119,7 @@ etemplate2.prototype._createArrayManagers = function(_data) { if (typeof _data[neededEntries[i]] == "undefined" || !_data[neededEntries[i]]) { - et2_debug("log", "Created not passed entry '" + neededEntries[i] + + egw.debug("log", "Created not passed entry '" + neededEntries[i] + "' in data array."); _data[neededEntries[i]] = {}; } @@ -257,7 +257,7 @@ etemplate2.prototype.submit = function(button) } else { - et2_debug("info", "Form got submitted with values: ", values); + egw.debug("info", "Form got submitted with values: ", values); } } } @@ -316,7 +316,7 @@ etemplate2.prototype.getValues = function(_root) } else { - et2_debug("error", "ID collision while writing at path " + + egw.debug("error", "ID collision while writing at path " + "node '" + path[i] + "'"); } } @@ -324,7 +324,7 @@ etemplate2.prototype.getValues = function(_root) // Check whether the entry is really undefined if (typeof _target[id] != "undefined") { - et2_debug("error", _widget, "Overwriting value of '" + _widget.id + + egw.debug("error", _widget, "Overwriting value of '" + _widget.id + "', id exists twice!"); } @@ -374,6 +374,6 @@ if (typeof egw_json_register_plugin != "undefined") } else { - et2_debug("info", "EGW JSON Plugin could not be registered, running ET2 standalone."); + egw.debug("info", "EGW JSON Plugin could not be registered, running ET2 standalone."); } diff --git a/phpgwapi/js/jsapi/egw.js b/phpgwapi/js/jsapi/egw.js index e926bfdc9f..db5eab4f80 100644 --- a/phpgwapi/js/jsapi/egw.js +++ b/phpgwapi/js/jsapi/egw.js @@ -21,5 +21,6 @@ egw_config; egw_images; egw_jsonq; + egw_files; */ diff --git a/phpgwapi/js/jsapi/egw_config.js b/phpgwapi/js/jsapi/egw_config.js index 421786e92d..8034c84916 100644 --- a/phpgwapi/js/jsapi/egw_config.js +++ b/phpgwapi/js/jsapi/egw_config.js @@ -16,7 +16,7 @@ egw_core; */ -egw().extend('config', function() { +egw.extend('config', egw.MODULE_GLOBAL, function() { /** * Clientside config diff --git a/phpgwapi/js/jsapi/egw_core.js b/phpgwapi/js/jsapi/egw_core.js index 334854a2c0..0ecdd434e3 100644 --- a/phpgwapi/js/jsapi/egw_core.js +++ b/phpgwapi/js/jsapi/egw_core.js @@ -1,4 +1,4 @@ -/** + /** * EGroupware clientside API object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License @@ -14,21 +14,6 @@ var egw; -/** - * IE Fix for array.indexOf - */ -if (typeof Array.prototype.indexOf == "undefined") -{ - Array.prototype.indexOf = function(_elem) { - for (var i = 0; i < this.length; i++) - { - if (this[i] === _elem) - return i; - } - return -1; - }; -} - /** * 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. @@ -51,9 +36,111 @@ if (typeof Array.prototype.indexOf == "undefined") // Extend the egw object for (var key in _from) { - if (typeof _to[key] === 'undefined') + _to[key] = _from[key]; + } + } + + function createEgwInstance(_egw, _modules, _list, _app, _window) + { + // Clone the global object + var instance = cloneObject(_egw); + + // Let "_window" be exactly null, if it evaluates to false + _window = _window ? _window : null; + + // Set the application name and the window the API instance belongs to + instance.appName = _app ? _app : null; + instance.window = _window; + + // Insert the newly created instance into the instances list + _list.push({ + 'window': _window, + 'app': _app, + 'instance': instance + }); + + // Re-instanciate all modules which are marked as "local" + for (var key in _modules) + { + // Get the module object + var mod = _modules[key]; + + if (mod.flags !== _egw.MODULE_GLOBAL) { - _to[key] = _from[key]; + // If the module is marked as application local and an + // application instance is given or if the module is marked as + // window local and a window instance is given, re-instanciate + // this module. + if (((mod.flags & _egw.MODULE_APP_LOCAL) && (_app)) || + ((mod.flags & _egw.MODULE_WND_LOCAL) && (_window))) + { + var extension = mod.code.call(instance, instance, + _window ? _window : window); + mergeObjects(instance, extension); + } + } + } + + return instance; + } + + function getEgwInstance(_egw, _modules, _instances, _app, _window) + { + // Generate the hash key for the instance descriptor object + var hash = _window ? _app + "_" + _window.location : _app; + + // Let "_window" be exactly null, if it evaluates to false + _window = _window ? _window : null; + + // Create a new entry if the calculated hash does not exist + if (typeof _instances[hash] === 'undefined') + { + _instances[hash] = []; + return createEgwInstance(_egw, _modules, _instances[hash], _app, + _window); + } + else + { + // Otherwise search for the api instance corresponding to the given + // window + for (var i = 0; i < _instances[hash].length; i++) + { + if (_instances[hash][i].window === _window) + { + return _instances[hash][i].instance; + } + } + } + + // If we're still here, no API instance for the given window has been + // found -- create a new entry + return createEgwInstance(_egw, _modules, _instances[hash], _app, _window); + } + + function cleanupEgwInstances(_instances) + { + // Iterate over the egw instances and check whether the window they + // correspond to is still open. + for (var key in _instances) + { + for (var i = _instances[key].length - 1; i >= 0; i--) + { + // Get the instance descriptor + var instDescr = _instances[key][i]; + + // Check whether the window this API instance belongs to is + // still opened. If not, remove the API instance. + if (instDescr.window && instDescr.window.closed) + { + _instances[key].splice(i, 1) + } + } + + // If all instances for the current hash have been deleted, delete + // the hash entry itself + if (_instances[key].length === 0) + { + delete _instances[key]; } } } @@ -69,67 +156,112 @@ if (typeof Array.prototype.indexOf == "undefined") else { /** - * EGW_DEBUGLEVEL specifies which messages are printed to the console. - * Decrease the value of EGW_DEBUGLEVEL to get less messages. + * Modules contains all currently loaded egw extension modules. A module + * is stored as an object of the following form: + * { + * name: , + * code: , + * flags: , + * app: , + * window: + * } */ - var modules = []; + var instances = {}; - var localEgw = {}; + /** + * Set a interval which is used to cleanup unused API instances all 10 + * seconds. + */ + window.setInterval(function() {cleanupEgwInstances(instances);}, 10000); /** * The egw function returns an instance of the client side api. If no * parameter is given, an egw istance, which is not bound to a certain * application is returned. + * You may pass either an application name (as string) to the egw + * 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 + * given, the root window will be used. */ - egw = function(_app) { + egw = function() { - // If no argument is given, simply return the global egw object, or - // check whether 'window.egw_appName' is set correctly. - if (typeof _app === 'undefined') + // Get the window/app reference + var _app = ""; + var _window = window; + + switch (arguments.length) { - // TODO: Remove this code, window.egw_appName will be removed - // in the future. - if (typeof window.egw_appName == 'string') - { - _app = window.egw_appName; - } - else - { + case 0: + // Return the global instance return egw; - } + + case 1: + if (typeof arguments[0] === 'string') + { + _app = arguments[0]; + } + else if (typeof arguments[0] === 'object') + { + _window = arguments[0]; + } + break; + + case 2: + _app = arguments[0]; + _window = arguments[1]; + break; + + default: + throw "Invalid count of parameters"; } - if (typeof _app == 'string') - { - // If a argument is given, this represents the current application - // name. Check whether we already have a copy of the egw object for - // that application. If yes, return it. - if (typeof localEgw[_app] === 'undefined') - { - // Otherwise clone the global egw object, set the application - // name and return it - localEgw[_app] = cloneObject(egw); - localEgw[_app].appName = _app; - } - - return localEgw[_app]; - } - - this.debug("error", "Non-string argument given to the egw function."); + // Generate an API instance + return getEgwInstance(egw, modules, instances, _app, _window); } var globalEgw = { + /** + * The MODULE_GLOBAL flag describes a module as global. A global + * module always works on the same data. + */ + MODULE_GLOBAL: 0x00, + + /** + * The MODULE_APP_LOCAL flag is used to describe a module as local + * for each application. Each time an api object is requested for + * another application, the complete module gets recreated. + */ + MODULE_APP_LOCAL: 0x01, + + /** + * The MODULE_WND_LOCAL flag is used to describe a module as local + * for each window. Each time an api object is requested for another + * window, the complete module gets recreated. + */ + MODULE_WND_LOCAL: 0x02, + /** * Name of the application the egw object belongs to. */ appName: null, + /** + * Reference to the window this egw object belongs to. + */ + window: window, + /** * Returns the current application name. The current application * name equals the name, which was given when calling the egw @@ -159,74 +291,65 @@ if (typeof Array.prototype.indexOf == "undefined") * * @param _module should be a string containing the name of the new * module. + * @param _flags specifies whether the extension should be treated + * as a local or a global module. * @param _code should be a function, which returns an object that * should extend the egw object. */ - extend: function(_module, _code) { + extend: function(_module, _flags, _code) { - // Check whether the given module has already been loaded. - if (modules.indexOf(_module) < 0) { + // Check whether that module is already registered + if (typeof modules[_module] === 'undefined') + { + // Create a new module entry + modules[_module] = { + 'code': _code, + 'flags': _flags, + 'name': _module + }; - // Call the function specified by "_code" which returns - // nothing but an object containing the extension. - var content = _code.call(this); + // Generate the global extension + var globalExtension = _code.call(egw, egw, window); - // Merge the extension into the egw function - mergeObjects(egw, content); + // Merge the global extension into the egw function + mergeObjects(egw, globalExtension); - // Merge the extension into the local egw object - for (var key in localEgw) { - mergeObjects(localEgw[key], content); + // Iterate over the instances and merge the modules into + // them + for (var key in instances) + { + for (var i = 0; i < instances[key].length; i++) + { + // Get the instance descriptor + var instDescr = instances[key][i]; + + // Merge the module into the instance + if (_flags !== egw.MODULE_GLOBAL) + { + mergeObjects(instDescr.instance, _code.call( + instDescr.instance, instDescr.instance, + instDescr.window ? instDescr.window : window)); + } + else + { + mergeObjects(instDescr.instance, globalExtension); + } + } } - - // Register the module as loaded - modules.push(_module); } }, - /** - * The debug function can be used to send a debug message to the - * java script console. The first parameter specifies the debug - * level, all other parameters are passed to the corresponding - * console function. - */ - debug: function(_level) { - if (typeof console != "undefined") - { - // Get the passed parameters and remove the first entry - var args = []; - for (var i = 1; i < arguments.length; i++) - { - args.push(arguments[i]); - } + dumpModules: function() { + return modules; + }, - if (_level == "log" && EGW_DEBUGLEVEL >= 4 && - typeof console.log == "function") - { - console.log.apply(console, args); - } - - if (_level == "info" && EGW_DEBUGLEVEL >= 3 && - typeof console.info == "function") - { - console.info.apply(console, args); - } - - if (_level == "warn" && EGW_DEBUGLEVEL >= 2 && - typeof console.warn == "function") - { - console.warn.apply(console, args); - } - - if (_level == "error" && EGW_DEBUGLEVEL >= 1 && - typeof console.error == "function") - { - console.error.apply(console, args); - } - } + dumpInstances: function() { + return instances; } + }; + // Merge the globalEgw functions into the egw object. mergeObjects(egw, globalEgw); } })(); diff --git a/phpgwapi/js/jsapi/egw_debug.js b/phpgwapi/js/jsapi/egw_debug.js new file mode 100644 index 0000000000..55f54a2dea --- /dev/null +++ b/phpgwapi/js/jsapi/egw_debug.js @@ -0,0 +1,70 @@ +/** + * EGroupware clientside API 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 (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw.extend('debug', egw.MODULE_WND_LOCAL, function(_egw, _wnd) { + + /** + * DEBUGLEVEL specifies which messages are printed to the console. + * Decrease the value of EGW_DEBUGLEVEL to get less messages. + */ + var DEBUGLEVEL = 4; + + /** + * The debug function can be used to send a debug message to the + * java script console. The first parameter specifies the debug + * level, all other parameters are passed to the corresponding + * console function. + */ + return { + debug: function(_level) { + if (typeof _wnd.console != "undefined") + { + // Get the passed parameters and remove the first entry + var args = []; + for (var i = 1; i < arguments.length; i++) + { + args.push(arguments[i]); + } + + if (_level == "log" && DEBUGLEVEL >= 4 && + typeof _wnd.console.log == "function") + { + _wnd.console.log.apply(_wnd.console, args); + } + + if (_level == "info" && DEBUGLEVEL >= 3 && + typeof _wnd.console.info == "function") + { + _wnd.console.info.apply(_wnd.console, args); + } + + if (_level == "warn" && DEBUGLEVEL >= 2 && + typeof _wnd.console.warn == "function") + { + _wnd.console.warn.apply(_wnd.console, args); + } + + if (_level == "error" && DEBUGLEVEL >= 1 && + typeof _wnd.console.error == "function") + { + _wnd.console.error.apply(_wnd.console, args); + } + } + } + } +}); diff --git a/phpgwapi/js/jsapi/egw_files.js b/phpgwapi/js/jsapi/egw_files.js new file mode 100644 index 0000000000..476beac8c8 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_files.js @@ -0,0 +1,153 @@ +/** + * EGroupware clientside API 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 (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + jquery.jquery; // Used for traversing the DOM + egw_core; + egw_debug; +*/ + +egw.extend('files', egw.MODULE_GLOBAL, function() { + + /** + * Array which contains all currently bound in javascript and css files. + */ + var files = {}; + + /** + * Gather all already loaded JavaScript and CSS files on document load. + * + * TODO: Currently this can only contain the JS files present in the main + * window. + */ + $j(document).ready(function() { + $j("script, link").each(function() { + var elem = $j(this); + + if (elem.attr("src")) + { + files[elem.attr("src")] = true; + } + + if (elem.attr("href")) + { + files[elem.attr("href")] = true; + } + + }); + }); + + function includeJSFile(_jsFile, _callback, _context) + { + var alreadyLoaded = false; + + if (typeof files[_jsFile] === 'undefined') + { + // Create the script node which contains the new file + var scriptnode = document.createElement('script'); + scriptnode.type = "text/javascript"; + scriptnode.src = _jsFile; + scriptnode._originalSrc = _jsFile; + + // Setup the 'onload' handler for FF, Opera, Chrome + scriptnode.onload = function(e) { + this.debug('info', 'Retrieved JS file "%s" from server', _jsFile); + _callback.call(_context, _jsFile); + }; + + // IE + if (typeof scriptnode.readyState != 'undefined') + { + if (scriptnode.readyState != 'complete' && + scriptnode.readyState != 'loaded') + { + scriptnode.onreadystatechange = function() { + var node = window.event.srcElement; + if (node.readyState == 'complete' || node.readyState == 'loaded') + { + this.debug('info', 'Retrieved JS file "%s" from server', _jsFile); + _callback.call(_context, _jsFile); + } + }; + } + else + { + alreadyLoaded = true; + } + } + + // Append the newly create script node to the head + var head = document.getElementsByTagName('head')[0]; + head.appendChild(scriptnode); + + // Request the given javascript file + this.debug('info', 'Requested JS file "%s" from server', _jsFile); + } + + // If the file is already loaded, call the callback + if (alreadyLoaded) + { + window.setTimeout( + function() { + _callback.call(_context, _jsFile); + }, 0); + } + } + + return { + includeJS: function(_jsFiles, _callback, _context) { + // Also allow including a single javascript file + if (typeof _jsFiles === 'string') + { + _jsFiles = [_jsFiles]; + } + + var loaded = 0; + + // Include all given JS files, if all are successfully loaded, call + // the context function + for (var i = 0; i < _jsFiles.length; i++) + { + includeJSFile.call(this, _jsFiles[i], function(_file) { + loaded++; + if (loaded == _jsFiles.length && _callback) { + _callback.call(_context); + } + }); + } + }, + + includeCSS: function(_cssFile) { + //Check whether the requested file has already been included + if (typeof files[_cssFile] === 'undefined') + { + files[_cssFile] = true; + + // Create the node which is used to include the css fiel + var cssnode = document.createElement('link'); + cssnode.type = "text/css"; + cssnode.rel = "stylesheet"; + cssnode.href = _cssFile; + + // Get the head node and append the newly created "link" node + // to it. + var head = document.getElementsByTagName('head')[0]; + head.appendChild(cssnode); + } + } + } + +}); + + diff --git a/phpgwapi/js/jsapi/egw_images.js b/phpgwapi/js/jsapi/egw_images.js index 6a169c0a74..2c4fd4c167 100644 --- a/phpgwapi/js/jsapi/egw_images.js +++ b/phpgwapi/js/jsapi/egw_images.js @@ -16,7 +16,7 @@ egw_core; */ -egw().extend('images', function() { +egw.extend('images', egw.MODULE_GLOBAL, function() { /** * Map to serverside available images for users template-set diff --git a/phpgwapi/js/jsapi/egw_json.js b/phpgwapi/js/jsapi/egw_json.js new file mode 100644 index 0000000000..deafed51e2 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_json.js @@ -0,0 +1,325 @@ +/** + * EGroupware clientside API 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 (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + jquery.jquery; + + egw_core; + egw_utils; + egw_files; +*/ + +egw.extend('json', egw.MODULE_WND_LOCAL, function(_egw, _wnd) { + + /** + * Object which contains all registered handlers for JS responses. + * The handlers are organized per response type in the top level of the + * object, where each response type can have an array of handlers attached + * to it. + */ + var plugins = {}; + + /** + * Internal implementation of the JSON request object. + */ + function json_request(_menuaction, _parameters, _callback, _context, _egw) + { + // Initialize undefined parameters + if (typeof _parameters === 'undefined') + { + _parameters = []; + } + + if (typeof _callback === 'undefined') + { + _callback = null; + } + + if (typeof _context === 'undefined') + { + _context = null; + } + + // Copy the parameters + this.parameters = _parameters; + this.egw = _egw; + this.callback = _callback; + this.context = _context; + + this.request = null; + this.sender = null; + this.callback = null; + + this.onLoadFinish = null; + this.jsFiles = 0; + this.jsCount = 0; + + this.alertHandler = this.alertFunc; + } + + /** + * Function which is currently used to display alerts -- may be replaced by + * some API function. + */ + json_request.prototype.alertFunc = function(_message, _details) + { + alert(_message); + if(_details) _egw_json_debug_log(_message, _details); + } + + var json = { + + /** The constructor of the egw_json_request class. + * + * @param _menuaction the menuaction function which should be called and + * which handles the actual request. If the menuaction is a full featured + * url, this one will be used instead. + * @param _parameters which should be passed to the menuaction function. + * @param _callback specifies the callback function which should be + * called, once the request has been sucessfully executed. + * @param _context is the context which will be used for the callback function + */ + request: function(_menuaction, _parameters, _callback, _context) + { + return new json_request(_menuaction, _parameters, _callback, + _context, this); + } + + /** + * Registers a new handler plugin. + * + * @param _callback is the callback function which should be called + * whenever a response is comming from the server. + * @param _context is the context in which the callback function should + * be called. If null is given, the plugin is executed in the context + * of the request object. + * @param _type is an optional parameter defaulting to 'global'. + * it describes the response type which this plugin should be + * handling. + */ + registerJSONPlugin: function(_callback, _context, _type) + { + // _type defaults to 'global' + if (typeof _type === 'undefined') + { + _type = 'global'; + } + + // Create an array for the given category inside the plugins object + if (typeof plugins[_type] === 'undefined') + { + plugins[_type] = []; + } + + // Add the entry + plugins[_type].push({ + 'callback': _callback, + 'context': _context, + }); + }, + + /** + * Removes a previously registered plugin. + * + * @param _callback is the callback function which should be called + * whenever a response is comming from the server. + * @param _context is the context in which the callback function should + * be called. + * @param _type is an optional parameter defaulting to 'global'. + * it describes the response type which this plugin should be + * handling. + */ + unregisterJSONPlugin: function(_callback, _context, _type) + { + // _type defaults to 'global' + if (typeof _type === 'undefined') + { + _type = 'global'; + } + + if (typeof plugins[_type] !== 'undefined') { + for (var i = 0; i < plugins[_type].length; i++) + { + if (plugins[_type][i].callback == _callback && + plugins[_type][i].context == _context) + { + plugins[_type].slice(i, 1); + break; + } + } + } + } + }; + + // Regisert the "alert" plugin + json.registerPlugin(function(type, res) { + //Check whether all needed parameters have been passed and call the alertHandler function + if ((typeof res.data.message != 'undefined') && + (typeof res.data.details != 'undefined')) + { + this.alertHandler( + res.data.message, + res.data.details) + return true; + } + throw 'Invalid parameters'; + }, null, 'alert'); + + // Register the "assign" plugin + json.registerPlugin(function(type, res) { + //Check whether all needed parameters have been passed and call the alertHandler function + if ((typeof res.data.id != 'undefined') && + (typeof res.data.key != 'undefined') && + (typeof res.data.value != 'undefined')) + { + var obj = document.getElementById(res.data.id); + if (obj) + { + obj[res.data.key] = res.data.value; + + if (res.data.key == "innerHTML") + { + egw_insertJS(res.data.value); + } + + return true; + } + + return false; + } + throw 'Invalid parameters'; + }, null, 'assign'); + + // Register the "data" plugin + json.registerPlugin(function(type, res) { + //Callback the caller in order to allow him to handle the data + if (this.callback) + { + this.callback.call(this.sender, res.data); + return true; + } + }, null, 'data'); + + // Register the "script" plugin + json.registerPlugin(function(type, res) { + if (typeof res.data == 'string') + { + try + { + var func = new Function(res.data); + func.call(window); + } + catch (e) + { + this.egw.debug('error', 'Error while executing script: ', + res.data) + } + return true; + } + throw 'Invalid parameters'; + }, null, 'script'); + + // Register the "apply" plugin + json.registerPlugin(function(type, res) { + if (typeof res.data.func == 'string' && + typeof window[res.data.func] == 'function') + { + try + { + window[res.data.func].apply(window, res.data.parms); + } + catch (e) + { + this.egw.debug('error', 'Function', res.data.func, + 'Parameters', res.data.parms); + } + return true; + } + throw 'Invalid parameters'; + }, null, 'apply'); + + // Register the "jquery" plugin + json.registerPlugin(function(type, res) { + if (typeof res.data.select == 'string' && + typeof res.data.func == 'string') + { + try + { + var jQueryObject = $j(res.data.select, this.context); + jQueryObject[res.data.func].apply(jQueryObject, res.data.parms); + } + catch (e) + { + this.egw.debug('error', 'Function', res.data.func, + 'Parameters', res.data.parms); + } + return true; + } + throw 'Invalid parameters'; + }, null, 'jquery'); + + // Register the "redirect" plugin + json.registerPlugin(function(type, res) { + //console.log(res.data.url); + if (typeof res.data.url == 'string' && + typeof res.data.global == 'boolean') + { + //Special handling for framework reload + res.data.global |= (res.data.url.indexOf("?cd=10") > 0); + + if (res.data.global) + { + egw_topWindow().location.href = res.data.url; + } + else + { + egw_appWindowOpen(this.app, res.data.url); + } + return true; + } + throw 'Invalid parameters'; + }, null, 'redirect'); + + // Register the 'css' plugin + json.registerPlugin(function(type, res) { + if (typeof res.data == 'string') + { + this.egw.includeCSS(res.data); + return true; + } + throw 'Invalid parameters'; + }, null, 'css'); + + // Register the 'js' plugin + json.registerPlugin(function(type, res) { + if (typeof res.data == 'string') + { + this.jsCount++; + var self = this; + + this.egw.includeJS(res.data, function() { + self.jsFiles++; + if (self.jsFiles == self.jsCount && this.onLoadFinish) + { + this.onLoadFinish.call(this.sender); + } + }); + } + throw 'Invalid parameters'; + }, null, 'js'); + + // Return the extension + return json; +}); + diff --git a/phpgwapi/js/jsapi/egw_jsonq.js b/phpgwapi/js/jsapi/egw_jsonq.js index ec33ef55ab..39f491939e 100644 --- a/phpgwapi/js/jsapi/egw_jsonq.js +++ b/phpgwapi/js/jsapi/egw_jsonq.js @@ -14,9 +14,10 @@ /*egw:uses egw_core; + egw_debug; */ -egw().extend('jsonq', function() { +egw.extend('jsonq', egw.MODULE_GLOBAL, function() { /** * Queued json requests (objects with attributes menuaction, parameters, context, callback, sender and callbeforesend) @@ -35,6 +36,79 @@ egw().extend('jsonq', function() { */ var jsonq_timer = null; + /** + * Dispatch responses received + * + * @param object _data uid => response pairs + */ + function jsonq_callback(_data) + { + if (typeof _data != 'object') throw "jsonq_callback called with NO object as parameter!"; + + var json = new egw_json_request('none'); + for(var uid in _data) + { + if (typeof jsonq_queue[uid] == 'undefined') + { + console.log("jsonq_callback received response for not existing queue uid="+uid+"!"); + console.log(_data[uid]); + continue; + } + var job = jsonq_queue[uid]; + var response = _data[uid]; + + // fake egw_json_request object, to call it with the current response + json.callback = job.callback; + json.sender = job.sender; + json.handleResponse({response: response}); + + delete jsonq_queue[uid]; + } + // if nothing left in queue, stop interval-timer to give browser a rest + if (jsonq_timer && typeof jsonq_queue['u'+(jsonq_uid-1)] != 'object') + { + window.clearInterval(jsonq_timer); + jsonq_timer = null; + } + } + + /** + * Send the whole job-queue to the server in a single json request with menuaction=queue + */ + function jsonq_send() + { + if (jsonq_uid > 0 && typeof jsonq_queue['u'+(jsonq_uid-1)] == 'object') + { + var jobs_to_send = {}; + var something_to_send = false; + for(var uid in jsonq_queue) + { + var job = jsonq_queue[uid]; + + if (job.menuaction == 'send') continue; // already send to server + + // if job has a callbeforesend callback, call it to allow it to modify pararmeters + if (typeof job.callbeforesend == 'function') + { + job.callbeforesend.call(job.sender, job.parameters); + } + jobs_to_send[uid] = { + menuaction: job.menuaction, + parameters: job.parameters + }; + job.menuaction = 'send'; + job.parameters = null; + something_to_send = true; + } + if (something_to_send) + { + // TODO: Passing this to the "home" application looks quite ugly + var request = new egw_json_request('home.queue', jobs_to_send, this); + request.sendRequest(true, jsonq_callback, this); + } + } + } + return { /** * Send a queued JSON call to the server @@ -64,82 +138,12 @@ egw().extend('jsonq', function() { // check / send queue every N ms var self = this; jsonq_timer = window.setInterval(function(){ - self.jsonq_send(); + jsonq_send.call(self); }, 100); } return uid; - }, - - /** - * Send the whole job-queue to the server in a single json request with menuaction=queue - */ - jsonq_send: function() - { - if (jsonq_uid > 0 && typeof jsonq_queue['u'+(jsonq_uid-1)] == 'object') - { - var jobs_to_send = {}; - var something_to_send = false; - for(var uid in jsonq_queue) - { - var job = jsonq_queue[uid]; + } - if (job.menuaction == 'send') continue; // already send to server - - // if job has a callbeforesend callback, call it to allow it to modify pararmeters - if (typeof job.callbeforesend == 'function') - { - job.callbeforesend.call(job.sender, job.parameters); - } - jobs_to_send[uid] = { - menuaction: job.menuaction, - parameters: job.parameters - }; - job.menuaction = 'send'; - job.parameters = null; - something_to_send = true; - } - if (something_to_send) - { - new egw_json_request('home.queue', jobs_to_send, this).sendRequest(true, this.jsonq_callback, this); - } - } - }, - - /** - * Dispatch responses received - * - * @param object _data uid => response pairs - */ - jsonq_callback: function(_data) - { - if (typeof _data != 'object') throw "jsonq_callback called with NO object as parameter!"; - - var json = new egw_json_request('none'); - for(var uid in _data) - { - if (typeof jsonq_queue[uid] == 'undefined') - { - console.log("jsonq_callback received response for not existing queue uid="+uid+"!"); - console.log(_data[uid]); - continue; - } - var job = jsonq_queue[uid]; - var response = _data[uid]; - - // fake egw_json_request object, to call it with the current response - json.callback = job.callback; - json.sender = job.sender; - json.handleResponse({response: response}); - - delete jsonq_queue[uid]; - } - // if nothing left in queue, stop interval-timer to give browser a rest - if (jsonq_timer && typeof jsonq_queue['u'+(jsonq_uid-1)] != 'object') - { - window.clearInterval(jsonq_timer); - jsonq_timer = null; - } - }, }; }); diff --git a/phpgwapi/js/jsapi/egw_lang.js b/phpgwapi/js/jsapi/egw_lang.js index 86174deb1e..8248e20719 100644 --- a/phpgwapi/js/jsapi/egw_lang.js +++ b/phpgwapi/js/jsapi/egw_lang.js @@ -16,7 +16,7 @@ egw_core; */ -egw().extend('lang', function() { +egw.extend('lang', egw.MODULE_GLOBAL, function() { /** * Translations diff --git a/phpgwapi/js/jsapi/egw_links.js b/phpgwapi/js/jsapi/egw_links.js index 621cf45336..06a27365b3 100644 --- a/phpgwapi/js/jsapi/egw_links.js +++ b/phpgwapi/js/jsapi/egw_links.js @@ -17,7 +17,7 @@ egw_link; */ -egw().extend('links', function() { +egw.extend('links', egw.MODULE_GLOBAL, function() { /** * Link registry diff --git a/phpgwapi/js/jsapi/egw_preferences.js b/phpgwapi/js/jsapi/egw_preferences.js index 6d07943a87..27c1d5053a 100644 --- a/phpgwapi/js/jsapi/egw_preferences.js +++ b/phpgwapi/js/jsapi/egw_preferences.js @@ -16,7 +16,7 @@ egw_core; */ -egw().extend('preferences', function() { +egw.extend('preferences', egw.MODULE_GLOBAL, function() { /** * Object holding the prefences as 2-dim. associative array, use diff --git a/phpgwapi/js/jsapi/egw_user.js b/phpgwapi/js/jsapi/egw_user.js index 8994f66f6d..a955bc2f15 100644 --- a/phpgwapi/js/jsapi/egw_user.js +++ b/phpgwapi/js/jsapi/egw_user.js @@ -16,7 +16,7 @@ egw_core; */ -egw().extend('user', function() { +egw.extend('user', egw.MODULE_GLOBAL, function() { /** * Data about current user diff --git a/phpgwapi/js/jsapi/egw_utils.js b/phpgwapi/js/jsapi/egw_utils.js new file mode 100644 index 0000000000..fe2c07c69b --- /dev/null +++ b/phpgwapi/js/jsapi/egw_utils.js @@ -0,0 +1,165 @@ +/** + * EGroupware clientside API 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 (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw.extend('utils', egw.MODULE_GLOBAL, function() { + + function json_escape_string(input) + { + var len = input.length; + var res = ""; + + for (var i = 0; i < len; i++) + { + switch (input.charAt(i)) + { + case '"': + res += '\\"'; + break; + + case '\n': + res += '\\n'; + break; + + case '\r': + res += '\\r'; + break; + + case '\\': + res += '\\\\'; + break; + + case '\/': + res += '\\/'; + break; + + case '\b': + res += '\\b'; + break; + + case '\f': + res += '\\f'; + break; + + case '\t': + res += '\\t'; + break; + + default: + res += input.charAt(i); + } + } + + return res; + } + + function json_encode_simple(input) + { + switch (input.constructor) + { + case String: + return '"' + json_escape_string(input) + '"'; + + case Number: + return input.toString(); + + case Boolean: + return input ? 'true' : 'false'; + + default: + return null; + } + } + + function json_encode(input) + { + if (input == null || !input && input.length == 0) return 'null'; + + var simple_res = json_encode_simple(input); + if (simple_res == null) + { + switch (input.constructor) + { + case Array: + var buf = []; + for (var k in input) + { + //Filter non numeric entries + if (!isNaN(k)) + buf.push(json_encode(input[k])); + } + return '[' + buf.join(',') + ']'; + + case Object: + var buf = []; + for (var k in input) + { + buf.push(json_encode_simple(k) + ':' + json_encode(input[k])); + } + return '{' + buf.join(',') + '}'; + + default: + switch(typeof input) + { + case 'array': + var buf = []; + for (var k in input) + { + //Filter non numeric entries + if (!isNaN(k)) + buf.push(json_encode(input[k])); + } + return '[' + buf.join(',') + ']'; + + case 'object': + var buf = []; + for (var k in input) + { + buf.push(json_encode_simple(k) + ':' + json_encode(input[k])); + } + return '{' + buf.join(',') + '}'; + + } + return 'null'; + } + } + else + { + return simple_res; + } + } + + // Create the utils object which contains references to all functions + // covered by it. + var utils = {}; + + // Check whether the browser already supports encoding JSON -- if yes, use + // its implementation, otherwise our own + if (typeof window.JSON !== 'undefined' && typeof window.JSON.stringify !== 'undefined') + { + utils["jsonEncode"] = JSON.stringify; + } + else + { + utils["jsonEncode"] = json_encode; + } + + // Return the extension + return {"utils": utils}; + +}); +