mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-24 07:39:27 +01:00
1216 lines
32 KiB
JavaScript
1216 lines
32 KiB
JavaScript
/**
|
|
* eGroupware Framework ui object
|
|
* @package framework
|
|
* @author Hadi Nategh <hn@stylite.de>
|
|
* @author Andreas Stoeckel <as@stylite.de>
|
|
* @copyright Stylite AG 2014
|
|
* @description Framework ui object, is implementation of UI class
|
|
*/
|
|
|
|
/*egw:uses
|
|
jquery.jquery;
|
|
/phpgwapi/js/jquery/mousewheel/mousewheel.js;
|
|
egw_inheritance.js;
|
|
*/
|
|
|
|
/**
|
|
* Constants definition
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* ui siemenu entry class
|
|
* Basic sidebar menu implementation
|
|
*
|
|
* @type @exp;Class@call;extend
|
|
*/
|
|
var fw_ui_sidemenu_entry = Class.extend({
|
|
|
|
|
|
/**
|
|
* Framework ui sidemenu entry class constructor
|
|
*
|
|
* @param {object} _parent specifies the parent egw_fw_ui_sidemenu
|
|
* @param {object} _baseDiv specifies "div" element the entries should be appended to.
|
|
* @param {object} _elemDiv
|
|
* @param {string} _name specifies the title of the entry in the side menu
|
|
* @param {string} _icon specifies the icon which should be viewd besides the title in the side menu
|
|
* @param {function}(_sender) _callback specifies the function which should be called when the entry is clicked. The _sender parameter passed is a reference to this egw_fw_ui_sidemenu_entry element.
|
|
* @param {object} _tag can be used to attach any user data to the object. Inside egw_fw _tag is used to attach an egw_fw_class_application to each sidemenu entry.
|
|
* @param {string} _app application name
|
|
*/
|
|
init: function (_parent, _baseDiv, _elemDiv, _name, _icon, _callback, _tag, _app)
|
|
{
|
|
this.baseDiv = _baseDiv;
|
|
this.elemDiv = _elemDiv;
|
|
this.entryName = _name;
|
|
this.icon = _icon;
|
|
this.tag = _tag;
|
|
this.parent = _parent;
|
|
this.atTop = false;
|
|
this.isDraged = false;
|
|
|
|
//Add a new div for the new entry to the base div
|
|
this.headerDiv = document.createElement("div");
|
|
this.headerDiv.id = _app+'_sidebox_header';
|
|
$j(this.headerDiv).addClass("egw_fw_ui_sidemenu_entry_header");
|
|
|
|
//Create the icon and set its image
|
|
var iconDiv = egw.image_element(this.icon, _name);
|
|
$j(iconDiv).addClass("egw_fw_ui_sidemenu_entry_icon");
|
|
|
|
//Create the AJAX loader image (currently NOT used)
|
|
this.ajaxloader = document.createElement("div");
|
|
$j(this.ajaxloader).addClass("egw_fw_ui_ajaxloader");
|
|
$j(this.ajaxloader).hide();
|
|
|
|
//Create the entry name header
|
|
var entryH1 = document.createElement("h1");
|
|
$j(entryH1).text(this.entryName);
|
|
|
|
//Append icon, name, and ajax loader
|
|
$j(this.headerDiv).append(iconDiv);
|
|
$j(this.headerDiv).append(entryH1);
|
|
$j(this.headerDiv).append(this.ajaxloader);
|
|
this.headerDiv._parent = this;
|
|
this.headerDiv._callbackObject = new egw_fw_class_callback(this, _callback);
|
|
$j(this.headerDiv).click(function(){
|
|
if (!this._parent.isDraged)
|
|
{
|
|
this._callbackObject.call(this);
|
|
}
|
|
this._parent.isDraged = false;
|
|
return true;
|
|
});
|
|
|
|
//Create the content div
|
|
this.contentDiv = document.createElement("div");
|
|
this.contentDiv.id = _app+'_sidebox_content';
|
|
$j(this.contentDiv).addClass("egw_fw_ui_sidemenu_entry_content");
|
|
$j(this.contentDiv).hide();
|
|
|
|
//Add in invisible marker to store the original position of this element in the DOM tree
|
|
this.marker = document.createElement("div");
|
|
this.marker._parent = this;
|
|
this.marker.className = 'egw_fw_ui_sidemenu_marker';
|
|
var entryH1_ = document.createElement("h1");
|
|
$j(entryH1_).text(this.entryName);
|
|
$j(this.marker).append(entryH1_);
|
|
$j(this.marker).hide();
|
|
|
|
//Create a container which contains all generated elements and is then added
|
|
//to the baseDiv
|
|
this.containerDiv = document.createElement("div");
|
|
this.containerDiv._parent = this;
|
|
$j(this.containerDiv).append(this.marker);
|
|
$j(this.containerDiv).append(this.headerDiv);
|
|
$j(this.containerDiv).append(this.contentDiv);
|
|
|
|
//Append header and content div to the base div
|
|
$j(this.elemDiv).append(this.containerDiv);
|
|
},
|
|
|
|
/**
|
|
* setContent replaces the content of the sidemenu entry with the content given by _content.
|
|
* @param {string} _content HTML/Text which should be displayed.
|
|
*/
|
|
setContent: function(_content)
|
|
{
|
|
//Set the content of the contentDiv
|
|
$j(this.contentDiv).empty();
|
|
$j(this.contentDiv).append(_content);
|
|
},
|
|
|
|
/**
|
|
* open openes this sidemenu_entry and displays the content.
|
|
*/
|
|
open: function()
|
|
{
|
|
/* Move this entry to the top of the list */
|
|
$j(this.baseDiv).prepend(this.contentDiv);
|
|
$j(this.baseDiv).prepend(this.headerDiv);
|
|
this.atTop = true;
|
|
|
|
$j(this.headerDiv).addClass("egw_fw_ui_sidemenu_entry_header_active");
|
|
$j(this.contentDiv).show();
|
|
},
|
|
|
|
/**
|
|
* close closes this sidemenu_entry and hides the content.
|
|
*/
|
|
close: function()
|
|
{
|
|
/* Move the content and header div behind the marker again */
|
|
if (this.atTop)
|
|
{
|
|
$j(this.marker).after(this.contentDiv);
|
|
$j(this.marker).after(this.headerDiv);
|
|
this.atTop = false;
|
|
}
|
|
|
|
$j(this.headerDiv).removeClass("egw_fw_ui_sidemenu_entry_header_active");
|
|
$j(this.contentDiv).hide();
|
|
},
|
|
|
|
/**
|
|
* egw_fw_ui_sidemenu_entry_header_active
|
|
* showAjaxLoader shows the AjaxLoader animation which should be displayed when
|
|
* the content of the sidemenu entry is just being loaded.
|
|
*/
|
|
showAjaxLoader: function()
|
|
{
|
|
$j(this.ajaxloader).show();
|
|
},
|
|
|
|
/**
|
|
* showAjaxLoader hides the AjaxLoader animation
|
|
*/
|
|
hideAjaxLoader: function()
|
|
{
|
|
$j(this.ajaxloader).hide();
|
|
},
|
|
|
|
/**
|
|
* Removes this entry.
|
|
*/
|
|
remove: function()
|
|
{
|
|
$j(this.headerDiv).remove();
|
|
$j(this.contentDiv).remove();
|
|
}
|
|
});
|
|
|
|
/**
|
|
*
|
|
* @type @exp;Class@call;extend
|
|
*/
|
|
var fw_ui_sidemenu = Class.extend({
|
|
/**
|
|
* The constructor of the egw_fw_ui_sidemenu.
|
|
*
|
|
* @param {object} _baseDiv specifies the "div" in which all entries added by the addEntry function should be displayed.
|
|
*/
|
|
init:function(_baseDiv)
|
|
{
|
|
this.baseDiv = _baseDiv;
|
|
this.elemDiv = document.createElement('div');
|
|
$j(this.baseDiv).append(this.elemDiv);
|
|
this.entries = new Array();
|
|
this.activeEntry = null;
|
|
},
|
|
|
|
/**
|
|
* Funtion used internally to recursively step through a dom tree and add all appliction
|
|
* markers in their order of appereance
|
|
*
|
|
* @param {array} _resultArray
|
|
* @param {array} _children
|
|
*/
|
|
_searchMarkers: function(_resultArray, _children)
|
|
{
|
|
for (var i = 0; i < _children.length; i++)
|
|
{
|
|
var child = _children[i];
|
|
|
|
if (child.className == 'egw_fw_ui_sidemenu_marker' && typeof child._parent != 'undefined')
|
|
{
|
|
_resultArray.push(child._parent);
|
|
}
|
|
|
|
this._searchMarkers(_resultArray, child.childNodes);
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* Adds an entry to the sidemenu.
|
|
*
|
|
* @param {string} _name specifies the title of the new sidemenu entry
|
|
* @param {string} _icon specifies the icon displayed aside the title
|
|
* @param {function}(_sender) _callback specifies the function which should be called when a callback is clicked
|
|
* @param {object} _tag extra data
|
|
* @param {string} _app application name
|
|
*/
|
|
addEntry: function(_name, _icon, _callback, _tag, _app)
|
|
{
|
|
//Create a new sidemenu entry and add it to the list
|
|
var entry = new egw_fw_ui_sidemenu_entry(this, this.baseDiv, this.elemDiv, _name, _icon,
|
|
_callback, _tag, _app);
|
|
this.entries[this.entries.length] = entry;
|
|
|
|
return entry;
|
|
},
|
|
|
|
/**
|
|
* Openes the specified entry whilst closing all other entries in the list.
|
|
*
|
|
* @param {object} _entry specifies the entry which should be opened.
|
|
*/
|
|
open: function(_entry)
|
|
{
|
|
//Close all other entries
|
|
for (var i = 0; i < this.entries.length; i++)
|
|
{
|
|
if (this.entries[i] != _entry)
|
|
{
|
|
this.entries[i].close();
|
|
}
|
|
}
|
|
|
|
if (_entry != null)
|
|
{
|
|
_entry.open();
|
|
}
|
|
|
|
this.activeEntry = _entry;
|
|
},
|
|
|
|
|
|
/**
|
|
* Deletes all sidemenu entries.
|
|
*/
|
|
clean: function()
|
|
{
|
|
for (var i = 0; i < this.entries.length; i++)
|
|
{
|
|
this.entries[i].remove();
|
|
}
|
|
|
|
this.entries = new Array();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Class: egw_fw_ui_tab
|
|
* The egw_fw_ui_tab represents a single tab "sheet" in the ui
|
|
*/
|
|
|
|
|
|
/**
|
|
* The constructor of the egw_fw_ui_tab class.
|
|
*
|
|
* @param {object} _parent specifies the parent egw_fw_ui_tabs class
|
|
* @param {object} _contHeaderDiv specifies the container "div" element, which should contain the headers
|
|
* @param {object} _contDiv specifies the container "div" element, which should contain the contents of the tabs
|
|
* @param {string} _icon specifies the icon which should be viewed besides the title of the tab
|
|
* @param {function}(_sender) _callback specifies the function which should be called when the tab title is clicked. The _sender parameter passed is a reference to this egw_fw_ui_tab element.
|
|
* @param {function}(_sender) _closeCallback specifies the function which should be called when the tab close button is clicked. The _sender parameter passed is a reference to this egw_fw_ui_tab element.
|
|
* @param {object} _tag can be used to attach any user data to the object. Inside egw_fw _tag is used to attach an egw_fw_class_application to each sidemenu entry.
|
|
* @param {int} _pos is the position where the tab will be inserted
|
|
*/
|
|
function egw_fw_ui_tab(_parent, _contHeaderDiv, _contDiv, _icon, _callback,
|
|
_closeCallback, _tag, _pos)
|
|
{
|
|
this.parent = _parent;
|
|
this.contHeaderDiv = _contHeaderDiv;
|
|
this.contDiv = _contDiv;
|
|
this.title = '';
|
|
this.tag = _tag;
|
|
this.closeable = true;
|
|
this.callback = _callback;
|
|
this.closeCallback = _closeCallback;
|
|
this.position = _pos;
|
|
|
|
//Create the header div and set its "click" function and "hover" event
|
|
this.headerDiv = document.createElement("span");
|
|
this.headerDiv._position = _pos;
|
|
$j(this.headerDiv).addClass("egw_fw_ui_tab_header");
|
|
|
|
//Create a new callback object and attach it to the header div
|
|
this.headerDiv._callbackObject = new egw_fw_class_callback(this, _callback);
|
|
$j(this.headerDiv).click(
|
|
function(){
|
|
this._callbackObject.call(this);
|
|
});
|
|
|
|
//Attach the hover effect to the header div
|
|
$j(this.headerDiv).hover(
|
|
function() {
|
|
if (!$j(this).hasClass("egw_fw_ui_tab_header_active"))
|
|
$j(this).addClass("egw_fw_ui_tab_header_hover");
|
|
},
|
|
function() {
|
|
$j(this).removeClass("egw_fw_ui_tab_header_hover");
|
|
}
|
|
);
|
|
|
|
// If dragging something over the tab, activate that app
|
|
var tab = this.headerDiv;
|
|
$j(this.headerDiv).droppable({
|
|
tolerance:"pointer",
|
|
over: function() {
|
|
tab._callbackObject.call(tab);
|
|
}
|
|
});
|
|
|
|
|
|
//Create the close button and append it to the header div
|
|
this.closeButton = document.createElement("span");
|
|
this.closeButton._callbackObject = new egw_fw_class_callback(this, _closeCallback);
|
|
$j(this.closeButton).addClass("egw_fw_ui_tab_close_button");
|
|
$j(this.closeButton).click(
|
|
function(){
|
|
//Only call the close callback if the tab is set closeable
|
|
if (this._callbackObject.context.closeable)
|
|
{
|
|
this._callbackObject.call(this);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
|
|
$j(this.headerDiv).append(this.closeButton);
|
|
|
|
//Create the icon and append it to the header div
|
|
var icon = egw.image_element(_icon);
|
|
$j(icon).addClass("egw_fw_ui_tab_icon");
|
|
$j(this.headerDiv).append(icon);
|
|
|
|
//Create the title h1 and append it to the header div
|
|
this.headerH1 = document.createElement("h1");
|
|
this.setTitle('');
|
|
$j(this.headerDiv).append(this.headerH1);
|
|
|
|
|
|
$j(this.headerDiv).append(this.closeButton);
|
|
|
|
this.contentDiv = document.createElement("div");
|
|
$j(this.contentDiv).addClass("egw_fw_ui_tab_content");
|
|
$j(this.contentDiv).hide();
|
|
|
|
//Sort the element in at the given position
|
|
var _this = this;
|
|
var $_children = $j(this.contHeaderDiv).children();
|
|
var _cnt = $_children.size();
|
|
|
|
if (_cnt > 0 && _pos > -1)
|
|
{
|
|
$_children.each(function(i) {
|
|
if (_pos <= this._position)
|
|
{
|
|
$j(this).before(_this.headerDiv);
|
|
return false;
|
|
}
|
|
else if (i == (_cnt - 1))
|
|
{
|
|
$j(this).after(_this.headerDiv);
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
$j(this.contHeaderDiv).append(this.headerDiv);
|
|
}
|
|
|
|
$j(this.contDiv).append(this.contentDiv);
|
|
}
|
|
|
|
/**
|
|
* setTitle sets the title of this tab. An existing title will be removed.
|
|
*
|
|
* @param {string} _title HTML/Text which should be displayed.
|
|
*/
|
|
egw_fw_ui_tab.prototype.setTitle = function(_title)
|
|
{
|
|
this.title = _title;
|
|
$j(this.headerH1).empty();
|
|
$j(this.headerH1).text(_title);
|
|
};
|
|
|
|
/**
|
|
* setTitle sets the content of this tab. Existing content is removed.
|
|
*
|
|
* @param {string} _content HTML/Text which should be displayed.
|
|
*/
|
|
egw_fw_ui_tab.prototype.setContent = function(_content)
|
|
{
|
|
$j(this.contentDiv).empty();
|
|
$j(this.contentDiv).append(_content);
|
|
};
|
|
|
|
/**
|
|
* Shows the content of the tab. Only one tab should be displayed at once. By using egw_fw_ui_tabs.showTab
|
|
* you can assure this.
|
|
*/
|
|
egw_fw_ui_tab.prototype.show = function()
|
|
{
|
|
$j(this.headerDiv).addClass("egw_fw_ui_tab_header_active");
|
|
var content = $j(this.contentDiv);
|
|
if(!content.is(':visible'))
|
|
{
|
|
content.show();
|
|
|
|
// Trigger an event on the browser content, so apps & widgets know
|
|
if(this.tag && this.tag.browser && this.tag.browser.contentDiv)
|
|
{
|
|
$j(this.tag.browser.contentDiv).trigger('show');
|
|
}
|
|
else if(content) // if the content is an iframe (eg. Calendar views)
|
|
{
|
|
$j(content).find('.egw_fw_content_browser_iframe').trigger('show');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Hides the content of this tab.
|
|
*/
|
|
egw_fw_ui_tab.prototype.hide = function()
|
|
{
|
|
$j(this.headerDiv).removeClass("egw_fw_ui_tab_header_active");
|
|
var content = $j(this.contentDiv);
|
|
if(content.is(':visible'))
|
|
{
|
|
content.hide();
|
|
|
|
// Trigger an event on the browser content, so apps & widgets know
|
|
if(this.tag && this.tag.browser && this.tag.browser.contentDiv)
|
|
{
|
|
$j(this.tag.browser.contentDiv).trigger('hide');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Removes this tab and all its content.
|
|
*/
|
|
egw_fw_ui_tab.prototype.remove = function()
|
|
{
|
|
this.hide();
|
|
$j(this.contentDiv).remove();
|
|
$j(this.headerDiv).remove();
|
|
};
|
|
|
|
/**
|
|
* Sets whether the close button is shown/the close callback ever gets called.
|
|
*
|
|
* @param {boolean} _closeable if true, the close button is shown, if false, the close button is hidden. default is true.
|
|
*/
|
|
egw_fw_ui_tab.prototype.setCloseable = function(_closeable)
|
|
{
|
|
this.closeable = _closeable;
|
|
if (_closeable)
|
|
$j(this.closeButton).show();
|
|
else
|
|
$j(this.closeButton).hide();
|
|
};
|
|
|
|
|
|
/**
|
|
* Class: egw_fw_ui_tabs
|
|
* The egw_fw_ui_tabs class cares about displaying a set of tab sheets.
|
|
*/
|
|
|
|
|
|
/**
|
|
* The constructor of the egw_fw_ui_sidemenu_tabs class. Two "divs" are created inside the specified container element, one for the tab headers and one for the tab contents.
|
|
*
|
|
* @param {object} _contDiv specifies "div" element the tab ui element should be displayed in.
|
|
*/
|
|
function egw_fw_ui_tabs(_contDiv)
|
|
{
|
|
this.contDiv = _contDiv;
|
|
|
|
//Create a div for the tab headers
|
|
this.contHeaderDiv = document.createElement("div");
|
|
$j(this.contHeaderDiv).addClass("egw_fw_ui_tabs_header");
|
|
$j(this.contDiv).append(this.contHeaderDiv);
|
|
|
|
this.appHeaderContainer = $j(document.createElement("div"));
|
|
this.appHeaderContainer.addClass("egw_fw_ui_app_header_container");
|
|
$j(this.contDiv).append(this.appHeaderContainer);
|
|
|
|
this.appHeader = $j(document.createElement("div"));
|
|
this.appHeader.addClass("egw_fw_ui_app_header");
|
|
this.appHeader.hide();
|
|
this.appHeaderContainer.append(this.appHeader);
|
|
|
|
this.tabs = Array();
|
|
|
|
this.activeTab = null;
|
|
this.tabHistory = Array();
|
|
}
|
|
|
|
/**
|
|
* Sets the "appHeader" text below the tabs list.
|
|
*
|
|
* @param {string} _text is the text which will be seen in the appHeader.
|
|
* @param {string} _msg_class css class for message
|
|
*/
|
|
egw_fw_ui_tabs.prototype.setAppHeader = function(_text, _msg_class)
|
|
{
|
|
this.appHeader.text(_text);
|
|
this.appHeader.prop('class', "egw_fw_ui_app_header");
|
|
if (_msg_class) this.appHeader.addClass(_msg_class);
|
|
this.appHeader.show();
|
|
};
|
|
|
|
/**
|
|
* Function internally used to remove double entries from the tab history. The tab
|
|
* history is used to store the order in which the tabs have been opened, to be able
|
|
* to switch back to the last tab when a tab is closed. Double entries in the tab history
|
|
* may appear whenever a tab is deleted.
|
|
*/
|
|
egw_fw_ui_tabs.prototype.cleanHistory = function()
|
|
{
|
|
for (var i = this.tabHistory.length - 1; i >= 0; i--)
|
|
{
|
|
if (this.tabHistory[i] == this.tabHistory[i - 1])
|
|
{
|
|
array_remove(this.tabHistory, i);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds a new tab to the tabs ui element.
|
|
* @param {string} _icon which should be displayed on the tab sheet header
|
|
* @param {function} _callback (_sender) function which should be called whenever the tab header is clicked. The _sender parameter passed is a reference to this egw_fw_ui_tab element.
|
|
* @param {function} _closeCallback (_sender) function which should be called whenever the close button of the tab is clicked. The _sender parameter passed is a reference to this egw_fw_ui_tab element.
|
|
* @param {object} _tag can be used to attach any user data to the object. Inside egw_fw _tag is used to attach an egw_fw_class_application to each sidemenu entry.
|
|
* @param {int} _pos specifies the position in the tab list. If _pos is -1, the tab will be added to the end of the tab list
|
|
*/
|
|
egw_fw_ui_tabs.prototype.addTab = function(_icon, _callback, _closeCallback, _tag, _pos)
|
|
{
|
|
var pos = -1;
|
|
if (typeof _pos != 'undefined')
|
|
pos = _pos;
|
|
|
|
var tab = new egw_fw_ui_tab(this, this.contHeaderDiv, this.contDiv, _icon, _callback,
|
|
_closeCallback, _tag, pos);
|
|
|
|
//Insert the tab into the tab list.
|
|
var inserted = false;
|
|
if (pos > -1)
|
|
{
|
|
for (var i in this.tabs)
|
|
{
|
|
if (this.tabs[i].position > pos)
|
|
{
|
|
this.tabs.splice(i, 0, tab);
|
|
inserted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pos == -1 || !inserted)
|
|
{
|
|
this.tabs[this.tabs.length] = tab;
|
|
}
|
|
|
|
if (this.activeTab == null)
|
|
this.showTab(tab);
|
|
|
|
return tab;
|
|
};
|
|
|
|
/**
|
|
* Removes the specified tab from the tab list whilst trying to keep one tab open.
|
|
* The tab which will be opened is determined throughout the tab open history.
|
|
*
|
|
* @param {object} _tab is the object which should be closed.
|
|
*/
|
|
egw_fw_ui_tabs.prototype.removeTab = function(_tab)
|
|
{
|
|
//Delete the deleted tab from the history
|
|
for (var i = this.tabHistory.length - 1; i >= 0; i--)
|
|
{
|
|
if (this.tabHistory[i] == _tab)
|
|
array_remove(this.tabHistory, i);
|
|
}
|
|
|
|
//Delete entries in the histroy which might be double
|
|
this.cleanHistory();
|
|
|
|
//Special treatement if the currently active tab gets deleted
|
|
if (_tab == this.activeTab)
|
|
{
|
|
//Search for the next tab which should be selected
|
|
if (this.tabs.length > 0)
|
|
{
|
|
//Check whether there is another tab in the tab history,
|
|
//if not, simply show the first tab in the list.
|
|
var tab = this.tabs[0];
|
|
if (typeof this.tabHistory[this.tabHistory.length - 1] != 'undefined')
|
|
{
|
|
tab = this.tabHistory[this.tabHistory.length - 1];
|
|
}
|
|
|
|
tab.callback.call(tab);
|
|
}
|
|
}
|
|
|
|
//Perform the actual deletion of the tab
|
|
_tab.remove();
|
|
for (var i = this.tabs.length - 1; i >= 0; i--)
|
|
{
|
|
if (this.tabs[i] == _tab)
|
|
array_remove(this.tabs, i);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Shows the specified _tab whilst closing all others.
|
|
*
|
|
* @param {object} _tab is the object which should be opened.
|
|
*/
|
|
egw_fw_ui_tabs.prototype.showTab = function(_tab)
|
|
{
|
|
if (this.activeTab != _tab)
|
|
{
|
|
for (var i = 0; i < this.tabs.length; i++)
|
|
{
|
|
if (this.tabs[i] != _tab)
|
|
{
|
|
this.tabs[i].hide();
|
|
}
|
|
}
|
|
|
|
_tab.show();
|
|
this.activeTab = _tab;
|
|
|
|
if (this.tabHistory[this.tabHistory.length - 1] != _tab)
|
|
this.tabHistory[this.tabHistory.length] = _tab;
|
|
|
|
//Limit the tabHistory size in order to save memory
|
|
if (this.tabHistory.length > 50)
|
|
{
|
|
array_remove(this.tabHistory, 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Calls the setCloseable function of all tabs in the list.
|
|
*
|
|
* @param {boolean} _closeable
|
|
*/
|
|
egw_fw_ui_tabs.prototype.setCloseable = function(_closeable)
|
|
{
|
|
for (var i = 0; i < this.tabs.length; i++)
|
|
{
|
|
this.tabs[i].setCloseable(_closeable);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Clears all data, removes all tabs, independently from the question, whether they may be closed or
|
|
* not.
|
|
*/
|
|
egw_fw_ui_tabs.prototype.clean = function()
|
|
{
|
|
//Remove all tabs, clean the tabs array
|
|
for (var i = 0; i < this.tabs.length; i++)
|
|
{
|
|
array_remove(this.tabs, i);
|
|
}
|
|
|
|
//Reset all arrays and references
|
|
this.tabs = new Array();
|
|
this.activeTab = null;
|
|
this.tabHistroy = new Array();
|
|
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Class: egw_fw_ui_category
|
|
* A class which manages and renderes a simple menu with categories, which can be opened and shown
|
|
*
|
|
* @param {object} _contDiv
|
|
* @param {string} _name
|
|
* @param {string} _title
|
|
* @param {object} _content
|
|
* @param {function} _callback
|
|
* @param {function} _animationCallback
|
|
* @param {object} _tag
|
|
*/
|
|
|
|
function egw_fw_ui_category(_contDiv, _name, _title, _content, _callback, _animationCallback, _tag)
|
|
{
|
|
//Copy the parameters
|
|
this.contDiv = _contDiv;
|
|
this.catName = _name;
|
|
this.callback = _callback;
|
|
this.animationCallback = _animationCallback;
|
|
this.tag = _tag;
|
|
|
|
//Create the ui divs
|
|
this.headerDiv = document.createElement('div');
|
|
$j(this.headerDiv).addClass('egw_fw_ui_category');
|
|
|
|
//Add the text
|
|
var entryH1 = document.createElement('h1');
|
|
$j(entryH1).append(_title);
|
|
$j(this.headerDiv).append(entryH1);
|
|
|
|
//Add the content
|
|
this.contentDiv = document.createElement('div');
|
|
this.contentDiv._parent = this;
|
|
$j(this.contentDiv).addClass('egw_fw_ui_category_content');
|
|
$j(this.contentDiv).append(_content);
|
|
$j(this.contentDiv).hide();
|
|
|
|
//Add content and header to the content div, add some magic jQuery code in order to make it foldable
|
|
this.headerDiv._parent = this;
|
|
$j(this.headerDiv).click(
|
|
function() {
|
|
if (!$j(this).hasClass('egw_fw_ui_category_active'))
|
|
{
|
|
this._parent.open(false);
|
|
}
|
|
else
|
|
{
|
|
this._parent.close(false);
|
|
}
|
|
});
|
|
$j(this.contDiv).append(this.headerDiv);
|
|
$j(this.contDiv).append(this.contentDiv);
|
|
}
|
|
|
|
egw_fw_ui_category.prototype.open = function(_instantly)
|
|
{
|
|
this.callback.call(this, true);
|
|
$j(this.headerDiv).addClass('egw_fw_ui_category_active');
|
|
|
|
if (_instantly)
|
|
{
|
|
$j(this.contentDiv).show();
|
|
this.animationCallback();
|
|
}
|
|
else
|
|
{
|
|
$j(this.contentDiv).slideDown(200, function() {
|
|
this._parent.animationCallback.call(this._parent);
|
|
});
|
|
}
|
|
};
|
|
|
|
egw_fw_ui_category.prototype.close = function(_instantly)
|
|
{
|
|
this.callback.call(this, false);
|
|
$j(this.headerDiv).removeClass('egw_fw_ui_category_active');
|
|
|
|
if (_instantly)
|
|
{
|
|
$j(this.contentDiv).hide();
|
|
this.animationCallback();
|
|
}
|
|
else
|
|
{
|
|
$j(this.contentDiv).slideUp(200, function() {
|
|
this._parent.animationCallback.call(this._parent);
|
|
});
|
|
}
|
|
};
|
|
|
|
egw_fw_ui_category.prototype.remove = function()
|
|
{
|
|
//Delete the content and header div
|
|
$j(this.contDiv).remove();
|
|
$j(this.headerDiv).remove();
|
|
};
|
|
|
|
/**
|
|
* egw_fw_ui_scrollarea class
|
|
*
|
|
* @param {object} _contDiv
|
|
*/
|
|
|
|
function egw_fw_ui_scrollarea(_contDiv)
|
|
{
|
|
this.startScrollSpeed = 50.0; //in px/sec
|
|
this.endScrollSpeed = 250.0; //in px/sec
|
|
this.scrollSpeedAccel = 75.0; //in px/sec^2
|
|
this.timerInterval = 0.04; //in seconds //20ms is the timer base timer resolution on windows systems
|
|
|
|
this.contDiv = _contDiv;
|
|
this.contHeight = 0;
|
|
this.boxHeight = 0;
|
|
this.scrollPos = 0;
|
|
this.buttonScrollOffs = 0;
|
|
this.maxScrollPos = 0;
|
|
this.buttonsVisible = true;
|
|
this.mouseOver = false;
|
|
this.scrollTime = 0.0;
|
|
this.btnUpEnabled = true;
|
|
this.btnDownEnabled = true;
|
|
|
|
//Wrap a new "scroll" div around the content of the content div
|
|
this.scrollDiv = document.createElement("div");
|
|
this.scrollDiv.style.position = "relative";
|
|
$j(this.scrollDiv).addClass("egw_fw_ui_scrollarea");
|
|
|
|
//Mousewheel handler
|
|
var self = this;
|
|
$j(this.scrollDiv).on('mousewheel',function(e, delta) {
|
|
// Do not scrolldown/up when we are on selectbox items
|
|
// seems Firefox does not prevent the mousewheel event over
|
|
// selectbox items with scrollbars
|
|
if (delta && e.target.tagName != "OPTION" && e.target.tagName != "SELECT")
|
|
{
|
|
e.stopPropagation();
|
|
self.scrollDelta(- delta * 30);
|
|
if (self.contHeight != this.scrollHeight) self.update();
|
|
}
|
|
|
|
});
|
|
|
|
//Create a container which contains the up/down buttons and the scrollDiv
|
|
this.outerDiv = document.createElement("div");
|
|
$j(this.outerDiv).addClass("egw_fw_ui_scrollarea_outerdiv");
|
|
$j(this.outerDiv).append(this.scrollDiv);
|
|
|
|
$j(this.contDiv).children().appendTo(this.scrollDiv);
|
|
$j(this.contDiv).append(this.outerDiv);
|
|
this.contentDiv = this.scrollDiv;
|
|
|
|
//Create the "up" and the "down" button
|
|
this.btnUp = document.createElement("span");
|
|
$j(this.btnUp).addClass("egw_fw_ui_scrollarea_button");
|
|
$j(this.btnUp).addClass("egw_fw_ui_scrollarea_button_up");
|
|
$j(this.btnUp).hide();
|
|
|
|
this.btnUp._parent = this;
|
|
$j(this.btnUp).mouseenter(function(){
|
|
this._parent.mouseOverToggle(true, -1);
|
|
$j(this).addClass("egw_fw_ui_scrollarea_button_hover");
|
|
});
|
|
$j(this.btnUp).click(function(){
|
|
this._parent.setScrollPos(0);
|
|
});
|
|
$j(this.btnUp).mouseleave(function(){
|
|
this._parent.mouseOverToggle(false, -1);
|
|
$j(this).removeClass("egw_fw_ui_scrollarea_button_hover");
|
|
});
|
|
|
|
$j(this.outerDiv).prepend(this.btnUp);
|
|
|
|
this.btnDown = document.createElement("span");
|
|
$j(this.btnDown).addClass("egw_fw_ui_scrollarea_button");
|
|
$j(this.btnDown).addClass("egw_fw_ui_scrollarea_button_down");
|
|
$j(this.btnDown).hide();
|
|
|
|
this.btnDown._parent = this;
|
|
$j(this.btnDown).mouseenter(function(){
|
|
this._parent.mouseOverToggle(true, 1);
|
|
$j(this).addClass("egw_fw_ui_scrollarea_button_hover");
|
|
});
|
|
$j(this.btnDown).click(function() {
|
|
this._parent.setScrollPos(this._parent.maxScrollPos);
|
|
});
|
|
$j(this.btnDown).mouseleave(function(){
|
|
this._parent.mouseOverToggle(false, 1);
|
|
$j(this).removeClass("egw_fw_ui_scrollarea_button_hover");
|
|
});
|
|
|
|
$j(this.outerDiv).prepend(this.btnDown);
|
|
|
|
//Update - read height of the children elements etc.
|
|
this.update();
|
|
}
|
|
|
|
egw_fw_ui_scrollarea.prototype.setScrollPos = function(_pos)
|
|
{
|
|
if (this.buttonsVisible)
|
|
{
|
|
if (_pos <= 0)
|
|
{
|
|
if (this.btnUpEnabled)
|
|
$j(this.btnUp).addClass("egw_fw_ui_scrollarea_button_disabled");
|
|
if (!this.btnDownEnabled)
|
|
$j(this.btnDown).removeClass("egw_fw_ui_scrollarea_button_disabled");
|
|
this.btnDownEnabled = true;
|
|
this.btnUpEnabled = false;
|
|
|
|
_pos = 0;
|
|
}
|
|
else if (_pos >= this.maxScrollPos)
|
|
{
|
|
if (this.btnDownEnabled)
|
|
$j(this.btnDown).addClass("egw_fw_ui_scrollarea_button_disabled");
|
|
if (!this.btnUpEnabled)
|
|
$j(this.btnUp).removeClass("egw_fw_ui_scrollarea_button_disabled");
|
|
this.btnDownEnabled = false;
|
|
this.btnUpEnabled = true;
|
|
|
|
_pos = this.maxScrollPos;
|
|
}
|
|
else
|
|
{
|
|
if (!this.btnUpEnabled)
|
|
$j(this.btnUp).removeClass("egw_fw_ui_scrollarea_button_disabled");
|
|
if (!this.btnDownEnabled)
|
|
$j(this.btnDown).removeClass("egw_fw_ui_scrollarea_button_disabled");
|
|
this.btnUpEnabled = true;
|
|
this.btnDownEnabled = true;
|
|
}
|
|
|
|
this.scrollPos = _pos;
|
|
|
|
//Apply the calculated scroll position to the scrollDiv
|
|
this.scrollDiv.style.top = Math.round(-_pos) + 'px';
|
|
}
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.scrollDelta = function(_delta)
|
|
{
|
|
this.setScrollPos(this.scrollPos + _delta);
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.toggleButtons = function(_visible)
|
|
{
|
|
if (_visible)
|
|
{
|
|
$j(this.btnDown).show();
|
|
$j(this.btnUp).show();
|
|
this.buttonHeight = $j(this.btnDown).outerHeight();
|
|
this.maxScrollPos = this.contHeight - this.boxHeight;
|
|
this.setScrollPos(this.scrollPos);
|
|
}
|
|
else
|
|
{
|
|
this.scrollDiv.style.top = '0';
|
|
$j(this.btnDown).hide();
|
|
$j(this.btnUp).hide();
|
|
}
|
|
|
|
this.buttonsVisible = _visible;
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.update = function()
|
|
{
|
|
//Get the height of the content and the outer box
|
|
this.contHeight = $j(this.scrollDiv).outerHeight();
|
|
this.boxHeight = $j(this.contDiv).height();
|
|
|
|
this.toggleButtons(this.contHeight > this.boxHeight);
|
|
this.setScrollPos(this.scrollPos);
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.getScrollDelta = function(_timeGap)
|
|
{
|
|
//Calculate the current scroll speed
|
|
var curScrollSpeed = this.startScrollSpeed + this.scrollSpeedAccel * this.scrollTime;
|
|
if (curScrollSpeed > this.endScrollSpeed)
|
|
{
|
|
curScrollSpeed = this.endScrollSpeed;
|
|
}
|
|
|
|
//Increment the scroll time counter
|
|
this.scrollTime = this.scrollTime + _timeGap;
|
|
|
|
//Return the actual delta value
|
|
return curScrollSpeed * _timeGap;
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.mouseOverCallback = function(_context)
|
|
{
|
|
//Do the scrolling
|
|
_context.scrollDelta(_context.getScrollDelta(_context.timerInterval) *
|
|
_context.dir);
|
|
|
|
if (_context.mouseOver)
|
|
{
|
|
//Set the next timeout
|
|
setTimeout(function(){_context.mouseOverCallback(_context);},
|
|
Math.round(_context.timerInterval * 1000));
|
|
}
|
|
};
|
|
|
|
egw_fw_ui_scrollarea.prototype.mouseOverToggle = function(_over, _dir)
|
|
{
|
|
this.mouseOver = _over;
|
|
this.dir = _dir;
|
|
this.update();
|
|
if (_over)
|
|
{
|
|
var _context = this;
|
|
setTimeout(function(){_context.mouseOverCallback(_context);},
|
|
Math.round(_context.timerInterval * 1000));
|
|
}
|
|
else
|
|
{
|
|
this.scrollTime = 0.0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* egw_fw_ui_splitter class
|
|
*/
|
|
|
|
var EGW_SPLITTER_HORIZONTAL = 0;
|
|
var EGW_SPLITTER_VERTICAL = 1;
|
|
|
|
function egw_fw_ui_splitter(_contDiv, _orientation, _resizeCallback, _constraints, _tag)
|
|
{
|
|
//Copy the parameters
|
|
this.tag = _tag;
|
|
this.contDiv = _contDiv;
|
|
this.orientation = _orientation;
|
|
this.resizeCallback = _resizeCallback;
|
|
this.startPos = 0;
|
|
this.constraints =
|
|
[
|
|
{
|
|
"size": 0,
|
|
"minsize": 0,
|
|
"maxsize": 0
|
|
},
|
|
{
|
|
"size": 0,
|
|
"minsize": 0,
|
|
"maxsize": 0
|
|
}
|
|
];
|
|
|
|
//Copy the given constraints parameter, keeping the default values set above
|
|
if (_constraints.constructor == Array)
|
|
{
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
if (typeof _constraints[i] != 'undefined')
|
|
{
|
|
if (typeof _constraints[i].size != 'undefined')
|
|
this.constraints[i].size = _constraints[i].size;
|
|
if (typeof _constraints[i].minsize != 'undefined')
|
|
this.constraints[i].minsize = _constraints[i].minsize;
|
|
if (typeof _constraints[i].maxsize != 'undefined')
|
|
this.constraints[i].maxsize = _constraints[i].maxsize;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Create the actual splitter div
|
|
this.splitterDiv = document.createElement('div');
|
|
this.splitterDiv._parent = this;
|
|
$j(this.splitterDiv).addClass("egw_fw_ui_splitter");
|
|
|
|
//Setup the options for the dragable object
|
|
var dragoptions = {
|
|
opacity: 0.7,
|
|
helper: 'clone',
|
|
start: function(event, ui) {
|
|
return this._parent.dragStartHandler.call(this._parent, event, ui);
|
|
},
|
|
drag: function(event, ui) {
|
|
return this._parent.dragHandler.call(this._parent, event, ui);
|
|
},
|
|
stop: function(event, ui) {
|
|
return this._parent.dragStopHandler.call(this._parent, event, ui);
|
|
},
|
|
containment: 'document',
|
|
appendTo: 'body',
|
|
axis: 'y',
|
|
iframeFix: true,
|
|
zIndex: 10000
|
|
};
|
|
|
|
switch (this.orientation)
|
|
{
|
|
case EGW_SPLITTER_HORIZONTAL:
|
|
dragoptions.axis = 'y';
|
|
$j(this.splitterDiv).addClass("egw_fw_ui_splitter_horizontal");
|
|
break;
|
|
case EGW_SPLITTER_VERTICAL:
|
|
dragoptions.axis = 'x';
|
|
$j(this.splitterDiv).addClass("egw_fw_ui_splitter_vertical");
|
|
break;
|
|
}
|
|
$j(this.splitterDiv).draggable(dragoptions);
|
|
|
|
//Handle mouse hovering of the splitter div
|
|
$j(this.splitterDiv).mouseenter(function() {
|
|
$j(this).addClass("egw_fw_ui_splitter_hover");
|
|
});
|
|
$j(this.splitterDiv).mouseleave(function() {
|
|
$j(this).removeClass("egw_fw_ui_splitter_hover");
|
|
});
|
|
|
|
$j(this.contDiv).append(this.splitterDiv);
|
|
}
|
|
|
|
egw_fw_ui_splitter.prototype.clipDelta = function(_delta)
|
|
{
|
|
var result = _delta;
|
|
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
var mul = (i == 0) ? 1 : -1;
|
|
|
|
if (this.constraints[i].maxsize > 0)
|
|
{
|
|
var size = this.constraints[i].size + mul * result;
|
|
if (size > this.constraints[i].maxsize)
|
|
result += mul * (this.constraints[i].maxsize - size);
|
|
}
|
|
|
|
if (this.constraints[i].minsize > 0)
|
|
{
|
|
var size = this.constraints[i].size + mul * result;
|
|
if (size < this.constraints[i].minsize)
|
|
result += mul * (this.constraints[i].minsize - size);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
egw_fw_ui_splitter.prototype.dragStartHandler = function(event, ui)
|
|
{
|
|
switch (this.orientation)
|
|
{
|
|
case EGW_SPLITTER_HORIZONTAL:
|
|
this.startPos = ui.offset.top;
|
|
break;
|
|
case EGW_SPLITTER_VERTICAL:
|
|
this.startPos = ui.offset.left;
|
|
break;
|
|
}
|
|
};
|
|
|
|
egw_fw_ui_splitter.prototype.dragHandler = function(event, ui)
|
|
{
|
|
/* var delta = 0;
|
|
switch (this.orientation)
|
|
{
|
|
case EGW_SPLITTER_HORIZONTAL:
|
|
var old = ui.offset.top - this.startPos;
|
|
clipped = this.clipDelta(old);
|
|
$j(this.splitterDiv).data('draggable').offset.click.top += (old - clipped);
|
|
break;
|
|
case EGW_SPLITTER_VERTICAL:
|
|
var old = ui.offset.left - this.startPos;
|
|
clipped = this.clipDelta(old);
|
|
$j(this.splitterDiv).data('draggable').offset.click.left += (old - clipped);
|
|
break;
|
|
}*/
|
|
};
|
|
|
|
|
|
egw_fw_ui_splitter.prototype.dragStopHandler = function(event, ui)
|
|
{
|
|
var delta = 0;
|
|
switch (this.orientation)
|
|
{
|
|
case EGW_SPLITTER_HORIZONTAL:
|
|
delta = ui.offset.top - this.startPos;
|
|
break;
|
|
case EGW_SPLITTER_VERTICAL:
|
|
delta = ui.offset.left - this.startPos;
|
|
break;
|
|
}
|
|
|
|
//Clip the delta value
|
|
delta = this.clipDelta(delta);
|
|
|
|
this.constraints[0].size += delta;
|
|
this.constraints[1].size -= delta;
|
|
|
|
this.resizeCallback(this.constraints[0].size, this.constraints[1].size);
|
|
}; |