mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 00:29:38 +01:00
45758aaada
Example was tracker site config opened in an iframe, then stopped further et2_load responses to the main window because it still used window object from the iframe and therefore could not find the node
832 lines
26 KiB
JavaScript
832 lines
26 KiB
JavaScript
/**
|
|
* EGroupware clientside API object
|
|
*
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package etemplate
|
|
* @subpackage api
|
|
* @link https://www.egroupware.org
|
|
* @author Andreas Stöckel (as AT stylite.de)
|
|
* @author Ralf Becker <RalfBecker@outdoor-training.de>
|
|
*/
|
|
|
|
/*egw:uses
|
|
vendor.bower-asset.jquery.dist.jquery;
|
|
|
|
egw_core;
|
|
egw_utils;
|
|
egw_files;
|
|
egw_debug;
|
|
*/
|
|
import './egw_core.js';
|
|
import './egw_utils.js';
|
|
|
|
/**
|
|
* Module sending json requests
|
|
*
|
|
* @param {string} _app application name object is instantiated for
|
|
* @param {object} _wnd window object is instantiated for
|
|
*/
|
|
egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
|
{
|
|
"use strict";
|
|
|
|
/**
|
|
* Object which contains all registered handlers for JS responses.
|
|
* The handlers are organized per response type in the top level of the
|
|
* object, where each response type can have an array of handlers attached
|
|
* to it.
|
|
*/
|
|
var plugins = {};
|
|
|
|
/**
|
|
* Global json handlers are from global modules, not window level
|
|
*/
|
|
if(typeof egw._global_json_handlers == 'undefined')
|
|
{
|
|
egw._global_json_handlers = {};
|
|
}
|
|
var global_plugins = egw._global_json_handlers;
|
|
|
|
/**
|
|
* Internal implementation of the JSON request object.
|
|
*
|
|
* @param {string} _menuaction
|
|
* @param {array} _parameters
|
|
* @param {function} _callback
|
|
* @param {object} _context
|
|
* @param {boolean|"keepalive"} _async true: asynchronious request, false: synchronious request,
|
|
* "keepalive": async. request with keepalive===true / sendBeacon, to be used in boforeunload event
|
|
* @param {object} _sender
|
|
* @param {egw} _egw
|
|
*/
|
|
const json_request = function(_menuaction, _parameters, _callback, _context,
|
|
_async, _sender, _egw)
|
|
{
|
|
// Copy the parameters
|
|
this.url = _egw.ajaxUrl(_menuaction);
|
|
// IE JSON-serializes arrays passed in from different window contextx (eg. popups)
|
|
// as objects (it looses object-type of array), causing them to be JSON serialized
|
|
// as objects and loosing parameters which are undefined
|
|
// JSON.strigify([123,undefined]) --> '{"0":123}' instead of '[123,null]'
|
|
this.parameters = _parameters ? [].concat(_parameters) : [];
|
|
this.async = typeof _async != 'undefined' ? _async : true;
|
|
this.callback = _callback ? _callback : null;
|
|
this.context = _context ? _context : null;
|
|
this.sender = _sender ? _sender : null;
|
|
this.egw = _egw;
|
|
|
|
// We currently don't have a request object
|
|
this.request = null;
|
|
|
|
// Some variables needed for notification about a JS files done loading
|
|
this.onLoadFinish = null;
|
|
this.jsFiles = 0;
|
|
this.jsCount = 0;
|
|
|
|
// Function which is currently used to display alerts -- may be replaced by
|
|
// some API function.
|
|
this.alertHandler = function(_message, _details) {
|
|
// we need to use the alert function of the window of the request, not just the main window
|
|
(this.egw ? this.egw.window : window).alert(_message);
|
|
|
|
if (_details)
|
|
{
|
|
_egw.debug('info', _message, _details);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Open websocket to push server (and keeps it open)
|
|
*
|
|
* @param {string} url this.websocket(s)://host:port
|
|
* @param {array} tokens tokens to subscribe too: sesssion-, user- and instance-token (in that order!)
|
|
* @param {number} account_id to connect for
|
|
* @param {function} error option error callback(_msg) used instead our default this.error
|
|
* @param {int} reconnect timeout in ms (internal)
|
|
*/
|
|
json_request.prototype.openWebSocket = function(url, tokens, account_id, error, reconnect)
|
|
{
|
|
const min_reconnect_time = 1000;
|
|
const max_reconnect_time = 300000;
|
|
const check_interval = 30000; // 30 sec
|
|
const max_ping_response_time = 1000;
|
|
let reconnect_time = reconnect || min_reconnect_time;
|
|
let check_timer;
|
|
let check = function()
|
|
{
|
|
this.websocket.send('ping');
|
|
check_timer = window.setTimeout(function()
|
|
{
|
|
console.log("Server did not respond to ping in "+max_ping_response_time+" seconds --> try reconnecting");
|
|
check_timer = null;
|
|
this.websocket.onclose = function()
|
|
{
|
|
this.websocket = null;
|
|
this.openWebSocket(url, tokens, account_id, error, reconnect_time);
|
|
}.bind(this);
|
|
this.websocket.close(); // closing it now, before reopening it, to not end up with multiple connections
|
|
}.bind(this), max_ping_response_time);
|
|
}.bind(this);
|
|
|
|
this.websocket = new WebSocket(url);
|
|
this.websocket.onopen = jQuery.proxy(function(e)
|
|
{
|
|
check_timer = window.setTimeout(check, check_interval);
|
|
this.websocket.send(JSON.stringify({
|
|
subscribe: tokens,
|
|
account_id: parseInt(account_id)
|
|
}));
|
|
}, this);
|
|
|
|
this.websocket.onmessage = jQuery.proxy(function(event)
|
|
{
|
|
reconnect_time = min_reconnect_time;
|
|
console.log(event);
|
|
if (check_timer) window.clearTimeout(check_timer);
|
|
check_timer = window.setTimeout(check, check_interval);
|
|
if (event.data === 'pong') return; // just a keepalive message
|
|
let data = JSON.parse(event.data);
|
|
if (data && data.type)
|
|
{
|
|
this.handleResponse({ response: [data]});
|
|
}
|
|
}, this);
|
|
|
|
this.websocket.onerror = jQuery.proxy(function(error)
|
|
{
|
|
reconnect_time *= 2;
|
|
if (reconnect_time > max_reconnect_time) reconnect_time = max_reconnect_time;
|
|
|
|
console.log(error);
|
|
(error||this.handleError({}, error));
|
|
}, this);
|
|
|
|
this.websocket.onclose = jQuery.proxy(function(event)
|
|
{
|
|
if (event.wasClean)
|
|
{
|
|
reconnect_time = min_reconnect_time;
|
|
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
|
|
}
|
|
else
|
|
{
|
|
reconnect_time *= 2;
|
|
if (reconnect_time > max_reconnect_time) reconnect_time = max_reconnect_time;
|
|
|
|
// e.g. server process killed or network down
|
|
// event.code is usually 1006 in this case
|
|
console.log('[close] Connection died --> reconnect in '+reconnect_time+'ms');
|
|
if (check_timer) window.clearTimeout(check_timer);
|
|
check_timer = null;
|
|
window.setTimeout(jQuery.proxy(this.openWebSocket, this, url, tokens, account_id, error, reconnect_time), reconnect_time);
|
|
}
|
|
}, this);
|
|
},
|
|
|
|
/**
|
|
* Sends the assembled request to the server
|
|
* @param {boolean|"keepalive"} _async Overrides async provided in constructor: true: asynchronious request,
|
|
* false: synchronious request, "keepalive": async. request with keepalive===true / sendBeacon, to be used in beforeunload event
|
|
* @param {string} method ='POST' allow to eg. use a (cachable) 'GET' request instead of POST
|
|
* @param {function} error option error callback(_xmlhttp, _err) used instead our default this.error
|
|
*
|
|
* @return {jqXHR|boolean} jQuery jqXHR request object or for async==="keepalive" boolean is returned
|
|
*/
|
|
json_request.prototype.sendRequest = function(async, method, error)
|
|
{
|
|
if(typeof async != "undefined")
|
|
{
|
|
this.async = async;
|
|
}
|
|
|
|
if (typeof method === 'undefined') method = 'POST';
|
|
|
|
// Assemble the complete request
|
|
var request_obj = JSON.stringify({
|
|
request: {
|
|
parameters: this.parameters
|
|
}
|
|
});
|
|
|
|
// send with keepalive===true or sendBeacon to be used in beforeunload event
|
|
if (this.async === "keepalive" && typeof navigator.sendBeacon !== "undefined")
|
|
{
|
|
const data = new FormData();
|
|
data.append('json_data', request_obj);
|
|
//(window.opener||window).console.log("navigator.sendBeacon", this.url, request_obj, data.getAll('json_data'));
|
|
return navigator.sendBeacon(this.url, data);
|
|
}
|
|
|
|
// Send the request via AJAX using the jquery ajax function
|
|
// we need to use jQuery of window of egw object, as otherwise the one from main window is used!
|
|
// (causing eg. apply from server with app.$app.method to run in main window instead of popup)
|
|
this.request = (this.egw.window?this.egw.window.jQuery:jQuery).ajax({
|
|
url: this.url,
|
|
async: this.async,
|
|
context: this,
|
|
// only POST can send JSON as direct payload, GET can not
|
|
data: method === 'GET' ? { json_data: request_obj } : request_obj,
|
|
contentType: method === 'GET' ? false : 'application/json',
|
|
dataType: 'json',
|
|
type: method,
|
|
success: this.handleResponse,
|
|
jsonp: false,
|
|
error: error || this.handleError
|
|
});
|
|
|
|
return this.request;
|
|
};
|
|
|
|
/**
|
|
* Default error callback displaying error via egw.message
|
|
*
|
|
* @param {XMLHTTP} _xmlhttp
|
|
* @param {string} _err
|
|
*/
|
|
json_request.prototype.handleError = function(_xmlhttp, _err) {
|
|
// Don't error about an abort
|
|
if(_err !== 'abort')
|
|
{
|
|
this.egw.message.call(this.egw,
|
|
this.egw.lang('A request to the EGroupware server returned with an error')+
|
|
': '+_xmlhttp.statusText+' ('+_xmlhttp.status+")\n\n"+
|
|
this.egw.lang('Please reload the EGroupware desktop (F5 / Cmd+r).')+"\n"+
|
|
this.egw.lang('If the error persists, contact your administrator for help and ask to check the error-log of the webserver.')+
|
|
"\n\nURL: "+this.url+"\n"+
|
|
(_xmlhttp.getAllResponseHeaders() ? (_xmlhttp.getAllResponseHeaders().match(/^Date:.*$/mi) ? _xmlhttp.getAllResponseHeaders().match(/^Date:.*$/mi)[0]:''):'')+
|
|
// if EGroupware send JSON payload with error, errno show it here too
|
|
(_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' && _xmlhttp.responseJSON.error ?
|
|
"\nError: "+_xmlhttp.responseJSON.error+' ('+_xmlhttp.responseJSON.errno+')' : '')
|
|
);
|
|
|
|
this.egw.debug('error', 'Ajax request to', this.url, ' failed: ', _err, _xmlhttp.status, _xmlhttp.statusText, _xmlhttp.responseJSON);
|
|
|
|
// check of unparsable JSON on server-side, which might be caused by some network problem --> resend max. twice
|
|
if (_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' &&
|
|
_xmlhttp.responseJSON.errno && _xmlhttp.responseJSON.error.substr(0, 5) === 'JSON ')
|
|
{
|
|
// ToDo: resend request max. twice
|
|
}
|
|
}
|
|
};
|
|
|
|
json_request.prototype.handleResponse = function(data) {
|
|
if (data && typeof data.response != 'undefined')
|
|
{
|
|
if (egw.preference('show_generation_time', 'common', false) == "1")
|
|
{
|
|
var gen_time_div = jQuery('#divGenTime').length > 0 ? jQuery('#divGenTime')
|
|
:jQuery('<div id="divGenTime" class="pageGenTime"><span class="pageTime"></span></div>').appendTo('#egw_fw_footer');
|
|
}
|
|
// Load files first
|
|
var js_files = [];
|
|
for (var i = data.response.length - 1; i >= 0; --i)
|
|
{
|
|
var res = data.response[i];
|
|
if(res.type == 'js' && typeof res.data == 'string')
|
|
{
|
|
js_files.unshift(res.data);
|
|
data.response.splice(i,1);
|
|
}
|
|
}
|
|
if(js_files.length > 0)
|
|
{
|
|
var start_time = (new Date).getTime();
|
|
this.egw.includeJS(js_files, function() {
|
|
var end_time = (new Date).getTime();
|
|
this.handleResponse(data);
|
|
if (egw.preference('show_generation_time', 'common', false) == "1")
|
|
{
|
|
var gen_time_div = jQuery('#divGenTime');
|
|
if (!gen_time_div.length) gen_time_div = jQuery('.pageGenTime');
|
|
var gen_time_async = jQuery('.asyncIncludeTime').length > 0 ? jQuery('.asyncIncludeTime'):
|
|
gen_time_div.append('<span class="asyncIncludeTime"></span>').find('.asyncIncludeTime');
|
|
gen_time_async.text(egw.lang('async includes took %1s', (end_time-start_time)/1000));
|
|
}
|
|
}, this);
|
|
return;
|
|
}
|
|
|
|
// defer apply's for app.* after et2_load is finished
|
|
let apply_app = [];
|
|
if (data.response.filter((res) => res.type === 'et2_load').length)
|
|
{
|
|
apply_app = data.response.filter((res) => res.type === 'apply' && res.data.func.substr(0, 4) === 'app.');
|
|
if (apply_app.length)
|
|
{
|
|
data.response = data.response.filter((res) => !(res.type === 'apply' && res.data.func.substr(0, 4) === 'app.'));
|
|
}
|
|
}
|
|
|
|
// Flag for only data response - don't call callback if only data
|
|
var only_data = (data.response.length > 0);
|
|
|
|
for (var i = 0; i < data.response.length; i++)
|
|
{
|
|
// Get the response object
|
|
var res = data.response[i];
|
|
if(typeof res.type == 'string' && res.type != 'data') only_data = false;
|
|
|
|
// Check whether a plugin for the given type exists
|
|
var handlers = [plugins, global_plugins];
|
|
for(var handler_idx = 0; handler_idx < handlers.length; handler_idx++)
|
|
{
|
|
var handler_level = handlers[handler_idx];
|
|
if (typeof handler_level[res.type] !== 'undefined')
|
|
{
|
|
for (var j = 0; j < handler_level[res.type].length; j++) {
|
|
try {
|
|
// Get a reference to the plugin
|
|
var plugin = handler_level[res.type][j];
|
|
if (res.type === 'et2_load')
|
|
{
|
|
if (egw.preference('show_generation_time', 'common', false) == "1")
|
|
{
|
|
if (gen_time_div.length > 0)
|
|
{
|
|
gen_time_div.find('span.pageTime').text(egw.lang("Page was generated in %1 seconds ", data.page_generation_time));
|
|
if (data.session_restore_time)
|
|
{
|
|
var gen_time_session_span = gen_time_div.find('span.session').length > 0 ? gen_time_div.find('span.session'):
|
|
gen_time_div.append('<span class="session"></span>').find('.session');
|
|
gen_time_session_span.text(egw.lang("session restore time in %1 seconds ", data.page_generation_time));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Call the plugin callback
|
|
const promise = plugin.callback.call(
|
|
plugin.context ? plugin.context : this.context,
|
|
res.type, res, this
|
|
);
|
|
// defer apply_app's after et2_load is finished (it returns a promise for that)
|
|
if (res.type === 'et2_load' && apply_app.length && typeof promise.then === 'function')
|
|
{
|
|
promise.then(() => this.handleResponse({response: apply_app}));
|
|
}
|
|
} catch(e) {
|
|
var msg = e.message ? e.message : e + '';
|
|
var stack = e.stack ? "\n-- Stack trace --\n" + e.stack : "";
|
|
this.egw.debug('error', 'Exception "' + msg + '" while handling JSON response from ' +
|
|
this.url + ' [' + JSON.stringify(this.parameters) + '] type "' + res.type +
|
|
'", plugin', plugin, 'response', res, stack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Call request callback, if provided
|
|
if(this.callback != null && !only_data)
|
|
{
|
|
this.callback.call(this.context,res);
|
|
}
|
|
}
|
|
this.request = null;
|
|
};
|
|
|
|
var json = {
|
|
|
|
/** The constructor of the egw_json_request class.
|
|
*
|
|
* @param _menuaction the menuaction function which should be called and
|
|
* which handles the actual request. If the menuaction is a full featured
|
|
* url, this one will be used instead.
|
|
* @param _parameters which should be passed to the menuaction function.
|
|
* @param _async specifies whether the request should be asynchronous or
|
|
* not.
|
|
* @param _callback specifies the callback function which should be
|
|
* called, once the request has been sucessfully executed.
|
|
* @param _context is the context which will be used for the callback function
|
|
* @param _sender is a parameter being passed to the _callback function
|
|
*/
|
|
json: function(_menuaction, _parameters, _callback, _context, _async,
|
|
_sender)
|
|
{
|
|
return new json_request(_menuaction, _parameters, _callback,
|
|
_context, _async, _sender, this);
|
|
},
|
|
|
|
/**
|
|
* Do an AJAX call and get a javascript promise, which will be resolved with the returned data.
|
|
*
|
|
* egw.request() returns immediately with a Promise. The promise will be resolved with just the returned data,
|
|
* any other "piggybacked" responses will be handled by registered handlers. The data will also be passed to
|
|
* any registered data handlers (egw.data) before it is passed to your handler.
|
|
*
|
|
* To use:
|
|
* @example
|
|
* egw.request(
|
|
* "EGroupware\\Api\\Etemplate\\Widget\\Select::ajax_get_options",
|
|
* ["select-cat"]
|
|
* )
|
|
* .then(function(data) {
|
|
* // Deal with the returned data here. data may be undefined if no data was returned.
|
|
* console.log("Here's the categories:",data);
|
|
* });
|
|
*
|
|
*
|
|
* The return is a Promise, so multiple .then() can be chained in the usual ways:
|
|
* @example
|
|
* egw.request(...)
|
|
* .then(function(data) {
|
|
* if(debug) console.log("Requested data", data);
|
|
* }
|
|
* .then(function(data) {
|
|
* // Change the data for the rest of the chain
|
|
* if(typeof data === "undefined") return [];
|
|
* }
|
|
* .then(function(data) {
|
|
* // data is never undefined now, if it was before it's an empty array now
|
|
* for(let i = 0; i < data.length; i++)
|
|
* {
|
|
* ...
|
|
* }
|
|
* }
|
|
*
|
|
*
|
|
* You can also fire off multiple requests, and wait for them to all be answered:
|
|
* @example
|
|
* let first = egw.request(...);
|
|
* let second = egw.request(...);
|
|
* Promise.all([first, second])
|
|
* .then(function(values) {
|
|
* console.log("First:", values[0], "Second:", values[1]);
|
|
* }
|
|
*
|
|
*
|
|
* @param {string} _menuaction
|
|
* @param {any[]} _parameters
|
|
*
|
|
* @return Promise
|
|
*/
|
|
request: function(_menuaction, _parameters)
|
|
{
|
|
let request = new json_request(_menuaction, _parameters, null, this, true, this, this);
|
|
let ajax_promise = request.sendRequest();
|
|
|
|
// This happens first, immediately
|
|
let resolvePromise = function(resolve, reject) {
|
|
// Bind to ajax response - this is called _after_ any other handling
|
|
ajax_promise.always(function(response, status, p) {
|
|
if(status !== "success") reject();
|
|
|
|
// The ajax request has completed, get just the data & pass it on
|
|
if(response && response.response)
|
|
{
|
|
for(let value of response.response)
|
|
{
|
|
if(value.type && value.type === "data" && typeof value.data !== "undefined")
|
|
{
|
|
// Data was packed in response
|
|
resolve(value.data);
|
|
}
|
|
else if (value && typeof value.type === "undefined" && typeof value.data === "undefined")
|
|
{
|
|
// Just raw data
|
|
resolve(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// No data? Resolve the promise with nothing
|
|
resolve();
|
|
});
|
|
};
|
|
|
|
const myPromise = new Promise(resolvePromise);
|
|
|
|
return myPromise;
|
|
},
|
|
|
|
/**
|
|
* Call a function specified by it's name (possibly dot separated, eg. "app.myapp.myfunc")
|
|
*
|
|
* @param {string|Function} _func dot-separated function name or function
|
|
* @param {mixed} ...args variable number of arguments
|
|
* @returns {mixed|Promise}
|
|
*/
|
|
callFunc: function(_func)
|
|
{
|
|
return this.applyFunc(_func, [].slice.call(arguments, 1));
|
|
},
|
|
|
|
/**
|
|
* Call a function specified by it's name (possibly dot separated, eg. "app.myapp.myfunc")
|
|
*
|
|
* @param {string|Function} _func dot-separated function name or function
|
|
* @param {array} args arguments
|
|
* @param {object} _context
|
|
* @returns {mixed|Promise}
|
|
*/
|
|
applyFunc: function(_func, args, _context)
|
|
{
|
|
let parent = _context || _wnd;
|
|
let func = _func;
|
|
|
|
if (typeof _func === 'string')
|
|
{
|
|
let parts = _func.split('.');
|
|
func = parts.pop();
|
|
for(var i=0; i < parts.length; ++i)
|
|
{
|
|
if (typeof parent[parts[i]] !== 'undefined')
|
|
{
|
|
parent = parent[parts[i]];
|
|
}
|
|
// check if we need a not yet included app.js object --> include it now and return a Promise
|
|
else if (i == 1 && parts[0] == 'app' && typeof app.classes[parts[1]] === 'undefined')
|
|
{
|
|
return this.includeJS('/'+parts[1]+'/js/app.js', undefined, undefined, this.webserverUrl)
|
|
.then(() => this.applyFunc(_func, args, _context),
|
|
(err) => {console.error("Failure loading /"+parts[1]+'/js/app.js' + " (" + err + ")\nAborting.")});
|
|
}
|
|
// check if we need a not yet instantiated app.js object --> instantiate it now
|
|
else if (i == 1 && parts[0] == 'app' && typeof app.classes[parts[1]] === 'function')
|
|
{
|
|
parent = parent[parts[1]] = new app.classes[parts[1]]();
|
|
}
|
|
}
|
|
if (typeof parent[func] == 'function')
|
|
{
|
|
func = parent[func];
|
|
}
|
|
}
|
|
if (typeof func != 'function')
|
|
{
|
|
throw _func+" is not a function!";
|
|
}
|
|
return func.apply(parent, args);
|
|
},
|
|
|
|
/**
|
|
* Registers a new handler plugin.
|
|
*
|
|
* @param _callback is the callback function which should be called
|
|
* whenever a response is comming from the server.
|
|
* @param _context is the context in which the callback function should
|
|
* be called. If null is given, the plugin is executed in the context
|
|
* of the request object context.
|
|
* @param _type is an optional parameter defaulting to 'global'.
|
|
* it describes the response type which this plugin should be
|
|
* handling.
|
|
* @param {boolean} [_global=false] Register the handler globally or
|
|
* locally. Global handlers must stay around, so should be used
|
|
* for global modules.
|
|
*/
|
|
registerJSONPlugin: function(_callback, _context, _type, _global)
|
|
{
|
|
// _type defaults to 'global'
|
|
if (typeof _type === 'undefined')
|
|
{
|
|
_type = 'global';
|
|
}
|
|
// _global defaults to false
|
|
if (typeof _global === 'undefined')
|
|
{
|
|
_global = false;
|
|
}
|
|
var scoped = _global ? global_plugins : plugins;
|
|
|
|
// Create an array for the given category inside the plugins object
|
|
if (typeof scoped[_type] === 'undefined')
|
|
{
|
|
scoped[_type] = [];
|
|
}
|
|
|
|
// Add the entry
|
|
scoped[_type].push({
|
|
'callback': _callback,
|
|
'context': _context
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Removes a previously registered plugin.
|
|
*
|
|
* @param _callback is the callback function which should be called
|
|
* whenever a response is comming from the server.
|
|
* @param _context is the context in which the callback function should
|
|
* be called.
|
|
* @param _type is an optional parameter defaulting to 'global'.
|
|
* it describes the response type which this plugin should be
|
|
* handling.
|
|
* @param {boolean} [_global=false] Remove a global or local handler.
|
|
*/
|
|
unregisterJSONPlugin: function(_callback, _context, _type, _global)
|
|
{
|
|
// _type defaults to 'global'
|
|
if (typeof _type === 'undefined')
|
|
{
|
|
_type = 'global';
|
|
}
|
|
// _global defaults to false
|
|
if (typeof _global === 'undefined')
|
|
{
|
|
_global = false;
|
|
}
|
|
var scoped = _global ? global_plugins : plugins;
|
|
if (typeof scoped[_type] !== 'undefined') {
|
|
for (var i = 0; i < scoped[_type].length; i++)
|
|
{
|
|
if (scoped[_type][i].callback == _callback &&
|
|
scoped[_type][i].context == _context)
|
|
{
|
|
scoped[_type].slice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Regisert the "alert" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
//Check whether all needed parameters have been passed and call the alertHandler function
|
|
if ((typeof res.data.message != 'undefined') &&
|
|
(typeof res.data.details != 'undefined'))
|
|
{
|
|
req.alertHandler(
|
|
res.data.message,
|
|
res.data.details);
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'alert');
|
|
|
|
// Regisert the "message" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
//Check whether all needed parameters have been passed and call the alertHandler function
|
|
if ((typeof res.data.message != 'undefined'))
|
|
{
|
|
req.egw.message(res.data.message, res.data.type);
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'message');
|
|
|
|
// Register the "assign" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
//Check whether all needed parameters have been passed and call the alertHandler function
|
|
if ((typeof res.data.id != 'undefined') &&
|
|
(typeof res.data.key != 'undefined') &&
|
|
(typeof res.data.value != 'undefined'))
|
|
{
|
|
var obj = _wnd.document.getElementById(res.data.id);
|
|
if (obj)
|
|
{
|
|
obj[res.data.key] = res.data.value;
|
|
|
|
if (res.data.key == "innerHTML")
|
|
{
|
|
egw_insertJS(res.data.value);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'assign');
|
|
|
|
// Register the "data" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
//Callback the caller in order to allow him to handle the data
|
|
if (req.callback)
|
|
{
|
|
req.callback.call(req.sender, res.data);
|
|
return true;
|
|
}
|
|
}, null, 'data');
|
|
|
|
// Register the "script" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data == 'string')
|
|
{
|
|
try
|
|
{
|
|
var func = new Function(res.data);
|
|
func.call(req.egw ? req.egw.window : window);
|
|
}
|
|
catch (e)
|
|
{
|
|
req.egw.debug('error', 'Error while executing script: ',
|
|
res.data,e);
|
|
}
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'script');
|
|
|
|
// Register the "apply" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data.func == 'string')
|
|
{
|
|
req.egw.applyFunc(res.data.func, res.data.parms, req.egw.window);
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'apply');
|
|
|
|
// Register the "jquery" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data.select == 'string' &&
|
|
typeof res.data.func == 'string')
|
|
{
|
|
try
|
|
{
|
|
var jQueryObject = jQuery(res.data.select, req.context);
|
|
jQueryObject[res.data.func].apply(jQueryObject, res.data.parms);
|
|
}
|
|
catch (e)
|
|
{
|
|
req.egw.debug('error', 'Function', res.data.func,
|
|
'Parameters', res.data.parms);
|
|
}
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, _wnd, 'jquery');
|
|
|
|
// Register the "redirect" plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
//console.log(res.data.url);
|
|
if (typeof res.data.url == 'string' &&
|
|
typeof res.data.global == 'boolean')
|
|
{
|
|
//Special handling for framework reload
|
|
res.data.global |= (res.data.url.indexOf("?cd=10") > 0);
|
|
|
|
if (res.data.global)
|
|
{
|
|
egw_topWindow().location.href = res.data.url;
|
|
}
|
|
// json request was originating from a different popup --> redirect that one
|
|
else if(this && this.DOMContainer && this.DOMContainer.ownerDocument.defaultView != window &&
|
|
egw(this.DOMContainer.ownerDocument.defaultView).is_popup())
|
|
{
|
|
this.DOMContainer.ownerDocument.location.href = res.data.url;
|
|
}
|
|
// main window, open url in respective tab
|
|
else
|
|
{
|
|
egw_appWindowOpen(res.data.app, res.data.url);
|
|
}
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'redirect');
|
|
|
|
// Register the 'css' plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data == 'string')
|
|
{
|
|
req.egw.includeCSS(res.data);
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'css');
|
|
|
|
// Register the 'js' plugin
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data == 'string')
|
|
{
|
|
req.jsCount++;
|
|
req.egw.includeJS(res.data, function() {
|
|
req.jsFiles++;
|
|
if (req.jsFiles == req.jsCount && req.onLoadFinish)
|
|
{
|
|
req.onLoadFinish.call(req.sender);
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'js');
|
|
|
|
// Register the 'html' plugin, replacing document content with send html
|
|
json.registerJSONPlugin(function(type, res, req) {
|
|
if (typeof res.data == 'string')
|
|
{
|
|
// Empty the document tree
|
|
while (_wnd.document.childNodes.length > 0)
|
|
{
|
|
_wnd.document.removeChild(_wnd.document.childNodes[0]);
|
|
}
|
|
|
|
// Write the given content
|
|
_wnd.document.write(res.data);
|
|
|
|
// Close the document
|
|
_wnd.document.close();
|
|
return true;
|
|
}
|
|
throw 'Invalid parameters';
|
|
}, null, 'html');
|
|
|
|
// Return the extension
|
|
return json;
|
|
});
|
|
|