diff --git a/phpgwapi/js/jsapi/egw.js b/phpgwapi/js/jsapi/egw.js index bf4af9a0ac..52df3bc439 100644 --- a/phpgwapi/js/jsapi/egw.js +++ b/phpgwapi/js/jsapi/egw.js @@ -25,5 +25,6 @@ egw_files; egw_json; egw_tooltip; + egw_css; */ diff --git a/phpgwapi/js/jsapi/egw_core.js b/phpgwapi/js/jsapi/egw_core.js index f120f0765a..332c9df663 100644 --- a/phpgwapi/js/jsapi/egw_core.js +++ b/phpgwapi/js/jsapi/egw_core.js @@ -12,14 +12,14 @@ "use strict"; -var egw; - /** * 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 = {}; @@ -40,54 +40,170 @@ var egw; } } - function createEgwInstance(_egw, _modules, _list, _app, _window) + /** + * 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 = {}; + + // Otherwise create the application specific instances + for (var key in _modules) + { + var mod = _modules[key]; + if (mod.flags === _egw.MODULE_APP_LOCAL) + { + modInsts[key] = mod.code.call(_egw, _app, window); + } + } + + _moduleInstances.app[_app] = modInsts; + } + + return _moduleInstances.app[_app]; + } + + /** + * 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 _wnd is the window for which the module instances should get + * created. + */ + function getWndModules(_egw, _modules, _moduleInstances, _window) + { + // Search for the specific window instance + for (var i = 0; i < _moduleInstances.wnd.length; i++) + { + var descr = _moduleInstances.wnd[i]; + + if (descr.window === _window) + { + return descr.modules; + } + } + + // If none was found, create the slot + var mods = {}; + _moduleInstances.wnd.push({ + 'window': _window, + 'modules': mods + }); + + // Otherwise create the window specific instances + for (var key in _modules) + { + var mod = _modules[key]; + if (mod.flags === _egw.MODULE_WND_LOCAL) + { + 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 _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, _app, + _window) { // Clone the global object var instance = cloneObject(_egw); - // Let "_window" be exactly null, if it evaluates to false + // 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 ? _app : null; + instance.appName = _app; instance.window = _window; - // Insert the newly created instance into the instances list + // Push the newly created instance onto the instance list _list.push({ + 'instance': instance, 'window': _window, - 'app': _app, - 'instance': instance + 'app': _app }); - // Re-instanciate all modules which are marked as "local" - for (var key in _modules) + // Merge either the application specific and/or the window specific + // module instances into the new instance + if (_app) { - // Get the module object - var mod = _modules[key]; + var appModules = getAppModules(_egw, _modules, _moduleInstances, + _app); - if (mod.flags !== _egw.MODULE_GLOBAL) + for (var key in appModules) { - // 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); - } + mergeObjects(instance, appModules[key]); } } + if (_window) + { + var wndModules = getWndModules(_egw, _modules, _moduleInstances, + _window); + + for (var key in wndModules) + { + mergeObjects(instance, wndModules[key]); + } + } + + // Return the new api instance return instance; } - function getEgwInstance(_egw, _modules, _instances, _app, _window) + /** + * 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]'; + var hash = _app ? _app : '~global~'; // Let "_window" be exactly null, if it evaluates to false _window = _window ? _window : null; @@ -96,8 +212,8 @@ var egw; if (typeof _instances[hash] === 'undefined') { _instances[hash] = []; - return createEgwInstance(_egw, _modules, _instances[hash], _app, - _window); + return createEgwInstance(_egw, _modules, _moduleInstances, + _instances[hash], _app, _window); } else { @@ -114,44 +230,118 @@ var egw; // 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); + return createEgwInstance(_egw, _modules, _moduleInstances, + _instances[hash], _app, _window); } - function cleanupEgwInstances(_instances) + function cleanupEgwInstances(_instances, _moduleInstances) { - // Iterate over the egw instances and check whether the window they - // correspond to is still open. - for (var key in _instances) + function deleteClosedWindows(_arr) { - for (var i = _instances[key].length - 1; i >= 0; i--) + for (var i = _arr.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) + if (_arr[i].window && _arr[i].window.closed) { - _instances[key].splice(i, 1) + _arr.splice(i, 1) } } + } - // If all instances for the current hash have been deleted, delete - // the hash entry itself + // Iterate over the instances + for (var key in _instances) + { + // Delete all entries corresponding to closed windows + deleteClosedWindows(_instances[key]); + + // 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 + deleteClosedWindows(_moduleInstances.wnd); + } + + function mergeGlobalModule(_module, _code, _instances, _moduleInstances) + { + // Generate the global extension + var globalExtension = _code.call(egw, null, window); + + 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); + + // 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); + } + } + } + } } if (window.opener && typeof window.opener.egw !== 'undefined') { - egw = window.opener.egw; + this['egw'] = window.opener.egw; } else if (window.top && typeof window.top.egw !== 'undefined') { - egw = window.top.egw; + this['egw'] = window.top.egw; } else { @@ -166,16 +356,13 @@ var egw; */ var modules = {}; + var moduleInstances = { + 'app': {}, + 'wnd': [] + } + /** - * instances contains all api instances. These are organized as a hash - * of the form _app + _window.location. For each of these hashes a list - * of instances is stored, where the instance itself is an entry of the - * form - * { - * instance: , - * app: , - * window: - * } + * instances contains references to all created instances. */ var instances = {}; @@ -183,7 +370,9 @@ var egw; * Set a interval which is used to cleanup unused API instances all 10 * seconds. */ - window.setInterval(function() {cleanupEgwInstances(instances);}, 10000); + window.setInterval(function() { + cleanupEgwInstances(instances, moduleInstances); + }, 10000); /** * The egw function returns an instance of the client side api. If no @@ -194,10 +383,10 @@ var egw; * has to preceed the window object reference. If no window object is * given, the root window will be used. */ - egw = function() { + var egw = function() { // Get the window/app reference - var _app = ""; + var _app = null; var _window = window; switch (arguments.length) @@ -227,7 +416,8 @@ var egw; } // Generate an API instance - return getEgwInstance(egw, modules, instances, _app, _window); + return getEgwInstance(egw, modules, moduleInstances, instances, + _app, _window); } var globalEgw = { @@ -236,21 +426,21 @@ var egw; * The MODULE_GLOBAL flag describes a module as global. A global * module always works on the same data. */ - MODULE_GLOBAL: 0x00, + 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: 0x01, + 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: 0x02, + MODULE_WND_LOCAL: 2, /** * Name of the application the egw object belongs to. @@ -292,7 +482,8 @@ var egw; * @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. + * 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. */ @@ -308,33 +499,33 @@ var egw; 'name': _module }; - // Generate the global extension - var globalExtension = _code.call(egw, egw, window); - - // Merge the global extension into the egw function - mergeObjects(egw, globalExtension); - - // Iterate over the instances and merge the modules into - // them - for (var key in instances) + // Create new app/module specific instances for the new + // module and merge the new module into all created + // instances + switch (_flags) { - for (var i = 0; i < instances[key].length; i++) - { - // Get the instance descriptor - var instDescr = instances[key][i]; + // Easiest case -- simply merge the extension into all + // instances + case egw.MODULE_GLOBAL: + mergeGlobalModule(_module, _code, instances, + moduleInstances); + break; - // 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); - } - } + // 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; } } }, @@ -344,13 +535,33 @@ var egw; }, dumpInstances: function() { - return instances; + return { + 'instances': instances, + 'moduleInstances': moduleInstances + } } }; // Merge the globalEgw functions into the egw object. mergeObjects(egw, globalEgw); - } -})(); + + // 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); diff --git a/phpgwapi/js/jsapi/egw_css.js b/phpgwapi/js/jsapi/egw_css.js new file mode 100644 index 0000000000..1faa3d186c --- /dev/null +++ b/phpgwapi/js/jsapi/egw_css.js @@ -0,0 +1,94 @@ +/** + * eGroupWare eTemplate2 - Stylesheet class + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel + * @copyright Stylite 2011 + * @version $Id$ + */ + +"use strict" + +/*egw:uses + egw_core; +*/ + +egw.extend('css', egw.MODULE_WND_LOCAL, function(_egw, _wnd) { + + /** + * Assoziative array which stores the current css rule for a given selector. + */ + var selectors = {}; + + /** + * Variable used to calculate unique id for the selectors. + */ + var selectorCount = 0; + + // Generate a style tag, which will be used to hold the newly generated css + // rules. + var style = _wnd.document.createElement('style'); + _wnd.document.getElementsByTagName('head')[0].appendChild(style); + + // Obtain the reference to the styleSheet object of the generated style tag + var sheet = style.sheet ? style.sheet : style.styleSheet; + + return { + /** + * The css function can be used to introduce a rule for the given css + * selector. So you're capable of adding new custom css selector while + * runtime and also update them. + * + * @param _selector is the css select which can be used to apply the + * stlyes to the html elements. + * @param _rule is the rule which should be connected to the selector. + * if empty or omitted, the given selector gets removed. + */ + css: function(_selector, _rule) { + // Set the current index to the maximum index + var index = selectorCount; + + // Remove any existing rule first, of no rule exists for the + if (typeof selectors[_selector] !== "undefined") + { + // Store the old index + index = selectors[_selector]; + if (typeof sheet.removeRule !== "undefined") + { + sheet.removeRule(index); + } + else + { + sheet.deleteRule(index); + } + + delete (selectors[_selector]); + } + else + { + selectorCount++; + } + + if (_rule) + { + // Add the rule to the stylesheet + if (typeof sheet.addRule !== "undefined") + { + sheet.addRule(_selector, _rule, index); + } + else + { + sheet.insertRule(_selector + "{" + _rule + "}", index); + } + + // Store the new index + selectors[_selector] = index; + } + } + } + +}); + diff --git a/phpgwapi/js/jsapi/egw_debug.js b/phpgwapi/js/jsapi/egw_debug.js index d7210ef849..1893e77dcd 100644 --- a/phpgwapi/js/jsapi/egw_debug.js +++ b/phpgwapi/js/jsapi/egw_debug.js @@ -16,7 +16,7 @@ egw_core; */ -egw.extend('debug', egw.MODULE_GLOBAL, function(_egw, _wnd) { +egw.extend('debug', egw.MODULE_GLOBAL, function(_app, _wnd) { /** * DEBUGLEVEL specifies which messages are printed to the console. diff --git a/phpgwapi/js/jsapi/egw_images.js b/phpgwapi/js/jsapi/egw_images.js index 2c4fd4c167..544cd27f79 100644 --- a/phpgwapi/js/jsapi/egw_images.js +++ b/phpgwapi/js/jsapi/egw_images.js @@ -48,18 +48,20 @@ egw.extend('images', egw.MODULE_GLOBAL, function() { // For logging all paths tried var tries = {}; - if (typeof _app == 'undefined') + if (typeof _app === 'undefined') { - if(_name.indexOf('/') > 0) + // If the application name is not given, set it to the name of + // current application + _app = this.getAppName(); + + // If this.getAppName does not work, try to determine the image + // by looking at the image path. + if(!_app && _name.indexOf('/') > 0) { var split = et2_csvSplit(_value, 2,"/"); var _app = split[0]; _name = split[1]; } - else - { - _app = this.getAppName(); - } } // own instance specific images in vfs have highest precedence diff --git a/phpgwapi/js/jsapi/egw_json.js b/phpgwapi/js/jsapi/egw_json.js index f66f014fdf..e1bb5e3f43 100644 --- a/phpgwapi/js/jsapi/egw_json.js +++ b/phpgwapi/js/jsapi/egw_json.js @@ -21,7 +21,7 @@ egw_debug; */ -egw.extend('json', egw.MODULE_WND_LOCAL, function(_egw, _wnd) { +egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) { /** * Object which contains all registered handlers for JS responses. diff --git a/phpgwapi/js/jsapi/egw_tooltip.js b/phpgwapi/js/jsapi/egw_tooltip.js index a268494c15..0ff0e4093a 100644 --- a/phpgwapi/js/jsapi/egw_tooltip.js +++ b/phpgwapi/js/jsapi/egw_tooltip.js @@ -17,7 +17,7 @@ egw_core; */ -egw.extend('tooltip', egw.MODULE_WND_LOCAL, function(_egw, _wnd) { +egw.extend('tooltip', egw.MODULE_WND_LOCAL, function(_app, _wnd) { var tooltip_div = null; var current_elem = null;