mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-23 00:13:35 +01:00
392 lines
11 KiB
JavaScript
392 lines
11 KiB
JavaScript
/**
|
|
* eGroupware Framework browser object
|
|
* @package framework
|
|
* @author Hadi Nategh <hn@stylite.de>
|
|
* @copyright Stylite AG 2014
|
|
* @description Framework browser object, is implementation of browser class in order to display application content
|
|
*/
|
|
|
|
/*egw:uses
|
|
vendor.bower-asset.jquery.dist.jquery;
|
|
egw_action.egw_action_common;
|
|
egw_inheritance.js;
|
|
*/
|
|
|
|
import '../egw_action/egw_action_common';
|
|
import '../jsapi/egw_inheritance.js';
|
|
import '../etemplate/etemplate2'; // otherwise et2_load json-response-handler is not (yet) available
|
|
|
|
/**
|
|
* Constants definition
|
|
*/
|
|
window.EGW_BROWSER_TYPE_NONE = 0;
|
|
window.EGW_BROWSER_TYPE_IFRAME = 1;
|
|
window.EGW_BROWSER_TYPE_DIV = 2;
|
|
|
|
window.fw_browser = (function(){ "use strict"; return Class.extend(
|
|
{
|
|
/**
|
|
* @param {string} _app
|
|
* @param {function} _heightCallback
|
|
* Framework browser class constructor
|
|
*/
|
|
init: function (_app, _heightCallback){
|
|
//Create a div which contains both, the legacy iframe and the contentDiv
|
|
this.baseDiv = document.createElement('div');
|
|
this.type = EGW_BROWSER_TYPE_NONE;
|
|
this.iframe = null;
|
|
this.contentDiv = null;
|
|
this.heightCallback = _heightCallback;
|
|
this.app = _app;
|
|
this.currentLocation = '';
|
|
this.ajaxLoaderDiv = null;
|
|
this.loadingDeferred = null;
|
|
},
|
|
|
|
/**
|
|
* Triggers resize event on window
|
|
*/
|
|
callResizeHandler: function()
|
|
{
|
|
var wnd = window;
|
|
if (this.iframe)
|
|
{
|
|
wnd = this.iframe.contentWindow;
|
|
}
|
|
|
|
// Call the resize handler (we have to use the jquery object of the iframe!)
|
|
try {
|
|
if (wnd && typeof wnd.jQuery != "undefined") {
|
|
wnd.jQuery(wnd).trigger("resize");
|
|
}
|
|
} catch(e) {} // ignore if iframe runs of a different origin
|
|
},
|
|
|
|
/**
|
|
* Resizes both, the contentDiv and the iframe to the size returned from the heightCallback
|
|
*/
|
|
resize: function()
|
|
{
|
|
var height = this.heightCallback.call(this.iframe) + 'px';
|
|
|
|
//Set the height of the content div or the iframe
|
|
if (this.contentDiv)
|
|
{
|
|
this.contentDiv.style.height = height;
|
|
}
|
|
if (this.iframe)
|
|
{
|
|
this.iframe.style.height = height;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets browser type either DIV or IFRAME
|
|
*
|
|
* @param {int} _type
|
|
*/
|
|
setBrowserType: function(_type)
|
|
{
|
|
//Only do anything if the browser type has changed
|
|
if (_type != this.type)
|
|
{
|
|
//Destroy the iframe and/or the contentDiv
|
|
jQuery(this.baseDiv).empty();
|
|
this.iframe = null;
|
|
this.contentDiv = null;
|
|
if(this.loadingDeferred && this.type)
|
|
{
|
|
this.loadingDeferred.reject();
|
|
}
|
|
|
|
switch (_type)
|
|
{
|
|
//Create the div for displaying the content
|
|
case EGW_BROWSER_TYPE_DIV:
|
|
this.contentDiv = document.createElement('div');
|
|
jQuery(this.contentDiv).addClass('egw_fw_content_browser_div');
|
|
jQuery(this.baseDiv).append(this.contentDiv);
|
|
|
|
break;
|
|
|
|
case EGW_BROWSER_TYPE_IFRAME:
|
|
//Create the iframe
|
|
this.iframe = document.createElement('iframe');
|
|
this.iframe.style.width = "100%";
|
|
this.iframe.style.borderWidth = 0;
|
|
this.iframe.frameBorder = 0;
|
|
this.iframe.name = 'egw_app_iframe_' + this.app.appName;
|
|
jQuery(this.iframe).addClass('egw_fw_content_browser_iframe');
|
|
jQuery(this.baseDiv).append(this.iframe);
|
|
|
|
break;
|
|
}
|
|
|
|
this.resize();
|
|
this.type = _type;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets url to browse and load the content in proper content browser
|
|
* @param {string} _url
|
|
* @return {Deferred} Returns a Deferred promise object
|
|
*/
|
|
browse: function(_url)
|
|
{
|
|
// check if app has its own linkHandler and it accepts the link (returns true), or returns different url instead
|
|
if (typeof app == 'object' && typeof app[this.app.appName] == 'object' &&
|
|
typeof app[this.app.appName].linkHandler == 'function')
|
|
{
|
|
var ret = app[this.app.appName].linkHandler.call(app[this.app.appName], _url);
|
|
{
|
|
if (ret === true) return this.loadingDeferred.promise();
|
|
if (typeof ret === 'string')
|
|
{
|
|
_url = ret;
|
|
}
|
|
}
|
|
}
|
|
var useIframe = true;
|
|
var targetUrl = _url;
|
|
if(_url == this.currentLocation && this.loadingDeferred != null)
|
|
{
|
|
// Still loading
|
|
return this.loadingDeferred.promise();
|
|
}
|
|
|
|
// Show loader div, start blocking
|
|
var self = this;
|
|
this.ajaxLoaderDiv = egw.loading_prompt(this.app.appName,true,egw.lang('please wait...'),this.baseDiv, egwIsMobile()?'horizental':'spinner');
|
|
this.loadingDeferred = new jQuery.Deferred();
|
|
|
|
// Try to escape from infinitive not resolved loadingDeferred
|
|
// At least user can close the broken tab and work with the others.
|
|
// Define a escape timeout for 5 sec
|
|
this.ajaxLoaderDivTimeout = setTimeout(function(){
|
|
(self.ajaxLoaderDiv || jQuery('div.loading')).hide().remove();
|
|
self.ajaxLoaderDiv = egw.loading_prompt(self.app.appName,false);
|
|
},5000);
|
|
|
|
this.loadingDeferred.always(function() {
|
|
framework.firstload_animation(self.app.appName,
|
|
framework.activeApp.appName == self.app.appName
|
|
&& !self.app.browser.contentDiv? 100 : null);
|
|
if(self.ajaxLoaderDiv)
|
|
{
|
|
|
|
self.ajaxLoaderDiv = egw.loading_prompt(self.app.appName,false);
|
|
// Remove escape timeout
|
|
clearTimeout(self.ajaxLoaderDivTimeout);
|
|
}
|
|
|
|
|
|
});
|
|
|
|
// Check whether the given url is a pseudo url which should be executed
|
|
// by calling the ajax_exec function
|
|
// we now send whole url back to server, so apps can use $_GET['ajax']==='true'
|
|
// to detect app-icon was clicked and eg. further reset filters
|
|
var matches = _url.match(/\/index.php\?menuaction=([A-Za-z0-9_\.]*.*&ajax=true.*)$/);
|
|
if (matches) {
|
|
// Matches[1] contains the menuaction which should be executed - replace
|
|
// the given url with the following line. This will be evaluated by the
|
|
// jdots_framework ajax_exec function which will be called by the code
|
|
// below as we set useIframe to false.
|
|
targetUrl = "index.php?menuaction=" + matches[1];
|
|
useIframe = false;
|
|
}
|
|
|
|
// Destroy application js
|
|
if(app[this.app.appName] && app[this.app.appName].destroy)
|
|
{
|
|
app[this.app.appName].destroy();
|
|
delete app[this.app.appName]; // really delete it, so new object get constructed and registered for push
|
|
}
|
|
|
|
// Unload etemplate2, if there
|
|
if(typeof etemplate2 == "function")
|
|
{
|
|
// Clear all etemplates on this tab, regardless of application, by using DOM nodes
|
|
jQuery('.et2_container',this.contentDiv||this.baseDiv).each(function() {
|
|
var et = etemplate2.getById(this.id);
|
|
if(et !== null)
|
|
{
|
|
et.clear();
|
|
}
|
|
});
|
|
}
|
|
else if(this.iframe && typeof this.iframe.contentWindow.etemplate2 == "function")
|
|
{
|
|
try
|
|
{
|
|
if(typeof this.iframe.contentWindow.etemplate2 == "function")
|
|
{
|
|
// Clear all etemplates on this tab, regardless of application, by using DOM nodes
|
|
var content = this.iframe.contentWindow;
|
|
jQuery('.et2_container',this.iframe.contentDocument).each(function() {
|
|
var et = content.etemplate2.getById(this.id);
|
|
if(et !== null)
|
|
{
|
|
et.clear();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
catch(e) {} // catch error if eg. SiteMgr runs a different origin, otherwise tab cant be closed
|
|
}
|
|
|
|
// Save the actual url which has been passed as parameter
|
|
this.currentLocation = _url;
|
|
|
|
//Set the browser type
|
|
if (useIframe)
|
|
{
|
|
this.setBrowserType(EGW_BROWSER_TYPE_IFRAME);
|
|
|
|
//Postpone the actual "navigation" - gives some speedup with internet explorer
|
|
//as it does no longer blocks the complete page until all frames have loaded.
|
|
window.setTimeout(function() {
|
|
//set iframe resource permissions
|
|
self.iframe.setAttribute('allow', 'fullscreen');
|
|
self.iframe.setAttribute('allowfullscreen', true); // for older browsers
|
|
|
|
// for own origin: bind load handler to set overflow-y: auto on body of contentDocument to allow vertical scrolling
|
|
if (_url[0] === '/' || top.location.origin === _url.replace(/^(https?:\/\/[^/]+)\/.*$/, '$1'))
|
|
{
|
|
self.iframe.addEventListener('load', (ev) => {
|
|
const body = self.iframe.contentDocument.getElementsByTagName('body')[0];
|
|
body.style.overflowY = 'auto';
|
|
});
|
|
}
|
|
|
|
//Load the iframe content
|
|
self.iframe.src = _url;
|
|
|
|
//Set the "_legacy_iframe" flag to allow link handlers to easily determine
|
|
//the type of the link source
|
|
if (self.iframe && self.iframe.contentWindow) {
|
|
try {
|
|
self.iframe.contentWindow._legacy_iframe = true;
|
|
|
|
// Focus the iframe of the current application
|
|
if (self.app == framework.activeApp)
|
|
{
|
|
self.iframe.contentWindow.focus();
|
|
}
|
|
}
|
|
catch (e) {
|
|
// ignore SecurityError: Blocked a frame ..., caused by different origin
|
|
}
|
|
}
|
|
|
|
if(self.loadingDeferred)
|
|
{
|
|
self.loadingDeferred.resolve();
|
|
self.loadingDeferred = null;
|
|
}
|
|
}, 1);
|
|
}
|
|
else
|
|
{
|
|
this.setBrowserType(EGW_BROWSER_TYPE_DIV);
|
|
|
|
//Special treatement of "about:blank"
|
|
if (targetUrl == "about:blank")
|
|
{
|
|
if (this.app.sidemenuEntry)
|
|
this.app.sidemenuEntry.hideAjaxLoader();
|
|
|
|
egw_widgetReplace(this.app.appName, this.contentDiv, '');
|
|
}
|
|
else
|
|
{
|
|
//Perform an AJAX request loading application output
|
|
if (this.app.sidemenuEntry)
|
|
this.app.sidemenuEntry.showAjaxLoader();
|
|
this.data = "";
|
|
jQuery(this.contentDiv).empty();
|
|
var self_egw = egw(this.app.appName);
|
|
var req = self_egw.json(
|
|
this.app.getMenuaction('ajax_exec', targetUrl),
|
|
[targetUrl], this.browse_callback,this, true, this
|
|
);
|
|
req.sendRequest();
|
|
}
|
|
}
|
|
return this.loadingDeferred.promise();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {type} _data
|
|
* @return {undefined} return undefined if data is not from the right response
|
|
*/
|
|
browse_callback: function(_data)
|
|
{
|
|
// Abort if data is from wrong kind of response - only 'data'
|
|
if(!_data || _data.type != undefined) return;
|
|
|
|
this.data = _data[0];
|
|
this.browse_finished();
|
|
},
|
|
|
|
/**
|
|
* Get call via browse_callback in order to attaching nodes to the DOM
|
|
*/
|
|
browse_finished: function()
|
|
{
|
|
if (this.app.sidemenuEntry)
|
|
this.app.sidemenuEntry.hideAjaxLoader();
|
|
// egw_widgetReplace(this.app.appName, this.contentDiv, this.data);
|
|
var content = {
|
|
html: this.data,
|
|
js: ''
|
|
};
|
|
|
|
if (this.app == framework.activeApp)
|
|
{
|
|
window.focus();
|
|
}
|
|
|
|
egw_seperateJavaScript(content);
|
|
|
|
// Insert the content
|
|
jQuery(this.contentDiv).append(content.html);
|
|
|
|
// Run the javascript code
|
|
//console.log(content.js);
|
|
jQuery(this.contentDiv).append(content.js);
|
|
|
|
if(this.loadingDeferred)
|
|
{
|
|
this.loadingDeferred.resolve();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* REload the content of the browser object
|
|
*/
|
|
reload: function()
|
|
{
|
|
switch (this.type)
|
|
{
|
|
case EGW_BROWSER_TYPE_DIV:
|
|
this.browse(this.currentLocation);
|
|
break;
|
|
|
|
case EGW_BROWSER_TYPE_IFRAME:
|
|
//Do a simple reload in the iframe case
|
|
this.iframe.contentWindow.location.reload();
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
*
|
|
*/
|
|
blank: function()
|
|
{
|
|
this.browse('about:blank', this.type == EGW_BROWSER_TYPE_IFRAME);
|
|
}
|
|
});}).call(window); |