2012-03-05 14:07:38 +01:00
/ * *
* EGroupware clientside API object
*
* @ license http : //opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @ package etemplate
* @ subpackage api
* @ link http : //www.egroupware.org
* @ author Andreas Stöckel ( as AT stylite . de )
* @ author Ralf Becker < RalfBecker @ outdoor - training . de >
* @ version $Id$
* /
/ * e g w : u s e s
2016-06-06 17:38:20 +02:00
vendor . bower - asset . jquery . dist . jquery ;
2012-03-05 14:07:38 +01:00
egw _core ;
egw _utils ;
egw _files ;
2012-03-05 16:02:00 +01:00
egw _debug ;
2012-03-05 14:07:38 +01:00
* /
2016-02-29 16:50:24 +01:00
/ * *
* Module sending json requests
*
* @ param { string } _app application name object is instanciated for
* @ param { object } _wnd window object is instanciated for
* /
egw . extend ( 'json' , egw . MODULE _WND _LOCAL , function ( _app , _wnd )
{
"use strict" ;
2012-03-05 14:07:38 +01:00
/ * *
* 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 = { } ;
2016-02-04 20:26:37 +01:00
/ * *
* Global json handlers are from global modules , not window level
* /
if ( typeof egw . _global _json _handlers == 'undefined' )
{
2016-02-29 16:50:24 +01:00
egw . _global _json _handlers = { } ;
2016-02-04 20:26:37 +01:00
}
var global _plugins = egw . _global _json _handlers ;
2012-03-05 14:07:38 +01:00
/ * *
* Internal implementation of the JSON request object .
2014-11-21 17:16:24 +01:00
*
* @ param { string } _menuaction
* @ param { array } _parameters
* @ param { function } _callback
* @ param { object } _context
* @ param { boolean } _async
* @ param { object } _sender
* @ param { egw } _egw
2012-03-05 14:07:38 +01:00
* /
2012-03-05 16:02:00 +01:00
function json _request ( _menuaction , _parameters , _callback , _context ,
2012-03-23 13:45:13 +01:00
_async , _sender , _egw )
2012-03-05 14:07:38 +01:00
{
// Copy the parameters
2012-03-05 16:02:00 +01:00
this . url = _egw . ajaxUrl ( _menuaction ) ;
2015-08-31 14:21:11 +02:00
// 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 ) : [ ] ;
2012-03-05 16:02:00 +01:00
this . async = _async ? _async : false ;
this . callback = _callback ? _callback : null ;
this . context = _context ? _context : null ;
this . sender = _sender ? _sender : null ;
2012-03-05 14:07:38 +01:00
this . egw = _egw ;
2012-03-05 16:02:00 +01:00
// We currently don't have a request object
2012-03-05 14:07:38 +01:00
this . request = null ;
2012-03-05 16:02:00 +01:00
// Some variables needed for notification about a JS files done loading
2012-03-05 14:07:38 +01:00
this . onLoadFinish = null ;
this . jsFiles = 0 ;
this . jsCount = 0 ;
2012-03-05 16:02:00 +01:00
// Function which is currently used to display alerts -- may be replaced by
// some API function.
this . alertHandler = function ( _message , _details ) {
alert ( _message ) ;
if ( _details )
{
_egw . debug ( 'info' , _message , _details ) ;
}
} ;
2012-03-05 14:07:38 +01:00
}
2013-09-10 22:22:47 +02:00
/ * *
* Sends the assembled request to the server
* @ param { boolean } [ async = false ] Overrides async provided in constructor to give an easy way to make simple async requests
2014-11-21 17:16:24 +01:00
* @ param { string } method = 'POST' allow to eg . use a ( cachable ) 'GET' request instead of POST
2014-03-26 18:53:09 +01:00
*
* @ return { jqXHR } jQuery jqXHR request object
2013-09-10 22:22:47 +02:00
* /
2014-01-18 18:43:15 +01:00
json _request . prototype . sendRequest = function ( async , method ) {
2013-09-16 16:35:55 +02:00
if ( typeof async != "undefined" )
2013-09-10 22:22:47 +02:00
{
this . async = async ;
}
2014-01-09 13:20:13 +01:00
2012-03-05 16:02:00 +01:00
// Assemble the complete request
var request _obj = {
'json_data' : this . egw . jsonEncode ( {
'request' : {
'parameters' : this . parameters
}
} )
} ;
// Send the request via AJAX using the jquery ajax function
2013-10-18 18:14:36 +02:00
// we need to use jQuery of window of egw object, as otherwise the one from main window is used!
2014-01-09 13:20:13 +01:00
// (causing eg. apply from server with app.$app.method to run in main window instead of popup)
2016-06-02 16:51:15 +02:00
this . request = ( this . egw . window ? this . egw . window . jQuery : jQuery ) . ajax ( {
2012-03-05 16:02:00 +01:00
url : this . url ,
async : this . async ,
context : this ,
data : request _obj ,
dataType : 'json' ,
2014-01-18 18:43:15 +01:00
type : method || 'POST' ,
2012-03-05 16:02:00 +01:00
success : this . handleResponse ,
error : function ( _xmlhttp , _err ) {
2014-03-26 18:53:09 +01:00
// Don't error about an abort
if ( _err !== 'abort' )
{
2016-06-28 20:39:39 +02:00
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.' ) +
2015-03-09 16:23:07 +01:00
"\n\nURL: " + this . url + "\n" + ( _xmlhttp . getAllResponseHeaders ( ) ? _xmlhttp . getAllResponseHeaders ( ) . match ( /^Date:.*$/m ) [ 0 ] : '' ) ) ;
2014-11-21 17:16:24 +01:00
this . egw . debug ( 'error' , 'Ajax request to' , this . url , ' failed: ' , _err , _xmlhttp . status , _xmlhttp . statusText ) ;
2014-03-26 18:53:09 +01:00
}
2012-03-05 16:02:00 +01:00
}
} ) ;
2014-03-26 18:53:09 +01:00
return this . request ;
2014-11-21 17:16:24 +01:00
} ;
2012-03-05 16:02:00 +01:00
json _request . prototype . handleResponse = function ( data ) {
2014-01-22 10:30:22 +01:00
if ( data && typeof data . response != 'undefined' )
2012-03-05 16:02:00 +01:00
{
2013-07-20 15:41:17 +02:00
// 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 )
{
2014-01-09 13:20:13 +01:00
var start _time = ( new Date ) . getTime ( ) ;
this . egw . includeJS ( js _files , function ( ) {
var end _time = ( new Date ) . getTime ( ) ;
this . handleResponse ( data ) ;
2016-06-02 16:51:15 +02:00
var gen _time _div = jQuery ( '#divGenTime_' + this . egw . appname ) ;
if ( ! gen _time _div . length ) gen _time _div = jQuery ( '.pageGenTime' ) ;
2014-01-09 13:20:13 +01:00
gen _time _div . append ( '<span class="asyncIncludeTime">' + egw . lang ( 'async includes took %1s' , ( end _time - start _time ) / 1000 ) + '</span>' ) ;
} , this ) ;
2013-07-20 15:41:17 +02:00
return ;
}
2014-01-11 15:47:31 +01:00
// Flag for only data response - don't call callback if only data
2014-01-22 10:30:22 +01:00
var only _data = ( data . response . length > 0 ) ;
2014-01-11 15:47:31 +01:00
2012-03-05 16:02:00 +01:00
for ( var i = 0 ; i < data . response . length ; i ++ )
{
2012-03-06 14:22:01 +01:00
// Get the response object
var res = data . response [ i ] ;
2014-01-11 15:47:31 +01:00
if ( typeof res . type == 'string' && res . type != 'data' ) only _data = false ;
2012-03-06 14:22:01 +01:00
// Check whether a plugin for the given type exists
2016-02-04 20:26:37 +01:00
var handlers = [ plugins , global _plugins ] ;
for ( var handler _idx = 0 ; handler _idx < handlers . length ; handler _idx ++ )
2012-03-06 14:22:01 +01:00
{
2016-02-04 20:26:37 +01:00
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 ] ;
2012-03-06 14:22:01 +01:00
2016-02-04 20:26:37 +01:00
// Call the plugin callback
plugin . callback . call (
plugin . context ? plugin . context : this . context ,
res . type , res , this
) ;
} 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 ) ;
}
2012-03-06 14:22:01 +01:00
}
2012-03-05 16:02:00 +01:00
}
}
}
2014-01-11 15:47:31 +01:00
// Call request callback, if provided
if ( this . callback != null && ! only _data )
{
this . callback . call ( this . context , res ) ;
}
2012-03-05 16:02:00 +01:00
}
2014-03-26 18:53:09 +01:00
this . request = null ;
2014-11-21 17:16:24 +01:00
} ;
2012-03-05 14:07:38 +01:00
var json = {
/ * * T h e c o n s t r u c t o r o f t h e e g w _ j s o n _ r e q u e s t c l a s s .
*
* @ 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 .
2012-03-05 16:02:00 +01:00
* @ param _async specifies whether the request should be asynchronous or
* not .
2012-03-05 14:07:38 +01:00
* @ param _callback specifies the callback function which should be
* called , once the request has been sucessfully executed .
2014-01-09 13:20:13 +01:00
* @ param _context is the context which will be used for the callback function
2012-03-05 16:02:00 +01:00
* @ param _sender is a parameter being passed to the _callback function
2012-03-05 14:07:38 +01:00
* /
2012-03-05 16:02:00 +01:00
json : function ( _menuaction , _parameters , _callback , _context , _async ,
_sender )
2012-03-05 14:07:38 +01:00
{
2014-01-09 13:20:13 +01:00
return new json _request ( _menuaction , _parameters , _callback ,
2012-03-05 16:02:00 +01:00
_context , _async , _sender , this ) ;
} ,
2012-03-05 14:07:38 +01:00
/ * *
* 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
2012-03-05 16:02:00 +01:00
* of the request object context .
2012-03-05 14:07:38 +01:00
* @ param _type is an optional parameter defaulting to 'global' .
* it describes the response type which this plugin should be
* handling .
2016-02-04 20:26:37 +01:00
* @ param { boolean } [ _global = false ] Register the handler globally or
* locally . Global handlers must stay around , so should be used
* for global modules .
2012-03-05 14:07:38 +01:00
* /
2016-02-04 20:26:37 +01:00
registerJSONPlugin : function ( _callback , _context , _type , _global )
2012-03-05 14:07:38 +01:00
{
// _type defaults to 'global'
if ( typeof _type === 'undefined' )
{
_type = 'global' ;
}
2016-02-04 20:26:37 +01:00
// _global defaults to false
if ( typeof _global === 'undefined' )
{
_global = false ;
}
var scoped = _global ? global _plugins : plugins ;
2012-03-05 14:07:38 +01:00
// Create an array for the given category inside the plugins object
2016-02-04 20:26:37 +01:00
if ( typeof scoped [ _type ] === 'undefined' )
2012-03-05 14:07:38 +01:00
{
2016-02-04 20:26:37 +01:00
scoped [ _type ] = [ ] ;
2012-03-05 14:07:38 +01:00
}
// Add the entry
2016-02-04 20:26:37 +01:00
scoped [ _type ] . push ( {
2012-03-05 14:07:38 +01:00
'callback' : _callback ,
2014-11-21 17:16:24 +01:00
'context' : _context
2012-03-05 14:07:38 +01:00
} ) ;
} ,
/ * *
* 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 .
2016-02-04 20:26:37 +01:00
* @ param { boolean } [ _global = false ] Remove a global or local handler .
2012-03-05 14:07:38 +01:00
* /
2016-02-04 20:26:37 +01:00
unregisterJSONPlugin : function ( _callback , _context , _type , _global )
2012-03-05 14:07:38 +01:00
{
// _type defaults to 'global'
if ( typeof _type === 'undefined' )
{
_type = 'global' ;
}
2016-02-04 20:26:37 +01:00
// _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 ++ )
2012-03-05 14:07:38 +01:00
{
2016-02-04 20:26:37 +01:00
if ( scoped [ _type ] [ i ] . callback == _callback &&
scoped [ _type ] [ i ] . context == _context )
2012-03-05 14:07:38 +01:00
{
2016-02-04 20:26:37 +01:00
scoped [ _type ] . slice ( i , 1 ) ;
2012-03-05 14:07:38 +01:00
break ;
}
}
}
}
} ;
// Regisert the "alert" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
//Check whether all needed parameters have been passed and call the alertHandler function
2014-01-09 13:20:13 +01:00
if ( ( typeof res . data . message != 'undefined' ) &&
2012-03-05 14:07:38 +01:00
( typeof res . data . details != 'undefined' ) )
2012-03-05 16:02:00 +01:00
{
req . alertHandler (
2012-03-05 14:07:38 +01:00
res . data . message ,
2014-11-21 17:16:24 +01:00
res . data . details ) ;
2012-03-05 14:07:38 +01:00
return true ;
}
throw 'Invalid parameters' ;
} , null , 'alert' ) ;
// Register the "assign" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
//Check whether all needed parameters have been passed and call the alertHandler function
2014-01-09 13:20:13 +01:00
if ( ( typeof res . data . id != 'undefined' ) &&
2012-03-05 14:07:38 +01:00
( typeof res . data . key != 'undefined' ) &&
( typeof res . data . value != 'undefined' ) )
2012-03-05 16:02:00 +01:00
{
2013-10-28 23:22:09 +01:00
var obj = _wnd . document . getElementById ( res . data . id ) ;
2012-03-05 14:07:38 +01:00
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
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
//Callback the caller in order to allow him to handle the data
2012-03-05 16:02:00 +01:00
if ( req . callback )
2012-03-05 14:07:38 +01:00
{
2012-03-05 16:02:00 +01:00
req . callback . call ( req . sender , res . data ) ;
2012-03-05 14:07:38 +01:00
return true ;
}
} , null , 'data' ) ;
// Register the "script" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
if ( typeof res . data == 'string' )
{
try
{
var func = new Function ( res . data ) ;
2013-03-25 17:58:33 +01:00
func . call ( req . egw ? req . egw . window : window ) ;
2012-03-05 14:07:38 +01:00
}
catch ( e )
{
2012-03-05 16:02:00 +01:00
req . egw . debug ( 'error' , 'Error while executing script: ' ,
2014-11-21 17:16:24 +01:00
res . data , e ) ;
2012-03-05 14:07:38 +01:00
}
return true ;
}
throw 'Invalid parameters' ;
} , null , 'script' ) ;
// Register the "apply" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2013-04-12 18:03:45 +02:00
if ( typeof res . data . func == 'string' )
2012-03-05 14:07:38 +01:00
{
2013-04-12 18:03:45 +02:00
var parts = res . data . func . split ( '.' ) ;
var func = parts . pop ( ) ;
2013-09-11 17:52:49 +02:00
var parent = req . egw . window ;
2014-01-23 10:03:30 +01:00
for ( var i = 0 ; i < parts . length ; ++ i )
2012-03-05 14:07:38 +01:00
{
2014-01-23 10:03:30 +01:00
if ( typeof parent [ parts [ i ] ] != 'undefined' )
{
parent = parent [ parts [ i ] ] ;
}
// check if we need a not yet instanciated app.js object --> instanciate it now
else if ( i == 1 && parts [ 0 ] == 'app' && typeof req . egw . window . app . classes [ parts [ 1 ] ] == 'function' )
{
parent = parent [ parts [ 1 ] ] = new req . egw . window . app . classes [ parts [ 1 ] ] ( ) ;
}
2012-03-05 14:07:38 +01:00
}
2013-04-12 18:03:45 +02:00
if ( typeof parent [ func ] == 'function' )
2012-03-05 14:07:38 +01:00
{
2013-04-12 18:03:45 +02:00
try
{
parent [ func ] . apply ( parent , res . data . parms ) ;
}
catch ( e )
{
2013-09-17 22:17:20 +02:00
req . egw . debug ( 'error' , e . message , ' in function' , res . data . func ,
2013-04-12 18:03:45 +02:00
'Parameters' , res . data . parms ) ;
}
return true ;
2012-03-05 14:07:38 +01:00
}
2013-10-04 11:40:35 +02:00
else
{
throw '"' + res . data . func + '" is not a callable function (type is ' + typeof parent [ func ] + ')' ;
}
2012-03-05 14:07:38 +01:00
}
throw 'Invalid parameters' ;
} , null , 'apply' ) ;
// Register the "jquery" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
if ( typeof res . data . select == 'string' &&
typeof res . data . func == 'string' )
{
try
{
2016-06-02 16:51:15 +02:00
var jQueryObject = jQuery ( res . data . select , req . context ) ;
2012-03-05 14:07:38 +01:00
jQueryObject [ res . data . func ] . apply ( jQueryObject , res . data . parms ) ;
}
catch ( e )
{
2012-03-05 16:02:00 +01:00
req . egw . debug ( 'error' , 'Function' , res . data . func ,
2012-03-05 14:07:38 +01:00
'Parameters' , res . data . parms ) ;
}
return true ;
}
throw 'Invalid parameters' ;
2013-10-28 23:22:09 +01:00
} , _wnd , 'jquery' ) ;
2012-03-05 14:07:38 +01:00
// Register the "redirect" plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
//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 ;
}
2014-11-19 17:10:39 +01:00
// 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 ( ) )
2014-10-15 16:55:08 +02:00
{
this . DOMContainer . ownerDocument . location . href = res . data . url ;
}
// main window, open url in respective tab
2012-03-05 14:07:38 +01:00
else
{
2014-01-27 17:28:39 +01:00
egw _appWindowOpen ( res . data . app , res . data . url ) ;
2012-03-05 14:07:38 +01:00
}
return true ;
}
throw 'Invalid parameters' ;
} , null , 'redirect' ) ;
// Register the 'css' plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
if ( typeof res . data == 'string' )
{
2012-03-05 16:02:00 +01:00
req . egw . includeCSS ( res . data ) ;
2012-03-05 14:07:38 +01:00
return true ;
}
throw 'Invalid parameters' ;
} , null , 'css' ) ;
// Register the 'js' plugin
2012-03-05 16:02:00 +01:00
json . registerJSONPlugin ( function ( type , res , req ) {
2012-03-05 14:07:38 +01:00
if ( typeof res . data == 'string' )
{
2012-03-05 16:02:00 +01:00
req . jsCount ++ ;
req . egw . includeJS ( res . data , function ( ) {
req . jsFiles ++ ;
if ( req . jsFiles == req . jsCount && req . onLoadFinish )
2012-03-05 14:07:38 +01:00
{
2012-03-05 16:02:00 +01:00
req . onLoadFinish . call ( req . sender ) ;
2012-03-05 14:07:38 +01:00
}
} ) ;
2013-02-14 12:01:50 +01:00
return true ;
2012-03-05 14:07:38 +01:00
}
throw 'Invalid parameters' ;
} , null , 'js' ) ;
2012-03-06 10:50:43 +01:00
// 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 )
{
2012-03-06 14:22:01 +01:00
_wnd . document . removeChild ( _wnd . document . childNodes [ 0 ] ) ;
2012-03-06 10:50:43 +01:00
}
// Write the given content
_wnd . document . write ( res . data ) ;
// Close the document
_wnd . document . close ( ) ;
2013-02-14 12:01:50 +01:00
return true ;
2012-03-06 10:50:43 +01:00
}
throw 'Invalid parameters' ;
} , null , 'html' ) ;
2012-03-05 14:07:38 +01:00
// Return the extension
return json ;
} ) ;