egroupware_official/phpgwapi/js/jsapi/egw_core.js
Andreas Stöckel d486e50a57 phpgwapi:
* Changed way of how "webserverUrl" gets set - any type of data can now be
	  injected into the egw object by creating an object with the data and an
	  entry "prefsOnly" set to true. This allows to ensure, that "webserverUrl"
	  is the first thing that is being set in the egw object (as needed when
	  including new JS/CSS files at runtime)

jsapi:
	* Fixed including JS/CSS files at runtime in other windows than the root
	  window
	* Added "ready" function/module, which provides an alternative to the
	  $j("ready") function. The ready module provides the functionality to
	  postpone calling the "ready" until certain events happened.
	* using jQuery calendar object instead of jscalendar in the calendar
	  function.
	* added "jquery" module which takes care of including all jQuery modules
	  in all windows
	* added possibility for modules to update constants using the "constant"
	  function.
	* added possibility for modules to access certain other modules using
	  the "module" function

etemplate:
	* Using new egw(window).ready function to build the template first if
	  loading has finished.
2012-03-09 15:32:29 +00:00

702 lines
19 KiB
JavaScript

/**
* 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 <RalfBecker@outdoor-training.de>
* @version $Id$
*/
"use strict";
/**
* 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.
*/
(function(_parent) {
var instanceUid = 0;
// Some local functions for cloning and merging javascript objects
function cloneObject(_obj) {
var result = {};
for (var key in _obj)
{
result[key] = _obj[key];
}
return result;
}
function mergeObjects(_to, _from) {
// Extend the egw object
for (var key in _from)
{
_to[key] = _from[key];
}
}
function deleteWhere(_arr, _cond)
{
for (var i = _arr.length - 1; i >= 0; i--)
{
if (_cond(_arr[i]))
{
_arr.splice(i, 1)
}
}
}
/**
* The getAppModules function returns all application specific api modules
* for the given application. If those application specific api instances
* were not created yet, the functions creates them.
*
* @param _egw is a reference to the global _egw instance and is passed as
* a context to the module instance.
* @param _modules is the hash map which contains all module descriptors.
* @param _moduleInstances is the the object which contains the application
* and window specific module instances.
* @param _app is the application for which the module instances should get
* created.
*/
function getAppModules(_egw, _modules, _moduleInstances, _app)
{
// Check whether the application specific modules for that instance
// already exists, if not, create it
if (typeof _moduleInstances.app[_app] === 'undefined')
{
var modInsts = {};
_moduleInstances.app[_app] = modInsts;
// Otherwise create the application specific instances
for (var key in _modules)
{
var mod = _modules[key];
// Check whether the module is actually an application local
// instance. As the module instance may already have been
// created by another extension (when calling the egw.module
// function) we're doing the second check.
if (mod.flags === _egw.MODULE_APP_LOCAL
&& typeof modInsts[key] === 'undefined')
{
modInsts[key] = mod.code.call(_egw, _app, window);
}
}
}
return _moduleInstances.app[_app];
}
function getExistingWndModules(_moduleInstances, _window)
{
// Search for the specific window instance
for (var i = 0; i < _moduleInstances.wnd.length; i++)
{
if (_moduleInstances.wnd[i].window === _window)
{
return _moduleInstances.wnd[i].modules;
}
}
return null;
}
/**
* The getWndModules function returns all window specific api modules for
* the given window. If those window specific api instances were not created
* yet, the functions creates them.
*
* @param _egw is a reference to the global _egw instance and is passed as
* a context to the module instance.
* @param _modules is the hash map which contains all module descriptors.
* @param _moduleInstances is the the object which contains the application
* and window specific module instances.
* @param _instances refers to all api instances.
* @param _wnd is the window for which the module instances should get
* created.
*/
function getWndModules(_egw, _modules, _moduleInstances, _instances, _window)
{
var mods = getExistingWndModules(_moduleInstances, _window);
if (mods) {
return mods;
}
// If none was found, create the slot
mods = {};
_moduleInstances.wnd.push({
'window': _window,
'modules': mods
});
// Add an eventlistener for the "onunload" event -- if "onunload" gets
// called, we have to delete the module slot created above
_window.addEventListener('beforeunload', function() {
cleanupEgwInstances(_instances, _moduleInstances, function(_w) {
return _w.window === _window});
}, false);
// Otherwise create the window specific instances
for (var key in _modules)
{
var mod = _modules[key];
// Check whether the module is actually a window local instance. As
// the module instance may already have been created by another
// extension (when calling the egw.module function) we're doing the
// second check.
if (mod.flags === _egw.MODULE_WND_LOCAL
&& typeof mods[key] === 'undefined')
{
mods[key] = mod.code.call(_egw, null, _window);
}
}
return mods;
}
/**
* Creates an api instance for the given application and the given window.
*
* @param _egw is the global _egw instance which should be used.
* @param _modules is the hash map which contains references to all module
* descriptors.
* @param _moduleInstances is the the object which contains the application
* and window specific module instances.
* @param _list is the overall instances list, to which the module should be
* added.
* @param _instances refers to all api instances.
* @param _app is the application for which the instance should be created.
* @param _wnd is the window for which the instance should be created.
*/
function createEgwInstance(_egw, _modules, _moduleInstances, _list,
_instances, _app, _window)
{
// Clone the global object
var instance = cloneObject(_egw);
// Let "_window" and "_app" be exactly null, if it evaluates to false
_window = _window ? _window : null;
_app = _app ? _app : null;
// Set the application name and the window the API instance belongs to
instance.appName = _app;
instance.window = _window;
// Push the newly created instance onto the instance list
_list.push({
'instance': instance,
'window': _window,
'app': _app
});
// Merge either the application specific and/or the window specific
// module instances into the new instance
if (_app)
{
var appModules = getAppModules(_egw, _modules, _moduleInstances,
_app);
for (var key in appModules)
{
mergeObjects(instance, appModules[key]);
}
}
if (_window)
{
var wndModules = getWndModules(_egw, _modules, _moduleInstances,
_instances, _window);
for (var key in wndModules)
{
mergeObjects(instance, wndModules[key]);
}
}
// Return the new api instance
return instance;
}
/**
* Returns a egw instance for the given application and the given window. If
* the instance does not exist now, the instance will be created.
*
* @param _egw is the global _egw instance which should be used.
* @param _modules is the hash map which contains references to all module
* descriptors.
* @param _moduleInstances is the the object which contains the application
* and window specific module instances.
* @param _list is the overall instances list, to which the module should be
* added.
* @param _app is the application for which the instance should be created.
* @param _wnd is the window for which the instance should be created.
*/
function getEgwInstance(_egw, _modules, _moduleInstances, _instances, _app,
_window)
{
// Generate the hash key for the instance descriptor object
var hash = _app ? _app : '~global~';
// 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, _moduleInstances,
_instances[hash], _instances, _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, _moduleInstances,
_instances[hash], _instances, _app, _window);
}
function cleanupEgwInstances(_instances, _moduleInstances, _cond)
{
// Iterate over the instances
for (var key in _instances)
{
// Delete all entries corresponding to closed windows
deleteWhere(_instances[key], _cond);
// Delete the complete instance key if the array is empty
if (_instances[key].length === 0)
{
delete _instances[key];
}
}
// Delete all entries corresponding to non existing elements in the
// module instances
deleteWhere(_moduleInstances.wnd, _cond);
}
function mergeGlobalModule(_module, _code, _instances, _moduleInstances)
{
// Generate the global extension
var globalExtension = _code.call(egw, null, window);
// Store the global extension module
_moduleInstances.glo[_module] = globalExtension;
for (var key in _instances)
{
for (var i = 0; i < _instances[key].length; i++)
{
mergeObjects(_instances[key][i].instance,
globalExtension);
}
}
}
function mergeAppLocalModule(_module, _code, _instances, _moduleInstances)
{
// Generate the global extension
var globalExtension = _code.call(egw, null, window);
// Store the global extension module
_moduleInstances.glo[_module] = globalExtension;
// Merge the extension into the global instances
for (var i = 0; i < _instances['~global~'].length; i++)
{
mergeObjects(_instances['~global~'][i].instance, globalExtension);
}
for (var key in _moduleInstances.app)
{
// Create the application specific instance and
// store it in the module instances
var appExtension = _code.call(egw, key, window);
_moduleInstances.app[key][_module] = appExtension;
// Merge the extension into all instances for
// the current application
for (var i = 0; i < _instances[key].length; i++)
{
mergeObjects(_instances[key][i].instance, appExtension);
}
}
}
function mergeWndLocalModule(_module, _code, _instances, _moduleInstances)
{
// Iterate over all existing windows
for (var i = 0; i < _moduleInstances.wnd.length; i++)
{
var wnd = _moduleInstances.wnd[i].window;
// Create the window specific instance and
// register it.
var wndExtension = _code.call(egw, null, wnd);
_moduleInstances.wnd[i].modules[_module] = wndExtension;
// Extend all existing instances which are using
// this window.
for (var key in _instances)
{
for (var j = 0; j < _instances[key].length; j++)
{
if (_instances[key][j].window === wnd)
{
mergeObjects(_instances[key][j].instance,
wndExtension);
}
}
}
}
}
/**
* Creates the egw object --- if the egw object should be created, some data
* has already been set inside the object by the egw_framework::header
* function and the instance has been marked as "prefsOnly".
*/
if (typeof window.egw != "undefined" && window.egw.prefsOnly)
{
// Rescue the old egw object
var prefs = window.egw;
delete prefs['prefsOnly'];
/**
* Modules contains all currently loaded egw extension modules. A module
* is stored as an object of the following form:
* {
* name: <NAME OF THE OBJECT>,
* code: <REFERENCE TO THE MODULE FUNCTION>,
* flags: <MODULE FLAGS (local, global, etc.)
* }
*/
var modules = {};
var moduleInstances = {
'app': {},
'wnd': [],
'glo': {}
}
/**
* instances contains references to all created instances.
*/
var instances = {};
/**
* Set a interval which is used to cleanup unused API instances all 10
* seconds.
*/
window.setInterval(function() {
cleanupEgwInstances(instances, moduleInstances, function(w) {
return w.window && w.window.closed
});
}, 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.
*/
var egw = function() {
// Get the window/app reference
var _app = null;
var _window = window;
switch (arguments.length)
{
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";
}
// Generate an API instance
return getEgwInstance(egw, modules, moduleInstances, 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: 0,
/**
* 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: 1,
/**
* 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: 2,
/**
* 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
* function. If the getAppName function is called on the global
* instance, 'etemplate' is returned.
*/
getAppName: function() {
// Return the default application name if this function is
// called on the global egw instance.
if (!this.appName) {
return 'etemplate';
}
// Otherwise return the correct application name.
return this.appName;
},
/**
* The extend function can be used to extend the egw object.
*
* @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. May be one of egw.MODULE_GLOBAL,
* MODULE_APP_LOCAL or MODULE_WND_LOCAL.
* @param _code should be a function, which returns an object that
* should extend the egw object.
*/
extend: function(_module, _flags, _code) {
// 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
};
// Create new app/module specific instances for the new
// module and merge the new module into all created
// instances
switch (_flags)
{
// Easiest case -- simply merge the extension into all
// instances
case egw.MODULE_GLOBAL:
mergeGlobalModule(_module, _code, instances,
moduleInstances);
break;
// Create new application specific instances and merge
// those into all api instances for that application
case egw.MODULE_APP_LOCAL:
mergeAppLocalModule(_module, _code, instances,
moduleInstances);
break;
// Create new window specific instances for each window
// and merge those into all api instances for that
// window
case egw.MODULE_WND_LOCAL:
mergeWndLocalModule(_module, _code, instances,
moduleInstances);
break;
}
}
},
/**
* Very similar to the egw function itself, but the module function
* returns just the functions exported by a single extension -- in
* this way extensions themselve are capable of accessing each
* others functions while they are being instanciated. Yet you
* should be carefull not to create any cyclic dependencies.
*
* @param _module is the name of the module
* @param _for may either be a string describing an application,
* an object referencing to a window or evaluate to false, in which
* case the global instance will be returned.
*/
module: function(_module, _for) {
if (typeof modules[_module] !== 'undefined')
{
// Return the global instance of the module if _for
// evaluates to false
if (!_for)
{
return moduleInstances.glo[_module];
}
// Assume _for is an application name if it is a string.
// Check whether the given application instance actually
// exists.
if (typeof _for === 'string'
&& typeof moduleInstances.app[_for] !== 'undefined')
{
var mods = moduleInstances.app[_for];
// Otherwise just instanciate the module if it has not
// been created yet.
if (typeof mods[_module] === 'undefined')
{
var mod = modules[_module];
mods[_module] = mod.code.call(this, _app, window);
}
return mods[_module];
}
// If _for is an object, assume it is a window.
if (typeof _for === 'object')
{
var mods = getExistingWndModules(moduleInstances, _for);
// Check whether the module container for that window
// has been found
if (mods != null)
{
// If the given module has not been instanciated for
// this window,
if (typeof mods[_module] === 'undefined')
{
var mod = modules[_module];
mods[_module] = mod.code.call(this, null, _for);
}
return mods[_module];
}
}
}
return null;
},
/**
* The "constant" function can be used to update a constant in all
* egw instances.
*
* @param _module is the module for which the constant should be set
* @param _name is the name of the constant
* @param _value is the value to which it should be set
* @param _window if set, updating the constant is restricted to
* those api instances which belong to the given window, if _window
* evaluates to false, all instances will be updated.
*/
constant: function(_module, _name, _value, _window) {
// Update the module instances first
for (var i = 0; i < moduleInstances.wnd.length; i++)
{
if (!_window || _window === moduleInstances.wnd[i].window)
{
moduleInstances.wnd[i].modules[_module][_name] = _value;
}
}
// Now update all already instanciated instances
for (var key in instances)
{
for (var i = 0; i < instances[key].length; i++)
{
if (!_window || _window === instances[key][i].window)
{
instances[key][i].instance[_name] = _value;
}
}
}
},
dumpModules: function() {
return modules;
},
dumpInstances: function() {
return {
'instances': instances,
'moduleInstances': moduleInstances
}
}
};
// Merge the globalEgw functions into the egw object.
mergeObjects(egw, globalEgw);
// Merge the preferences into the egw object.
mergeObjects(egw, prefs);
// Create the entry for the root window in the module instances
moduleInstances.wnd.push({
'window': window,
'modules': []
});
// Create the entry for the global window in the instances and register
// the global instance there
instances['~global~'] = [{
'window': window,
'instance': egw,
'app': null
}];
// Publish the egw object
this['egw'] = egw;
}
}).call(window);