diff --git a/phpgwapi/js/egw_action/egw_action.js b/phpgwapi/js/egw_action/egw_action.js new file mode 100644 index 0000000000..8653efe7fa --- /dev/null +++ b/phpgwapi/js/egw_action/egw_action.js @@ -0,0 +1,474 @@ +/** + * eGroupWare egw_action framework - egw action framework + * + * @link http://www.egroupware.org + * @author Andreas Stöckel + * @copyright 2011 by Andreas Stöckel + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package egw_action + * @version $Id:$ + */ + +// XXX WARNING: UNTESTED, UNFINISHED, NOT (YET) WORKING CODE! XXX + +/** egwActionHandler Interface **/ + +/** + * Constructor for the egwActionHandler interface which (at least) should have the + * execute function implemented. + */ +function egwActionHandler(_executeEvent) +{ + //Copy the executeEvent parameter + this.execute = _executeEvent; +} + + +/** egwAction Object **/ + +/** + * Associative array where action classes may register themselves + */ +var _egwActionClasses = + { + "default": + { + "actionConstructor": egwAction, + "implementationConstructor": null + } + } + +function egwAction(_id, _handler, _caption, _icon, _onExecute, _allowOnMultiple) +{ + //Default and check the values + if (typeof _id != "string" || !_id) + throw "egwAction _id must be a non-empty string!"; + if (typeof _handler == "undefined") + this.handler = null; + if (typeof _label == "undefined") + _label = ""; + if (typeof _icon == "undefined") + _icon = ""; + if (typeof _onExecute == "undefined") + _onExecute = null; + if (typeof _allowOnMultiple == "undefined") + _allowOnMultiple = true; + + this.id = _id; + this.caption = _caption; + this.icon = _icon; + this.allowOnMultiple = _allowOnMultiple; + this.type = "default"; //All derived classes have to override this! + + this.execJSFnct = null; + this.execHandler = false; + this.set_onExecute(_onExecute); +} + +/** + * Executes this action by using the method specified in the onExecute setter. + * + * @param array_senders array with references to the objects which caused the action + * //TODO: With DragDrop we don't only have senders but also one(!) target. + */ +egwAction.prototype.execute = function(_senders) +{ + if (this.execJSFnct && typeof this.execJSFnct == "function") + { + this.execJSFnct(this, _senders); + } + else if (this.execHandler) + { + this.handler.execute(this, _senders); + } +} + +/** + * The set_onExecute function is the setter function for the onExecute event of + * the egwAction object. There are three possible types the passed "_value" may + * take: + * 1. _value may be a string with the word "javaScript:" prefixed. The function + * which is specified behind the colon and which has to be in the global scope + * will be executed. + * 2. _value may be a boolean, which specifies whether the external onExecute handler + * (passed as "_handler" in the constructor) will be used. + * 3. _value may be a JS functino which will then be called. + * In all possible situation, the called function will get the following parameters: + * 1. A reference to this action + * 2. The senders, an array of all objects (JS)/object ids (PHP) which evoked the event + */ +egwAction.prototype.set_onExecute(_value) +{ + //Reset the onExecute handlers + this.execJSFnct = null; + this.execHandler = false; + + if (typeof _value == "string") + { + // Check whether the given string contains a javaScript function which + // should be called upon executing the action + if (_value.substr(0, 11) == "javaScript:") + { + //Check whether the given function exists + var fnct = _value.substr(11); + if (typeof window[fnct] == "function") + { + this.execJSFnct = window[fnct]; + } + } + } + else if (typeof _value == "boolean") + { + // There is no direct reference to the PHP code which should be executed, + // as the PHP code has knowledge about this. + this.execHandler = _value; + } + else if (typeof _value == "function") + { + //The JS function has been passed directly + this.execJSFnct = _value; + } +} + +egwAction.prototype.set_caption(_value) +{ + this.caption = _value; +} + +egwAction.prototype.set_icon(_value) +{ + this.icon = _value; +} + +egwAction.prototype.set_allowOnMultiple(_value) +{ + this.allowOnMultiple = _value; +} + + +/** egwActionManager Object **/ + +/** + * egwActionManager manages a list of actions, provides functions to add new + * actions or to update them via JSON. + */ +function egwActionManager(_handler) +{ + //Preset the handler parameter to null + if (typeof _handler == "undefined") + _handler = null; + + this.handler = _handler; + this.actions = []; +} + +egwActionManager.prototype.addAction = function(_type, _id, _caption, _icon, + _onExecute, _allowOnMultiple) +{ + //Get the constructor for the given action type + if (!_type) + _type = "default"; + var constructor = _egwActionClasses[_type].actionConstructor; + + if (typeof constructor == "function") + { + var action = new constructor(_id, this.handler, _caption, _icon, _onExecute, + _allowOnMultiple); + this.actions.push[action]; + + return action; + } + else + throw "Given action type not registered."; +} + +egwActionManager.prototype.updateActions = function(_actions) +{ + for (var i = 0 ; i < _actions.length; i++) + { + //Check whether the given action is already part of this action manager instance + var elem = _actions[i]; + if (typeof elem == "object" && typeof elem.id == "string" && elem.id) + { + //Check whether the action already exists, and if no, add it to the + //actions list + var action = this.getAction(elem.id); + if (!action) + { + if (typeof elem.type == "undefined") + elem.type = "default"; + + var constructor = _egwActionClasses[elem.type].actionConstructor; + + if (typeof constructor == "function") + action = new constructor(elem.id, this.handler); + else + throw "Given action type not registered."; + + this.actions.push(action); + } + + //Update the actions by calling the corresponding setter functions + //TODO: Hirachical actions will need a reference to their parent - + // this parent is has to be translated to a js object + //TODO: Maby the setter, JSON, update stuff should somehow be moved + // to a own base class. + egwActionStoreJSON(elem, action, true); + } + } +} + +egwActionManager.prototype.getAction = function(_id) +{ + for (var i = 0; i < this.actions.length; i++) + { + if (this.actions[i].id == _id) + return this.actions[i]; + } + + return null; +} + + +/** egwActionImplementation Interface **/ + +/** + * Abstract interface for the egwActionImplementation object. The egwActionImplementation + * object is responsible for inserting the actual action representation (context menu, + * drag-drop code) into the DOM Tree by using the egwActionObjectInterface object + * supplied by the object. + * To write a "class" which derives from this object, simply write a own constructor, + * which replaces "this" with a "new egwActionImplementation" and implement your + * code in "doRegisterAction" und "doUnregisterAction". + * Register your own implementation within the _egwActionClasses object. + * + * @param object _action is the parent egwAction object for that instance. + */ +function egwActionImplementation(_action) +{ + this.action = _action; + + this.doRegisterAction = null; + this.doUnregisterAction = null; +} + +/** + * Injects the implementation code into the DOM tree by using the supplied + * actionObjectInterface. + * + * @returns true if the Action had been successfully registered, false if it + * had not. + */ +egwActionImplementation.registerAction = function(_actionObjectInterface) +{ + if (this.doRegisterAction == null) + { + throw "Abstract function call: registerAction"; + } + else + { + return this.doRegisterAction(_action, _actionObjectInterface); + } +} + +/** + * Unregister action will be called before an actionObjectInterface is destroyed, + * which gives the egwActionImplementation the opportunity to remove the previously + * injected code. + * + * @returns true if the Action had been successfully unregistered, false if it + * had not. + */ +egwActionImplementation.unregisterAction = function(_actionObjectInterface) +{ + if (this.doUnregisterAction == null) + { + throw "Abstract function call: unregisterAction"; + } + else + { + return this.doUnregisterAction(_action, _actionObjectInterface); + } +} + + +/** egwActionLink Object **/ + +/** + * The egwActionLink is used to interconnect egwActionObjects and egwActions. + * This gives each action object the possibility to decide, whether the action + * should be active in this context or not. + * + * @param _manager is a reference to the egwActionManager whic contains the action + * the object wants to link to. + */ +function egwActionLink(_manager) +{ + this.enabled = true; + this.actionId = ""; + this.actionObj = null; + this.manager = _manager; +} + +egwActionLink.prototype.updateLink = function (_data) +{ + egwActionStoreJSON(_data, this, true); +} + +egwActionLink.prototype.set_enabled = function(_value) +{ + this.enabled = _value; +} + +egwActionLink.prototype.set_actionId = function(_value) +{ + this.actionId = _value; + this.actionObj = this.manager.getAction(_value); + + if (!this.actionObj) + throw "Given action object does not exist!" +} + + +/** egwActionObject Object **/ + +/** + * The egwActionObject represents an abstract object to which actions may be + * applied. Communication with the DOM tree is established by using the + * egwActionObjectInterface (AOI), which is passed in the constructor. + * egwActionObjects are organized in a tree structure. + * + * @param string _id is the identifier of the object which + * @param object _parent is the parent object in the hirachy. This may be set to NULL + * @param object _manager is the action manager this object is connected to + * @param object _interaction is the egwActionObjectInterface which connects + * this object to the DOM tree. + */ +function egwActionObject(_id, _parent, _manager, _interaction) +{ + this.id = _id; + this.parent = _parent; + this.interaction = _interaction; + this.children = []; + this.actionLinks = []; + this.manager = _manager; +} + +/** + * Updates the actionLinks of the given ActionObject. + * + * @param array _actionLinks contains the information about the actionLinks which + * should be updated as an array of objects. Example + * [ + * { + * "actionId": "file_delete", + * "enabled": true + * } + * ] + * If an supplied link doesn't exist yet, it will be created (if _doCreate is true) + * and added to the list. Otherwise the information will just be updated. + * @param boolean _recursive If true, the settings will be applied to all child + * object (default false) + * @param boolean _doCreate If true, not yet existing links will be created (default true) + */ +egwActionObject.prototype.updateActionLinks = function(_actionLinks, _recursive, _doCreate) +{ + if (typeof _recursive == "undefined") + _recursive = false; + if (typeof _doCreate == "undefined") + _doCreate = true; + + for (var i = 0; i < _actionLinks.length; i++) + { + var elem = _actionLinks[i]; + if (typeof elem.actionId != "undefined" && elem.actionId) + { + //Get the action link object, if it doesn't exists yet, create it + var actionLink = this.getActionLink(elem.actionId); + if (!actionLink && _doCreate) + { + actionLink = new egwActionLink(this.manager); + } + + //Set the supplied data + if (actionLink) + { + actionLink.updateLink(elem); + } + } + } + + if (_recursive) + { + for (var i = 0; i < this.children.length; i++) + { + this.children[i].updateActionLinks(_actionLinks, true, _doCreate); + } + } +} + +/** + * Returns the action link, which contains the association to the action with + * the given actionId. + * + * @param string _actionId name of the action associated to the link + */ +egwActionObject.prototype.getActionLink = function(_actionId) +{ + for (var i = 0; i < this.actionLinks.length; i++) + { + if (this.actionLinks[i].actionObj.id == _actionId) + { + return this.actionLinks[i]; + } + } + + return null; +} + +/** + * Returns all actions associated to the object tree, grouped by type. + * + * @param function _test gets an egwActionObject and should return, whether the + * actions of this object are added to the result. Defaults to a "always true" + * function. + * @param object _groups is an internally used parameter, may be omitted. + */ +egwActionObject.prototype.getActionImplementationGroups = function(_test, _groups) +{ + // If the _groups parameter hasn't been given preset it to an empty object + // (associative array). + if (typeof _groups == "undefined") + _groups = {}; + if (typeof _test == "undefined") + _test = function(_obj) {return true}; + + for (var i = 0; i < this.actionLinks.length; i++) + { + var action = this.actionsLink[i].actionObj; + if (typeof action != "undefined" && _test(this)) + { + if (typeof _groups[action.type] == "undefined") + { + _groups[action.type] = []; + } + + _groups[action.type].push( + { + "object": this, + "link": this.actionLinks[i] + } + ); + } + } + + // Recursively add the actions of the children to the result (as _groups is) + // a object, only the reference is passed. + for (var i = 0; i < this.children.length; i++) + { + this.children[i].getActionImplementationGroups(_test, _groups); + } + + return _groups; +} + diff --git a/phpgwapi/js/egw_action/egw_action_common.js b/phpgwapi/js/egw_action/egw_action_common.js new file mode 100644 index 0000000000..4fe02a2e61 --- /dev/null +++ b/phpgwapi/js/egw_action/egw_action_common.js @@ -0,0 +1,39 @@ +/** + * eGroupWare egw_action framework - egw action framework + * + * @link http://www.egroupware.org + * @author Andreas Stöckel + * @copyright 2011 by Andreas Stöckel + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package egw_action + * @version $Id:$ + */ + +/** + * Sets properties given in _data in _obj. Checks whether the property keys + * exists and if corresponding setter functions are available. Properties starting + * with "_" are ignored. + * + * @param object _data may be an object with data that will be stored inside the + * given object. + * @param object _obj is the object where the data will be stored. + */ +function egwActionStoreJSON(_data, _obj, _setterOnly) +{ + for (key in _data) + { + if (key.charAt(0) != '_') + { + //Check whether there is a setter function available + if (typeof _obj['set_' + key] == "function") + { + _obj['set_' + key](_data[key]); + } + else if (typeof _obj[key] != "undefined" && !_setterOnly) + { + _obj[key] = _data[key]; + } + } + } +} + diff --git a/phpgwapi/js/egw_action/egw_menu.js b/phpgwapi/js/egw_action/egw_menu.js new file mode 100644 index 0000000000..55535589f3 --- /dev/null +++ b/phpgwapi/js/egw_action/egw_menu.js @@ -0,0 +1,377 @@ +/** + * eGroupWare egw_action framework - JS Menu abstraction + * + * @link http://www.egroupware.org + * @author Andreas Stöckel + * @copyright 2011 by Andreas Stöckel + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package egw_action + * @version $Id:$ + */ + +//Global variable which is used to store the currently active menu so that it +//may be closed when another menu openes +var _egw_active_menu = null; + +/** + * Internal function which generates a menu item with the given parameters as used + * in e.g. the egwMenu.addItem function. + */ +//TODO Icons: write PHP GD script which is cabable of generating the menu icons in various states (disabled, highlighted) +function _egwGenMenuItem(_parent, _id, _label, _iconUrl, _onClick) +{ + //Preset the parameters + if (typeof _parent == "undefined") + _parent = null; + if (typeof _id == "undefined") + _id = ""; + if (typeof _label == "undefined") + _label = ""; + if (typeof _iconUrl == "undefined") + _iconUrl = ""; + if (typeof _onClick == "undefined") + _onClick = null; + + //Create a menu item with no parent (null) and set the given parameters + var item = new egwMenuItem(_parent, _id); + item.set_caption(_label); + item.set_iconUrl(_iconUrl); + item.set_onClick(_onClick); + + return item; +} + +/** + * Internal function which parses the given menu tree in _elements and adds the + * elements to the given parent. + */ +function _egwGenMenuStructure(_elements, _parent) +{ + var items = []; + + //Go through each object in the elements array + for (var i = 0; i < _elements.length; i++) + { + //Go through each key of the current object + var obj = _elements[i]; + var item = new egwMenuItem(_parent, null); + for (key in obj) + { + if (key == "children" && obj[key].constructor === Array) + { + //Recursively load the children. + item.children = _egwGenMenuStructure(obj[key], item); + } + else + { + //Directly set the other keys + //TODO Sanity neccessary checks here? + //TODO Implement menu item getters? + if (key == "id" || key == "caption" || key == "iconUrl" || + key == "checkbox" || key == "checked" || key == "groupIndex" || + key == "enabled" || key == "default" || key == "onClick") + { + item['set_' + key](obj[key]); + } + } + } + + items.push(item); + } + + return items; +} + +/** + * Internal function which searches for the given ID inside an element tree. + */ +function _egwSearchMenuItem(_elements, _id) +{ + for (var i = 0; i < _elements.length; i++) + { + if (_elements[i].id === _id) + return _elements[i]; + + var item = _egwSearchMenuItem(_elements[i].children, _id); + if (item) + return item; + } + + return null; +} + +/** + * Internal function which alows to set the onClick handler of multiple menu items + */ +function _egwSetMenuOnClick(_elements, _onClick) +{ + for (var i = 0; i < _elements.length; i++) + { + if (_elements[i].onClick === null) + { + _elements[i].onClick = _onClick; + } + _egwSetMenuOnClick(_elements[i].children, _onClick); + } +} + +/** + * Constructor for the egwMenu object. The egwMenu object is a abstract representation + * of a context/popup menu. The actual generation of the menu can by done by so + * called menu implementations. Those are activated by simply including the JS file + * of such an implementation. + * + * The currently available implementation is the "egwDhtmlxMenu.js" which is based + * upon the dhtmlxmenu component. + */ +function egwMenu() +{ + //The "items" variable contains all menu items of the menu + this.children = []; + + //The "instance" variable contains the currently opened instance. There may + //only be one instance opened at a time. + this.instance = null; +} + +/** + * The private _checkImpl function checks whether a menu implementation is available. + * + * @returns bool whether a menu implemenation is available. + */ +egwMenu.prototype._checkImpl = function() +{ + return typeof egwMenuImpl == 'function'; +} + +/** + * The showAtElement function shows the menu at the given screen position in an + * (hopefully) optimal orientation. There can only be one instance of the menu opened at + * one time and the menu implementation should care that there is only one menu + * opened globaly at all. + * + * @param int _x is the x position at which the menu will be opened + * @param int _y is the y position at which the menu will be opened + * @param bool _force if true, the menu will be reopened at the given position, + * even if it already had been opened. Defaults to false. + * @returns bool whether the menu had been opened + */ +egwMenu.prototype.showAt = function(_x, _y, _force) +{ + if (typeof _force == "undefined") + _force = false; + + //Hide any other currently active menu + if (_egw_active_menu != null) + { + if (_egw_active_menu == this && !_force) + { + this.hide(); + return false; + } + else + { + _egw_active_menu.hide(); + } + } + + if (this.instance == null && this._checkImpl) + { + //Obtain a new egwMenuImpl object and pass this instance to it + this.instance = new egwMenuImpl(this.children); + + _egw_active_menu = this; + + var self = this; + this.instance.showAt(_x, _y, function() { + self.instance = null; + _egw_active_menu = null; + }); + return true; + } + + return false; +} + +/** + * Hides the menu if it is currently opened. Otherwise nothing happenes. + */ +egwMenu.prototype.hide = function() +{ + //Reset the currently active menu variable + if (_egw_active_menu == this) + _egw_active_menu = null; + + //Check whether an currently opened instance exists. If it does, close it. + if (this.instance != null) + { + this.instance.hide(); + this.instance = null; + } +} + +/** + * Adds a new menu item to the list and returns a reference to that object. + * + * @param string _id is a unique identifier of the menu item. You can use the + * the getItem function to search a specific menu item inside the menu tree. The + * id may also be false, null or "", which makes sense for items like seperators, + * which you don't want to access anymore after adding them to the menu tree. + * @param string _label is the label of the newly generated menu item. Set the label + * to "-" in order to create a sperator. + * @param string _iconUrl is the URL of the icon which should be prepended to the + * menu item. It may be false, null or "" if you don't want a icon to be displayed. + * @param function _onClick is the JS function which is being executed when the + * menu item is clicked. + * @returns egwMenuItem the newly generated menu item, which had been appended to the + * menu item list. + */ +egwMenu.prototype.addItem = function(_id, _label, _iconUrl, _onClick) +{ + //Append the item to the list + var item = _egwGenMenuItem(this, _id, _label, _iconUrl, _onClick); + this.children.push(item); + + return item; +} + +/** + * Removes all elements fromt the menu structure. + */ +egwMenu.prototype.clear = function() +{ + this.children = []; +} + +/** + * Loads the menu structure from the given object tree. The object tree is an array + * of objects which may contain a subset of the menu item properties. The "children" + * property of such an object is interpreted as a new sub-menu tree and appended + * to that child. + * + * @param array _elements is a array of elements which should be added to the menu + */ +egwMenu.prototype.loadStructure = function(_elements) +{ + this.children = _egwGenMenuStructure(_elements, this); +} + +/** + * Searches for the given item id within the element tree. + */ +egwMenu.prototype.getItem = function(_id) +{ + return _egwSearchMenuItem(this.children, _id); +} + +/** + * Applies the given onClick handler to all menu items which don't have a clicked + * handler assigned yet. + */ +egwMenu.prototype.setGlobalOnClick = function(_onClick) +{ + _egwSetMenuOnClick(this.children, _onClick); +} + +/** + * Constructor for the egwMenuItem. Each entry in a menu (including seperators) + * is represented by a menu item. + */ +function egwMenuItem(_parent, _id) +{ + this.id = _id; + this.caption = ""; + this.checkbox = false; + this.checked = false; + this.groupIndex = 0; + this.enabled = true; + this.iconUrl = ""; + this.onClick = null; + this.default = false; + + this.children = []; + this.parent = _parent; +} + +/** + * Searches for the given item id within the element tree. + */ +egwMenuItem.prototype.getItem = function(_id) +{ + if (this.id === _id) + return this; + + return _egwSearchMenuItem(this.children, _id); +} + +/** + * Applies the given onClick handler to all menu items which don't have a clicked + * handler assigned yet. + */ +egwMenuItem.prototype.setGlobalOnClick = function(_onClick) +{ + this.onClick = _onClick; + _egwSetMenuOnClick(this.children, _onClick); +} + +//Setter functions for the menuitem properties + +egwMenuItem.prototype.set_id = function(_value) +{ + this.id = _value; +} + +egwMenuItem.prototype.set_caption = function(_value) +{ + //A value of "-" means that this element is a seperator. + this.caption = _value; +} + +egwMenuItem.prototype.set_checkbox = function(_value) +{ + this.checkbox = _value; +} + +egwMenuItem.prototype.set_checked = function(_value) +{ + if (_value && this.groupIndex > 0) + { + //Uncheck all other elements in this radio group + for (var i = 0; i < this.parent.children.length; i++) + { + var obj = this.parent.children[i]; + if (obj.groupIndex == this.groupIndex) + obj.checked = false; + } + } + this.checked = _value; +} + +egwMenuItem.prototype.set_groupIndex = function(_value) +{ + //If groupIndex is greater than 0 and the element is a checkbox, it is + //treated like a radio box + this.groupIndex = _value; +} + +egwMenuItem.prototype.set_enabled = function(_value) +{ + this.enabled = _value; +} + +egwMenuItem.prototype.set_onClick = function(_value) +{ + this.onClick = _value; +} + +egwMenuItem.prototype.set_iconUrl = function(_value) +{ + this.iconUrl = _value; +} + +egwMenuItem.prototype.set_default = function(_value) +{ + this.default = _value; +} + + diff --git a/phpgwapi/js/egw_action/egw_menu_dhtmlx.js b/phpgwapi/js/egw_action/egw_menu_dhtmlx.js new file mode 100644 index 0000000000..c0bb6fece3 --- /dev/null +++ b/phpgwapi/js/egw_action/egw_menu_dhtmlx.js @@ -0,0 +1,147 @@ +/** + * eGroupWare egw_action framework - JS Menu abstraction + * + * @link http://www.egroupware.org + * @author Andreas Stöckel + * @copyright 2011 by Andreas Stöckel + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package egw_action + * @version $Id:$ + */ + +function egwMenuImpl(_structure) +{ + //Create a new dhtmlxmenu object + this.dhtmlxmenu = new dhtmlXMenuObject(); + this.dhtmlxmenu.setSkin("egw"); + this.dhtmlxmenu.renderAsContextMenu(); + + var self = this; + + //Attach the simple click handler + this.dhtmlxmenu.attachEvent("onClick", function(id) { + if (id) + { + var elem = self.dhtmlxmenu.getUserData(id, 'egw_menu'); + if (elem && elem.onClick) + { + elem.onClick(elem); + } + } + }); + + //Attach the radiobutton click handler + this.dhtmlxmenu.attachEvent("onRadioClick", function(group, idChecked, idClicked, zoneId, casState) { + if (idClicked) + { + var elem = self.dhtmlxmenu.getUserData(idClicked, 'egw_menu'); + if (elem) + { + elem.set_checked(true); + } + } + + return true; + }); + + //Attach the radiobutton click handler + this.dhtmlxmenu.attachEvent("onCheckboxClick", function(id, state, zoneId, casState) { + if (id) + { + var elem = self.dhtmlxmenu.getUserData(id, 'egw_menu'); + if (elem) + { + elem.set_checked(!state); + } + } + + return true; + }); + + + //Translate the given structure to the dhtmlx object structure + this._translateStructure(_structure, this.dhtmlxmenu.topId, 0); +} + +egwMenuImpl.prototype._translateStructure = function(_structure, _parentId, _idCnt) +{ + //Initialize the counter which we will use to generate unique id's for all + //dhtmlx menu objects + var counter = 0; + var last_id = null; + + for (var i = 0; i < _structure.length; i++) + { + var id = 'elem_' + (_idCnt + counter); + var elem = _structure[i]; + + counter++; + + //Check whether this element is a seperator + if (elem.caption == '-' && last_id != null) + { + //Add the separator next to last_id with the id "id" + this.dhtmlxmenu.addNewSeparator(last_id, id); + } + else + { + if (elem.checkbox && elem.groupIndex === 0) + { + //Add checkbox + this.dhtmlxmenu.addCheckbox("child", _parentId, i, id, + elem.caption, elem.checked, !elem.enabled); + } + else if (elem.checkbox && elem.groupIndex > 0) + { + //Add radiobox + elem._dhtmlx_grpid = "grp_" + _idCnt + '_' + elem.groupIndex; + this.dhtmlxmenu.addRadioButton("child", _parentId, i, id, + elem.caption, elem._dhtmlx_grpid, elem.checked, !elem.enabled); + } + else + { + var caption = elem.caption; + if (elem.default) + caption = "" + caption + "" + this.dhtmlxmenu.addNewChild(_parentId, i, id, caption, !elem.enabled, + elem.iconUrl, elem.iconUrl); + } + + if (elem.children.length > 0) + { + counter += this._translateStructure(elem.children, id, (_idCnt + counter)); + } + } + + //Set the actual egw menu as user data element + this.dhtmlxmenu.setUserData(id, 'egw_menu', elem); + + var last_id = id; + } + + return counter; +} + + +egwMenuImpl.prototype.showAt = function(_x, _y, _onHide) +{ + var self = this; + + if (_onHide) + { + this.dhtmlxmenu.attachEvent("onHide", function(id) { + if (id === null) + { + _onHide(); + } + }); + } + this.dhtmlxmenu.showContextMenu(_x, _y); +} + +egwMenuImpl.prototype.hide = function() +{ + this.dhtmlxmenu.hide(); +} + + diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_chrd.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_chrd.gif new file mode 100644 index 0000000000..704f564ce5 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_chrd.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_loader.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_loader.gif new file mode 100644 index 0000000000..9ef515f1e3 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_loader.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_subar.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subar.gif new file mode 100644 index 0000000000..19fc12f356 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subar.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.gif new file mode 100644 index 0000000000..6f9b96036c Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.png b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.png new file mode 100644 index 0000000000..247435c0e6 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subselbg.png differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_subsepbg.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subsepbg.gif new file mode 100644 index 0000000000..50dffd218e Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_subsepbg.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_topbg.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topbg.gif new file mode 100644 index 0000000000..68463e8a1b Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topbg.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_topselbg.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topselbg.gif new file mode 100644 index 0000000000..117e8e6d31 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topselbg.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_topsepbg.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topsepbg.gif new file mode 100644 index 0000000000..c5277e3c44 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_topsepbg.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/dhxmenu_upar.gif b/phpgwapi/js/egw_action/test/imgs/dhxmenu_upar.gif new file mode 100644 index 0000000000..e96db88500 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/dhxmenu_upar.gif differ diff --git a/phpgwapi/js/egw_action/test/imgs/disk.png b/phpgwapi/js/egw_action/test/imgs/disk.png new file mode 100755 index 0000000000..99d532e8b1 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/disk.png differ diff --git a/phpgwapi/js/egw_action/test/imgs/folder.png b/phpgwapi/js/egw_action/test/imgs/folder.png new file mode 100755 index 0000000000..784e8fa482 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/folder.png differ diff --git a/phpgwapi/js/egw_action/test/imgs/page.png b/phpgwapi/js/egw_action/test/imgs/page.png new file mode 100755 index 0000000000..03ddd799fa Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/page.png differ diff --git a/phpgwapi/js/egw_action/test/imgs/wrench.png b/phpgwapi/js/egw_action/test/imgs/wrench.png new file mode 100755 index 0000000000..5c8213fef5 Binary files /dev/null and b/phpgwapi/js/egw_action/test/imgs/wrench.png differ diff --git a/phpgwapi/js/egw_action/test/js/dhtmlxcommon.js b/phpgwapi/js/egw_action/test/js/dhtmlxcommon.js new file mode 100644 index 0000000000..e8144c718a --- /dev/null +++ b/phpgwapi/js/egw_action/test/js/dhtmlxcommon.js @@ -0,0 +1,928 @@ +dhtmlx=function(obj){ + for (var a in obj) dhtmlx[a]=obj[a]; + return dhtmlx; //simple singleton +}; +dhtmlx.extend_api=function(name,map,ext){ + var t = window[name]; + if (!t) return; //component not defined + window[name]=function(obj){ + if (obj && typeof obj == "object" && !obj.tagName){ + var that = t.apply(this,(map._init?map._init(obj):arguments)); + //global settings + for (var a in dhtmlx) + if (map[a]) this[map[a]](dhtmlx[a]); + //local settings + for (var a in obj){ + if (map[a]) this[map[a]](obj[a]); + else if (a.indexOf("on")==0){ + this.attachEvent(a,obj[a]); + } + } + } else + var that = t.apply(this,arguments); + if (map._patch) map._patch(this); + return that||this; + }; + window[name].prototype=t.prototype; + if (ext) + dhtmlXHeir(window[name].prototype,ext); +}; + +dhtmlxAjax={ + get:function(url,callback){ + var t=new dtmlXMLLoaderObject(true); + t.async=(arguments.length<3); + t.waitCall=callback; + t.loadXML(url) + return t; + }, + post:function(url,post,callback){ + var t=new dtmlXMLLoaderObject(true); + t.async=(arguments.length<4); + t.waitCall=callback; + t.loadXML(url,true,post) + return t; + }, + getSync:function(url){ + return this.get(url,null,true) + }, + postSync:function(url,post){ + return this.post(url,post,null,true); + } +} + +/** + * @desc: xmlLoader object + * @type: private + * @param: funcObject - xml parser function + * @param: object - jsControl object + * @param: async - sync/async mode (async by default) + * @param: rSeed - enable/disable random seed ( prevent IE caching) + * @topic: 0 + */ +function dtmlXMLLoaderObject(funcObject, dhtmlObject, async, rSeed){ + this.xmlDoc=""; + + if (typeof (async) != "undefined") + this.async=async; + else + this.async=true; + + this.onloadAction=funcObject||null; + this.mainObject=dhtmlObject||null; + this.waitCall=null; + this.rSeed=rSeed||false; + return this; +}; +/** + * @desc: xml loading handler + * @type: private + * @param: dtmlObject - xmlLoader object + * @topic: 0 + */ +dtmlXMLLoaderObject.prototype.waitLoadFunction=function(dhtmlObject){ + var once = true; + this.check=function (){ + if ((dhtmlObject)&&(dhtmlObject.onloadAction != null)){ + if ((!dhtmlObject.xmlDoc.readyState)||(dhtmlObject.xmlDoc.readyState == 4)){ + if (!once) + return; + + once=false; //IE 5 fix + if (typeof dhtmlObject.onloadAction == "function") + dhtmlObject.onloadAction(dhtmlObject.mainObject, null, null, null, dhtmlObject); + + if (dhtmlObject.waitCall){ + dhtmlObject.waitCall.call(this,dhtmlObject); + dhtmlObject.waitCall=null; + } + } + } + }; + return this.check; +}; + +/** + * @desc: return XML top node + * @param: tagName - top XML node tag name (not used in IE, required for Safari and Mozilla) + * @type: private + * @returns: top XML node + * @topic: 0 + */ +dtmlXMLLoaderObject.prototype.getXMLTopNode=function(tagName, oldObj){ + if (this.xmlDoc.responseXML){ + var temp = this.xmlDoc.responseXML.getElementsByTagName(tagName); + if(temp.length==0 && tagName.indexOf(":")!=-1) + var temp = this.xmlDoc.responseXML.getElementsByTagName((tagName.split(":"))[1]); + var z = temp[0]; + } else + var z = this.xmlDoc.documentElement; + + if (z){ + this._retry=false; + return z; + } + + if ((_isIE)&&(!this._retry)){ + //fall back to MS.XMLDOM + var xmlString = this.xmlDoc.responseText; + var oldObj = this.xmlDoc; + this._retry=true; + this.xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); + this.xmlDoc.async=false; + this.xmlDoc["loadXM"+"L"](xmlString); + + return this.getXMLTopNode(tagName, oldObj); + } + dhtmlxError.throwError("LoadXML", "Incorrect XML", [ + (oldObj||this.xmlDoc), + this.mainObject + ]); + + return document.createElement("DIV"); +}; + +/** + * @desc: load XML from string + * @type: private + * @param: xmlString - xml string + * @topic: 0 + */ +dtmlXMLLoaderObject.prototype.loadXMLString=function(xmlString){ + { + try{ + var parser = new DOMParser(); + this.xmlDoc=parser.parseFromString(xmlString, "text/xml"); + } + catch (e){ + this.xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); + this.xmlDoc.async=this.async; + this.xmlDoc["loadXM"+"L"](xmlString); + } + } + + this.onloadAction(this.mainObject, null, null, null, this); + + if (this.waitCall){ + this.waitCall(); + this.waitCall=null; + } +} +/** + * @desc: load XML + * @type: private + * @param: filePath - xml file path + * @param: postMode - send POST request + * @param: postVars - list of vars for post request + * @topic: 0 + */ +dtmlXMLLoaderObject.prototype.loadXML=function(filePath, postMode, postVars, rpc){ + if (this.rSeed) + filePath+=((filePath.indexOf("?") != -1) ? "&" : "?")+"a_dhx_rSeed="+(new Date()).valueOf(); + this.filePath=filePath; + + if ((!_isIE)&&(window.XMLHttpRequest)) + this.xmlDoc=new XMLHttpRequest(); + else { + this.xmlDoc=new ActiveXObject("Microsoft.XMLHTTP"); + } + + if (this.async) + this.xmlDoc.onreadystatechange=new this.waitLoadFunction(this); + this.xmlDoc.open(postMode ? "POST" : "GET", filePath, this.async); + + if (rpc){ + this.xmlDoc.setRequestHeader("User-Agent", "dhtmlxRPC v0.1 ("+navigator.userAgent+")"); + this.xmlDoc.setRequestHeader("Content-type", "text/xml"); + } + + else if (postMode) + this.xmlDoc.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + this.xmlDoc.setRequestHeader("X-Requested-With","XMLHttpRequest"); + this.xmlDoc.send(null||postVars); + + if (!this.async) + (new this.waitLoadFunction(this))(); +}; +/** + * @desc: destructor, cleans used memory + * @type: private + * @topic: 0 + */ +dtmlXMLLoaderObject.prototype.destructor=function(){ + this._filterXPath = null; + this._getAllNamedChilds = null; + this._retry = null; + this.async = null; + this.rSeed = null; + this.filePath = null; + this.onloadAction = null; + this.mainObject = null; + this.xmlDoc = null; + this.doXPath = null; + this.doXPathOpera = null; + this.doXSLTransToObject = null; + this.doXSLTransToString = null; + this.loadXML = null; + this.loadXMLString = null; + // this.waitLoadFunction = null; + this.doSerialization = null; + this.xmlNodeToJSON = null; + this.getXMLTopNode = null; + this.setXSLParamValue = null; + return null; +} + +dtmlXMLLoaderObject.prototype.xmlNodeToJSON = function(node){ + var t={}; + for (var i=0; i-1) + _isChrome=true; + +if ((navigator.userAgent.indexOf('Safari') != -1)||(navigator.userAgent.indexOf('Konqueror') != -1)){ + var _KHTMLrv = parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Safari')+7, 5)); + + if (_KHTMLrv > 525){ //mimic FF behavior for Safari 3.1+ + _isFF=true; + var _FFrv = 1.9; + } else + _isKHTML=true; +} else if (navigator.userAgent.indexOf('Opera') != -1){ + _isOpera=true; + _OperaRv=parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Opera')+6, 3)); +} + + +else if (navigator.appName.indexOf("Microsoft") != -1){ + _isIE=true; + if (navigator.appVersion.indexOf("MSIE 8.0")!= -1 && document.compatMode != "BackCompat") _isIE=8; + if (navigator.appVersion.indexOf("MSIE 9.0")!= -1 && document.compatMode != "BackCompat") _isIE=8; +} else { + _isFF=true; + var _FFrv = parseFloat(navigator.userAgent.split("rv:")[1]) +} + + +//multibrowser Xpath processor +dtmlXMLLoaderObject.prototype.doXPath=function(xpathExp, docObj, namespace, result_type){ + if (_isKHTML || (!_isIE && !window.XPathResult)) + return this.doXPathOpera(xpathExp, docObj); + + if (_isIE){ //IE + if (!docObj) + if (!this.xmlDoc.nodeName) + docObj=this.xmlDoc.responseXML + else + docObj=this.xmlDoc; + + if (!docObj) + dhtmlxError.throwError("LoadXML", "Incorrect XML", [ + (docObj||this.xmlDoc), + this.mainObject + ]); + + if (namespace != null) + docObj.setProperty("SelectionNamespaces", "xmlns:xsl='"+namespace+"'"); // + + if (result_type == 'single'){ + return docObj.selectSingleNode(xpathExp); + } + else { + return docObj.selectNodes(xpathExp)||new Array(0); + } + } else { //Mozilla + var nodeObj = docObj; + + if (!docObj){ + if (!this.xmlDoc.nodeName){ + docObj=this.xmlDoc.responseXML + } + else { + docObj=this.xmlDoc; + } + } + + if (!docObj) + dhtmlxError.throwError("LoadXML", "Incorrect XML", [ + (docObj||this.xmlDoc), + this.mainObject + ]); + + if (docObj.nodeName.indexOf("document") != -1){ + nodeObj=docObj; + } + else { + nodeObj=docObj; + docObj=docObj.ownerDocument; + } + var retType = XPathResult.ANY_TYPE; + + if (result_type == 'single') + retType=XPathResult.FIRST_ORDERED_NODE_TYPE + var rowsCol = new Array(); + var col = docObj.evaluate(xpathExp, nodeObj, function(pref){ + return namespace + }, retType, null); + + if (retType == XPathResult.FIRST_ORDERED_NODE_TYPE){ + return col.singleNodeValue; + } + var thisColMemb = col.iterateNext(); + + while (thisColMemb){ + rowsCol[rowsCol.length]=thisColMemb; + thisColMemb=col.iterateNext(); + } + return rowsCol; + } +} + +function _dhtmlxError(type, name, params){ + if (!this.catches) + this.catches=new Array(); + + return this; +} + +_dhtmlxError.prototype.catchError=function(type, func_name){ + this.catches[type]=func_name; +} +_dhtmlxError.prototype.throwError=function(type, name, params){ + if (this.catches[type]) + return this.catches[type](type, name, params); + + if (this.catches["ALL"]) + return this.catches["ALL"](type, name, params); + + alert("Error type: "+arguments[0]+"\nDescription: "+arguments[1]); + return null; +} + +window.dhtmlxError=new _dhtmlxError(); + + +//opera fake, while 9.0 not released +//multibrowser Xpath processor +dtmlXMLLoaderObject.prototype.doXPathOpera=function(xpathExp, docObj){ + //this is fake for Opera + var z = xpathExp.replace(/[\/]+/gi, "/").split('/'); + var obj = null; + var i = 1; + + if (!z.length) + return []; + + if (z[0] == ".") + obj=[docObj]; else if (z[0] == ""){ + obj=(this.xmlDoc.responseXML||this.xmlDoc).getElementsByTagName(z[i].replace(/\[[^\]]*\]/g, "")); + i++; + } else + return []; + + for (i; i < z.length; i++)obj=this._getAllNamedChilds(obj, z[i]); + + if (z[i-1].indexOf("[") != -1) + obj=this._filterXPath(obj, z[i-1]); + return obj; +} + +dtmlXMLLoaderObject.prototype._filterXPath=function(a, b){ + var c = new Array(); + var b = b.replace(/[^\[]*\[\@/g, "").replace(/[\[\]\@]*/g, ""); + + for (var i = 0; i < a.length; i++) + if (a[i].getAttribute(b)) + c[c.length]=a[i]; + + return c; +} +dtmlXMLLoaderObject.prototype._getAllNamedChilds=function(a, b){ + var c = new Array(); + + if (_isKHTML) + b=b.toUpperCase(); + + for (var i = 0; i < a.length; i++)for (var j = 0; j < a[i].childNodes.length; j++){ + if (_isKHTML){ + if (a[i].childNodes[j].tagName&&a[i].childNodes[j].tagName.toUpperCase() == b) + c[c.length]=a[i].childNodes[j]; + } + + else if (a[i].childNodes[j].tagName == b) + c[c.length]=a[i].childNodes[j]; + } + + return c; +} + +function dhtmlXHeir(a, b){ + for (var c in b) + if (typeof (b[c]) == "function") + a[c]=b[c]; + return a; +} + +function dhtmlxEvent(el, event, handler){ + if (el.addEventListener) + el.addEventListener(event, handler, false); + + else if (el.attachEvent) + el.attachEvent("on"+event, handler); +} + +//============= XSL Extension =================================== + +dtmlXMLLoaderObject.prototype.xslDoc=null; +dtmlXMLLoaderObject.prototype.setXSLParamValue=function(paramName, paramValue, xslDoc){ + if (!xslDoc) + xslDoc=this.xslDoc + + if (xslDoc.responseXML) + xslDoc=xslDoc.responseXML; + var item = + this.doXPath("/xsl:stylesheet/xsl:variable[@name='"+paramName+"']", xslDoc, + "http:/\/www.w3.org/1999/XSL/Transform", "single"); + + if (item != null) + item.firstChild.nodeValue=paramValue +} +dtmlXMLLoaderObject.prototype.doXSLTransToObject=function(xslDoc, xmlDoc){ + if (!xslDoc) + xslDoc=this.xslDoc; + + if (xslDoc.responseXML) + xslDoc=xslDoc.responseXML + + if (!xmlDoc) + xmlDoc=this.xmlDoc; + + if (xmlDoc.responseXML) + xmlDoc=xmlDoc.responseXML + + //MOzilla + if (!_isIE){ + if (!this.XSLProcessor){ + this.XSLProcessor=new XSLTProcessor(); + this.XSLProcessor.importStylesheet(xslDoc); + } + var result = this.XSLProcessor.transformToDocument(xmlDoc); + } else { + var result = new ActiveXObject("Msxml2.DOMDocument.3.0"); + try{ + xmlDoc.transformNodeToObject(xslDoc, result); + }catch(e){ + result = xmlDoc.transformNode(xslDoc); + } + } + return result; +} + +dtmlXMLLoaderObject.prototype.doXSLTransToString=function(xslDoc, xmlDoc){ + var res = this.doXSLTransToObject(xslDoc, xmlDoc); + if(typeof(res)=="string") + return res; + return this.doSerialization(res); +} + +dtmlXMLLoaderObject.prototype.doSerialization=function(xmlDoc){ + if (!xmlDoc) + xmlDoc=this.xmlDoc; + if (xmlDoc.responseXML) + xmlDoc=xmlDoc.responseXML + if (!_isIE){ + var xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(xmlDoc); + } else + return xmlDoc.xml; +} + +/** +* @desc: +* @type: private +*/ +dhtmlxEventable=function(obj){ + obj.attachEvent=function(name, catcher, callObj){ + name='ev_'+name.toLowerCase(); + if (!this[name]) + this[name]=new this.eventCatcher(callObj||this); + + return(name+':'+this[name].addEvent(catcher)); //return ID (event name & event ID) + } + obj.callEvent=function(name, arg0){ + name='ev_'+name.toLowerCase(); + if (this[name]) + return this[name].apply(this, arg0); + return true; + } + obj.checkEvent=function(name){ + return (!!this['ev_'+name.toLowerCase()]) + } + obj.eventCatcher=function(obj){ + var dhx_catch = []; + var z = function(){ + var res = true; + for (var i = 0; i < dhx_catch.length; i++){ + if (dhx_catch[i] != null){ + var zr = dhx_catch[i].apply(obj, arguments); + res=res&&zr; + } + } + return res; + } + z.addEvent=function(ev){ + if (typeof (ev) != "function") + ev=eval(ev); + if (ev) + return dhx_catch.push(ev)-1; + return false; + } + z.removeEvent=function(id){ + dhx_catch[id]=null; + } + return z; + } + obj.detachEvent=function(id){ + if (id != false){ + var list = id.split(':'); //get EventName and ID + this[list[0]].removeEvent(list[1]); //remove event + } + } + obj.detachAllEvents = function(){ + for (var name in this){ + if (name.indexOf("ev_")==0) + delete this[name]; + } + } +} diff --git a/phpgwapi/js/egw_action/test/js/dhtmlxmenu.js b/phpgwapi/js/egw_action/test/js/dhtmlxmenu.js new file mode 100644 index 0000000000..85a88233e2 --- /dev/null +++ b/phpgwapi/js/egw_action/test/js/dhtmlxmenu.js @@ -0,0 +1,2190 @@ +//v.2.6 build 100722 + +/* +Copyright DHTMLX LTD. http://www.dhtmlx.com +You allowed to use this component or parts of it under GPL terms +To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com +*/ +/** +* @desc: a constructor, creates a new dhtmlxMenu object, baseId defines a base object for the top menu level +* @param: baseId - id of the html element to which a menu will be attached, in case of a contextual menu - if specified, will used as a contextual zone +* @type: public +*/ +function dhtmlXMenuObject(baseId, skin) { + var main_self = this; + + this.addBaseIdAsContextZone = null; + + this.isDhtmlxMenuObject = true; + + // skin settings + this.skin = (skin!=null?skin:"dhx_skyblue"); + this.imagePath = ""; + + // iframe + this._isIE6 = false; + if (_isIE) this._isIE6 = (window.XMLHttpRequest==null?true:false); + + if (baseId == null) { + this.base = document.body; + } else { + if (document.getElementById(baseId) != null) { + this.base = document.getElementById(baseId); + while (this.base.childNodes.length > 0) { this.base.removeChild(this.base.childNodes[0]); } + this.base.className += " dhtmlxMenu_"+this.skin+"_Middle dir_left"; + this.base._autoSkinUpdate = true; + // preserv default oncontextmenu for future restorin in case of context menu + if (this.base.oncontextmenu) this.base._oldContextMenuHandler = this.base.oncontextmenu; + // + this.addBaseIdAsContextZone = baseId; + this.base.onselectstart = function(e) { e = e || event; e.returnValue = false; return false; } + this.base.oncontextmenu = function(e) { e = e || event; e.returnValue = false; return false; } + } else { + this.base = document.body; + } + } + // this.topId = topId; + this.topId = "dhxWebMenuTopId"; + + // + if (!this.extendedModule) { + // add notify for menu + var t = function(){alert(this.i18n.dhxmenuextalert);}; + var extMethods = new Array("setItemEnabled", "setItemDisabled", "isItemEnabled", "_changeItemState", "getItemText", "setItemText", + "loadFromHTML", "hideItem", "showItem", "isItemHidden", "_changeItemVisible", "setUserData", "getUserData", + "setOpenMode", "setWebModeTimeout", "enableDynamicLoading", "_updateLoaderIcon", "getItemImage", "setItemImage", + "clearItemImage", "setAutoShowMode", "setAutoHideMode", "setContextMenuHideAllMode", "getContextMenuHideAllMode", + "setVisibleArea", "setTooltip", "getTooltip", "setHotKey", "getHotKey", "setItemSelected", "setTopText", "setRTL", + "setAlign", "setHref", "clearHref", "getCircuit", "_clearAllSelectedSubItemsInPolygon", "_checkArrowsState", + "_addUpArrow", "_addDownArrow", "_removeUpArrow", "_removeDownArrow", "_isArrowExists", "_doScrollUp", "_doScrollDown", + "_countPolygonItems", "setOverflowHeight", "_getRadioImgObj", "_setRadioState", "_radioOnClickHandler", + "getRadioChecked", "setRadioChecked", "addRadioButton", "_getCheckboxState", "_setCheckboxState", "_readLevel", + "_updateCheckboxImage", "_checkboxOnClickHandler", "setCheckboxState", "getCheckboxState", "addCheckbox", "serialize"); + for (var q=0; q