/** * eGroupware JavaScript Framework * * @link http://www.egroupware.org * @author Andreas Stoeckel * @version $Id$ */ /** * Some constant definitions */ EGW_LINK_SOURCE_FRAMEWORK = 0; EGW_LINK_SOURCE_LEGACY_IFRAME = 1; EGW_LINK_SOURCE_POPUP = 2; /** * Class: egw_fw * The egw_fw class is the base framework class. It wraps around all egw_fw_ui and * egw_fw_classes. It creates both, a side bar and a tab area and cares about linking. */ /** * The constructor of the egw_fw class. * * @param string _sidemenuId specifies the name of the div container which should contain the sidebar menu * @param string _tabsId specifies the name of the div container which should cotain the tab area * @param string _webserverUrl specifies the egroupware root url */ function egw_fw(_sidemenuId, _tabsId, _splitterId, _webserverUrl, _sideboxSizeCallback, _sideboxStartSize) { /* Get the base div */ this.sidemenuDiv = document.getElementById(_sidemenuId); this.tabsDiv = document.getElementById(_tabsId); this.splitterDiv = document.getElementById(_splitterId); this.webserverUrl = _webserverUrl; this.sideboxSizeCallback = _sideboxSizeCallback; window.egw_webserverUrl = _webserverUrl; this.sidemenuUi = null; this.tabsUi = null; this.categoryOpenCache = new Object(); this.applications = new Object(); this.activeApp = null; if (this.sidemenuDiv && this.tabsDiv && this.splitterDiv) { //Wrap a scroll area handler around the applications this.scrollAreaUi = new egw_fw_ui_scrollarea(this.sidemenuDiv); //Create the sidemenu, the tabs area and the splitter this.sidemenuUi = new egw_fw_ui_sidemenu(this.scrollAreaUi.contentDiv, this.sortCallback); this.tabsUi = new egw_fw_ui_tabs(this.tabsDiv); this.splitterUi = new egw_fw_ui_splitter(this.splitterDiv, EGW_SPLITTER_VERTICAL, this.splitterResize, [ { "size": _sideboxStartSize, "minsize": 225, "maxsize": 0 }, ], this); this.loadApplications("home.jdots_framework.ajax_navbar_apps"); } _sideboxSizeCallback(_sideboxStartSize); //Register the resize handler $(window).resize(function(){window.framework.resizeHandler()}); //Register the global alert handler window.egw_alertHandler = this.alertHandler; //Register the key press handler //$(document).keypress(this.keyPressHandler); //Override the old egw_openWindowCentered2 window.egw_openWindowCentered2 = this.egw_openWindowCentered2; //Override the app_window function window.egw_appWindow = this.egw_appWindow; } egw_fw.prototype.alertHandler = function(_message, _details) { alert('Error:\n ' + _message + '\n\nDetails:\n ' + _details); } /** * Function called whenever F1 is pressed inside the framework * @returns boolean true if the call manual function could be called, false if the manual is not available */ egw_fw.prototype.f1Handler = function() { if (typeof window.callManual != 'undefined') { window.callManual(); return true; } return false; } /** * Function called whenever a key is pressed * @param object event describes the key press event */ egw_fw.prototype.keyPressHandler = function(event) { switch (event.keyCode) { case 112: //F1 { event.preventDefault(); framework.f1Handler(); } } } /** * Sets the active framework application to the application specified by _app */ egw_fw.prototype.setActiveApp = function(_app) { //Only perform the following commands if a new application is activated if (_app != this.activeApp) { this.activeApp = _app; //Set the sidebox width if a application specific sidebox width is set if (_app.sideboxWidth !== false) { this.sideboxSizeCallback(_app.sideboxWidth); this.splitterUi.constraints[0].size = _app.sideboxWidth; } //Open the sidemenuUi that belongs to the app, if no sidemenu is attached //to the app, close the sidemenuUi if (_app.sidemenuEntry) { if (_app.hasSideboxMenuContent) { this.sidemenuUi.open(_app.sidemenuEntry); } } else { this.sidemenuUi.open(null); } //Set the website title if (_app.website_title) { document.title = _app.website_title; } //Show the application tab if (_app.tab) { this.tabsUi.showTab(_app.tab); } //Resize the scroll area... this.scrollAreaUi.update(); //...and scroll to the top this.scrollAreaUi.setScrollPos(0); } } /** * Function called whenever the sidemenu entries are sorted */ egw_fw.prototype.sortCallback = function(_entriesArray) { //Create an array with the names of the applications in their sort order var name_array = new Array(); for (var i = 0; i < _entriesArray.length; i++) { name_array.push(_entriesArray[i].tag.appName); } //Send the sort order to the server via ajax var req = new egw_json_request('home.jdots_framework.ajax_appsort', [name_array]); req.sendRequest(true); } /** * Function called whenever the sidebox is resized */ egw_fw.prototype.splitterResize = function(_width) { if (this.tag.activeApp) { app_name = this.tag.activeApp.appName; var req = new egw_json_request(app_name + '.jdots_framework.ajax_sideboxwidth', [app_name, _width]); req.sendRequest(true); //If there are no global application width values, set the sidebox width of //the application every time the splitter is resized if (this.tag.activeApp.sideboxWidth !== false) { this.tag.activeApp.sideboxWidth = _width; } } this.tag.sideboxSizeCallback(_width); } /** * tabCloseClickCallback is used internally by egw_fw in order to handle clicks * on the close button of every tab. * * @param egw_fw_ui_tab _sender specifies the tab ui object, the user has clicked */ egw_fw.prototype.tabCloseClickCallback = function(_sender) { //Save references to the application and the tabsUi as "this" will be deleted var app = this.tag; var tabsUi = this.parent; //At least one tab must stay open if (tabsUi.tabs.length > 1) { tabsUi.removeTab(this); app.tab = null; app.browser = null; if (app.sidemenuEntry) app.sidemenuEntry.hideAjaxLoader(); //Set the active application to the application of the currently active tab app.parentFw.setActiveApp(tabsUi.activeTab.tag); } tabsUi.setCloseable(tabsUi.tabs.length > 1); //As a new tab might remove a row from the tab header, we have to resize all tab content browsers this.tag.parentFw.resizeHandler(); } egw_fw.prototype.resizeHandler = function() { //Resize the browser area of the applications for (var app in this.applications) { if (this.applications[app].browser != null) { this.applications[app].browser.resize(); } } //Update the scroll area this.scrollAreaUi.update(); } egw_fw.prototype.getIFrameHeight = function() { var height = $(window).height() - (this.tabsUi.contHeaderDiv.offsetTop + this.tabsUi.contHeaderDiv.offsetHeight + 30); /* 30 is the height of the footer */ return height; } /** * tabClickCallback is used internally by egw_fw in order to handle clicks on * a tab. * * @param egw_fw_ui_tab _sender specifies the tab ui object, the user has clicked */ egw_fw.prototype.tabClickCallback = function(_sender) { //Set the active application in the framework this.tag.parentFw.setActiveApp(this.tag); } /** * applicationClickCallback is used internally by egw_fw in order to handle clicks on * an application in the sidebox menu. * * @param egw_fw_ui_tab _sender specifies the tab ui object, the user has clicked */ egw_fw.prototype.applicationClickCallback = function(_sender) { this.tag.parentFw.applicationTabNavigate(this.tag, this.tag.indexUrl); } /** * Checks whether the application already owns a tab and creates one if it doesn't exist */ egw_fw.prototype.createApplicationTab = function(_app) { if (_app.tab == null) { //Create the tab _app.tab = this.tabsUi.addTab(_app.icon, this.tabClickCallback, this.tabCloseClickCallback, _app); _app.tab.setTitle(_app.displayName); //Set the tab closeable if there's more than one tab this.tabsUi.setCloseable(this.tabsUi.tabs.length > 1); } } /** * Navigate to the tab of an application (opening the tab if not yet open) * * @param egw_fw_class_application _app * @param string _url optional url, default index page of app */ egw_fw.prototype.applicationTabNavigate = function(_app, _url, _useIframe) { //Create the tab for that application this.createApplicationTab(_app); if (typeof _url == 'undefined') _url = _app.indexUrl; if (typeof _useIframe == 'undefined') { if (!_url.match(/menuaction=/)) { _useIframe = true; } else { _useIframe = _app.legacyApp; } } if (_app.browser == null) { //Create a new browser ui and set it as application tab callback var callback = new egw_fw_class_callback(this, this.getIFrameHeight); _app.browser = new egw_fw_content_browser(_app, callback); _app.tab.setContent(_app.browser.baseDiv); } _app.browser.browse(_url, true);//_useIframe); this.setActiveApp(_app); } /** * Tries to obtain the application from a menuaction */ egw_fw.prototype.parseAppFromUrl = function(_url) { var _app = null; //Read the menuaction parts from the url and check whether the first part //of the url contains a valid app name var matches = _url.match(/menuaction=([a-z0-9_-]+)\./i); if (matches && (_app = this.getApplicationByName(matches[1]))) { return _app; } //Check the url for a scheme of "/app/something.php" and check this one for a valid app //name var matches = _url.match(/\/([^\/]+)\/[^\/]+\.php/i); if (matches && (_app = this.getApplicationByName(matches[1]))) { return _app; } return null; } /** * loadApplicationsCallback is internally used by egw_fw in order to handle the * receiving of the application list. * * @param object apps contains the parsed JSON data describing the applications. * The JSON object should have the following structure * apps = array[ * { * string name (the internal name of the application) * string title (the name of the application how it should be viewed) * string icon (path to the icon of the application) * string url (path to the application) //TODO: Change this * [boolean isDefault] (whether this entry is the default entry which should be opened) * } * ] */ egw_fw.prototype.loadApplicationsCallback = function(apps) { var defaultApp = null; //Iterate through the application array returned for (var i = 0; i < apps.length; i++) { var app = apps[i]; //Check for the "legacyApp" flag - if it is not set, default it to true var legacyApp = true; if (typeof app.legacyApp != 'undefined') legacyApp = app.legacyApp; appData = new egw_fw_class_application(this, app.name, app.title, app.icon, app.url, app.sideboxwidth, legacyApp); //Create a sidebox menu entry for each application if (!app.noNavbar) { appData.sidemenuEntry = this.sidemenuUi.addEntry( appData.displayName, appData.icon, this.applicationClickCallback, appData); } //If this entry is the default entry, show it using the click callback if (app.isDefault && (app.isDefault === true)) { defaultApp = appData; } this.applications[appData.appName] = appData; } // check if a menuaction or app is specified in the url --> display that var _app = this.parseAppFromUrl(window.location.href); if (_app) { _url = window.location.href.replace(/&?cd=yes/,''); this.applicationTabNavigate(_app, _url); } // else display the default application else if (defaultApp) { this.applicationTabNavigate(defaultApp); } this.scrollAreaUi.update(); } /** * loadApplications refreshes the list of applications. Upon calling, all existing applications * will be deleted from the list, and all open tabs will be closed. Then an AJAX request to the * given URL will be send in order to obtain the application list with JSON encoding. * * @param string _menuaction specifies the menuaction */ egw_fw.prototype.loadApplications = function(_menuaction) { //Close all open tabs, remove all applications from the application list this.sidemenuUi.clean(); this.tabsUi.clean(); //Perform an AJAX request loading all available applications var req = new egw_json_request(_menuaction) req.sendRequest(true, this.loadApplicationsCallback, this); } /** * Goes through all applications and returns the application with the specified name. * @param string _name the name of the application which should be returned. * @return object or null if application is not found. */ egw_fw.prototype.getApplicationByName = function(_name) { if (typeof this.applications[_name] != 'undefined') { return this.applications[_name]; } return null; } /** * Seperates all script tags from the given html code and returns the seperately * @param object _html object that the html code from which the script should be seperated. The html code has to be stored in _html.html, the result js will be written to _html.js */ egw_fw.prototype.seperateJavaScript = function(_html) { var html = _html.html; var in_pos = html.search(/ tag */ var js_str = html.substring(in_pos, out_pos+9); /*Remove the initial tag */ /*js_str = js_str.substring(js_str.search(/>/) + 1);*/ _html.js += js_str; html = html.substring(0, in_pos - 1) + html.substring(out_pos + 9); var in_pos = html.search(/