web/data/cubicweb.ajax.js
branchtls-sprint
changeset 1419 7ff24154351d
parent 1407 75863d3ffd9b
child 1498 2c6eec0b46b9
equal deleted inserted replaced
1417:06af20e663f2 1419:7ff24154351d
     1 /*
     1 /*
     2  *  :organization: Logilab
     2  *  :organization: Logilab
     3  *  :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     3  *  :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     4  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     4  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5  */
     5  */
     6 
     6 
     7 CubicWeb.require('python.js');
     7 CubicWeb.require('python.js');
     8 CubicWeb.require('htmlhelpers.js');
     8 CubicWeb.require('htmlhelpers.js');
    35 	buildWidgets(node);
    35 	buildWidgets(node);
    36     }
    36     }
    37     if (typeof roundedCornersOnLoad != 'undefined') {
    37     if (typeof roundedCornersOnLoad != 'undefined') {
    38 	roundedCornersOnLoad();
    38 	roundedCornersOnLoad();
    39     }
    39     }
       
    40     jQuery(CubicWeb).trigger('ajax-loaded');
    40 }
    41 }
    41 
    42 
    42 // cubicweb loadxhtml plugin to make jquery handle xhtml response
    43 // cubicweb loadxhtml plugin to make jquery handle xhtml response
    43 jQuery.fn.loadxhtml = function(url, data, reqtype, mode) {
    44 jQuery.fn.loadxhtml = function(url, data, reqtype, mode) {
    44     var ajax = null;
    45     var ajax = null;
   107 
   108 
   108 jQuery(document).ready(loadDynamicFragments);
   109 jQuery(document).ready(loadDynamicFragments);
   109 
   110 
   110 //============= base AJAX functions to make remote calls =====================//
   111 //============= base AJAX functions to make remote calls =====================//
   111 
   112 
   112 
       
   113 /*
       
   114  * This function will call **synchronously** a remote method on the cubicweb server
       
   115  * @param fname: the function name to call (as exposed by the JSONController)
       
   116  * @param args: the list of arguments to pass the function
       
   117  */
       
   118 function remote_exec(fname) {
       
   119     setProgressCursor();
       
   120     var props = {'mode' : "remote", 'fname' : fname, 'pageid' : pageid,
       
   121      		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
       
   122     var result  = jQuery.ajax({url: JSON_BASE_URL, data: props, async: false}).responseText;
       
   123     result = evalJSON(result);
       
   124     resetCursor();
       
   125     return result;
       
   126 }
       
   127 
       
   128 function remoteCallFailed(err, req) {
   113 function remoteCallFailed(err, req) {
   129     if (req.status == 500) {
   114     if (req.status == 500) {
   130 	updateMessage(err);
   115 	updateMessage(err);
   131     } else {
   116     } else {
   132 	updateMessage(_("an error occured while processing your request"));
   117 	updateMessage(_("an error occured while processing your request"));
   135 
   120 
   136 /*
   121 /*
   137  * This function is the equivalent of MochiKit's loadJSONDoc but
   122  * This function is the equivalent of MochiKit's loadJSONDoc but
   138  * uses POST instead of GET
   123  * uses POST instead of GET
   139  */
   124  */
   140 function loadJSONDocUsingPOST(url, queryargs, mode) {
   125 function loadJSONDocUsingPOST(url, data) {
   141     mode = mode || 'remote';
       
   142     setProgressCursor();
   126     setProgressCursor();
   143     var dataType = (mode == 'remote') ? "json":null;
   127     var deferred = loadJSON(url, data, 'POST');
   144     var deferred = loadJSON(url, queryargs, 'POST', dataType);
       
   145     deferred = deferred.addErrback(remoteCallFailed);
   128     deferred = deferred.addErrback(remoteCallFailed);
   146 //     if (mode == 'remote') {
       
   147 // 	deferred = deferred.addCallbacks(evalJSONRequest);
       
   148 //     }
       
   149     deferred = deferred.addCallback(resetCursor);
   129     deferred = deferred.addCallback(resetCursor);
   150     return deferred;
   130     return deferred;
   151 }
   131 }
   152 
   132 
   153 
   133 
   154 function _buildRemoteArgs(fname) {
   134 /*
   155     return  {'mode' : "remote", 'fname' : fname, 'pageid' : pageid,
   135  * This function will call **synchronously** a remote method on the cubicweb server
   156      	     'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
       
   157 }
       
   158 
       
   159 /*
       
   160  * This function will call **asynchronously** a remote method on the cubicweb server
       
   161  * This function is a low level one. You should use `async_remote_exec` or
       
   162  * `async_rawremote_exec` instead.
       
   163  *
       
   164  * @param fname: the function name to call (as exposed by the JSONController)
   136  * @param fname: the function name to call (as exposed by the JSONController)
   165  * @param funcargs: the function's arguments
   137  *
   166  * @param mode: rawremote or remote
   138  * additional arguments will be directly passed to the specified function
   167  */
   139  *
   168 function _async_exec(fname, funcargs, mode) {
   140  * It looks at http headers to guess the response type.
   169     var props = {'mode' : mode, 'fname' : fname, 'pageid' : pageid};
   141  */
   170     var args = map(urlEncode, map(jQuery.toJSON, funcargs));
   142 function remoteExec(fname /* ... */) {
   171     args.unshift(''); // this is to be able to use join() directly
   143     setProgressCursor();
   172     var queryargs = as_url(props) + args.join('&arg=');
   144     var props = {'fname' : fname, 'pageid' : pageid,
   173     return loadJSONDocUsingPOST(JSON_BASE_URL, queryargs, mode);
   145      		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
   174 }
   146     var result  = jQuery.ajax({url: JSON_BASE_URL, data: props, async: false}).responseText;
   175 
   147     if (result) {
   176 /*
   148 	result = evalJSON(result);
   177  * This function will call **asynchronously** a remote method on the cubicweb server
   149     }
       
   150     resetCursor();
       
   151     return result;
       
   152 }
       
   153 
       
   154 /*
       
   155  * This function will call **asynchronously** a remote method on the json
       
   156  * controller of the cubicweb http server
       
   157  *
   178  * @param fname: the function name to call (as exposed by the JSONController)
   158  * @param fname: the function name to call (as exposed by the JSONController)
       
   159  *
   179  * additional arguments will be directly passed to the specified function
   160  * additional arguments will be directly passed to the specified function
   180  * Expected response type is Json.
   161  *
   181  */
       
   182 function async_remote_exec(fname /* ... */) {
       
   183     return _async_exec(fname, sliceList(arguments, 1), 'remote');
       
   184 }
       
   185 
       
   186 /*
       
   187  * This version of _async_exec doesn't expect a json response.
       
   188  * It looks at http headers to guess the response type.
   162  * It looks at http headers to guess the response type.
   189  */
   163  */
   190 function async_rawremote_exec(fname /* ... */) {
   164 function asyncRemoteExec(fname /* ... */) {
   191     return _async_exec(fname, sliceList(arguments, 1), 'rawremote');
   165     var props = {'fname' : fname, 'pageid' : pageid,
   192 }
   166      		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
   193 
   167     return loadJSONDocUsingPOST(JSON_BASE_URL, props);
   194 /*
   168 }
   195  * This function will call **asynchronously** a remote method on the cubicweb server
   169 
   196  * @param fname: the function name to call (as exposed by the JSONController)
       
   197  * @param varargs: the list of arguments to pass to the function
       
   198  * This is an alternative form of `async_remote_exec` provided for convenience
       
   199  */
       
   200 function async_remote_exec_varargs(fname, varargs) {
       
   201     return _async_exec(fname, varargs, 'remote');
       
   202 }
       
   203 
   170 
   204 /* emulation of gettext's _ shortcut
   171 /* emulation of gettext's _ shortcut
   205  */
   172  */
   206 function _(message) {
   173 function _(message) {
   207     return remote_exec('i18n', [message])[0];
   174     return remoteExec('i18n', [message])[0];
   208 }
       
   209 
       
   210 function rqlexec(rql) {
       
   211     return async_remote_exec('rql', rql);
       
   212 }
   175 }
   213 
   176 
   214 function userCallback(cbname) {
   177 function userCallback(cbname) {
   215     async_remote_exec('user_callback', cbname);
   178     asyncRemoteExec('user_callback', cbname);
   216 }
   179 }
   217 
   180 
   218 function unloadPageData() {
   181 function unloadPageData() {
   219     // NOTE: do not make async calls on unload if you want to avoid
   182     // NOTE: do not make async calls on unload if you want to avoid
   220     //       strange bugs
   183     //       strange bugs
   221     remote_exec('unload_page_data');
   184     remoteExec('unload_page_data');
   222 }
   185 }
   223 
   186 
   224 function openHash() {
   187 function openHash() {
   225     if (document.location.hash) {
   188     if (document.location.hash) {
   226 	var nid = document.location.hash.replace('#', '');
   189 	var nid = document.location.hash.replace('#', '');
   234     registry = registry || 'components';
   197     registry = registry || 'components';
   235     rql = rql || '';
   198     rql = rql || '';
   236     nodeid = nodeid || (compid + 'Component');
   199     nodeid = nodeid || (compid + 'Component');
   237     extraargs = extraargs || {};
   200     extraargs = extraargs || {};
   238     var node = getNode(nodeid);
   201     var node = getNode(nodeid);
   239     var d = async_rawremote_exec('component', compid, rql, registry, extraargs);
   202     var d = asyncRemoteExec('component', compid, rql, registry, extraargs);
   240     d.addCallback(function(result, req) {
   203     d.addCallback(function(result, req) {
   241 	var domnode = getDomFromResponse(result);
   204 	var domnode = getDomFromResponse(result);
   242 	if (node) {
   205 	if (node) {
   243 	    // make sure the component is visible
   206 	    // make sure the component is visible
   244 	    removeElementClass(node, "hidden");
   207 	    removeElementClass(node, "hidden");
   257 function reloadBox(boxid, rql) {
   220 function reloadBox(boxid, rql) {
   258     return reloadComponent(boxid, rql, 'boxes', boxid);
   221     return reloadComponent(boxid, rql, 'boxes', boxid);
   259 }
   222 }
   260 
   223 
   261 function userCallbackThenUpdateUI(cbname, compid, rql, msg, registry, nodeid) {
   224 function userCallbackThenUpdateUI(cbname, compid, rql, msg, registry, nodeid) {
   262     var d = async_remote_exec('user_callback', cbname);
   225     var d = asyncRemoteExec('user_callback', cbname);
   263     d.addCallback(function() {
   226     d.addCallback(function() {
   264 	reloadComponent(compid, rql, registry, nodeid);
   227 	reloadComponent(compid, rql, registry, nodeid);
   265 	if (msg) { updateMessage(msg); }
   228 	if (msg) { updateMessage(msg); }
   266     });
   229     });
   267     d.addCallback(resetCursor);
   230     d.addCallback(resetCursor);
   271 	return resetCursor();
   234 	return resetCursor();
   272     });
   235     });
   273 }
   236 }
   274 
   237 
   275 function userCallbackThenReloadPage(cbname, msg) {
   238 function userCallbackThenReloadPage(cbname, msg) {
   276     var d = async_remote_exec('user_callback', cbname);
   239     var d = asyncRemoteExec('user_callback', cbname);
   277     d.addCallback(function() {
   240     d.addCallback(function() {
   278 	window.location.reload();
   241 	window.location.reload();
   279 	if (msg) { updateMessage(msg); }
   242 	if (msg) { updateMessage(msg); }
   280     });
   243     });
   281     d.addCallback(resetCursor);
   244     d.addCallback(resetCursor);
   289 /*
   252 /*
   290  * unregisters the python function registered on the server's side
   253  * unregisters the python function registered on the server's side
   291  * while the page was generated.
   254  * while the page was generated.
   292  */
   255  */
   293 function unregisterUserCallback(cbname) {
   256 function unregisterUserCallback(cbname) {
   294     var d = async_remote_exec('unregister_user_callback', cbname);
   257     var d = asyncRemoteExec('unregister_user_callback', cbname);
   295     d.addCallback(function() {resetCursor();});
   258     d.addCallback(function() {resetCursor();});
   296     d.addErrback(function(xxx) {
   259     d.addErrback(function(xxx) {
   297 	updateMessage(_("an error occured"));
   260 	updateMessage(_("an error occured"));
   298 	log(xxx);
   261 	log(xxx);
   299 	return resetCursor();
   262 	return resetCursor();
   320     if (node) {
   283     if (node) {
   321 	props['rql'] = rql;
   284 	props['rql'] = rql;
   322 	props['pageid'] = pageid;
   285 	props['pageid'] = pageid;
   323 	if (vid) { props['vid'] = vid; }
   286 	if (vid) { props['vid'] = vid; }
   324 	if (extraparams) { jQuery.extend(props, extraparams); }
   287 	if (extraparams) { jQuery.extend(props, extraparams); }
   325 	// FIXME we need to do as_url(props) manually instead of
   288 	// FIXME we need to do asURL(props) manually instead of
   326 	// passing `props` directly to loadxml because replacePageChunk
   289 	// passing `props` directly to loadxml because replacePageChunk
   327 	// is sometimes called (abusively) with some extra parameters in `vid`
   290 	// is sometimes called (abusively) with some extra parameters in `vid`
   328 	var mode = swap?'swap':'replace';
   291 	var mode = swap?'swap':'replace';
   329 	var url = JSON_BASE_URL + as_url(props);
   292 	var url = JSON_BASE_URL + asURL(props);
   330 	jQuery(node).loadxhtml(url, params, 'get', mode);
   293 	jQuery(node).loadxhtml(url, params, 'get', mode);
   331     } else {
   294     } else {
   332 	log('Node', nodeId, 'not found');
   295 	log('Node', nodeId, 'not found');
   333     }
   296     }
   334 }
   297 }
   355 }
   318 }
   356 
   319 
   357 jQuery(document).ready(buildWysiwygEditors);
   320 jQuery(document).ready(buildWysiwygEditors);
   358 
   321 
   359 
   322 
       
   323 /*
       
   324  * takes a list of DOM nodes and removes all empty text nodes
       
   325  */
       
   326 function stripEmptyTextNodes(nodelist) {
       
   327     var stripped = [];
       
   328     for (var i=0; i < nodelist.length; i++) {
       
   329 	var node = nodelist[i];
       
   330 	if (isTextNode(node) && !node.textContent.strip()) {
       
   331 	    continue;
       
   332 	} else {
       
   333 	    stripped.push(node);
       
   334 	}
       
   335     }
       
   336     return stripped;
       
   337 }
       
   338 
   360 /* convenience function that returns a DOM node based on req's result. */
   339 /* convenience function that returns a DOM node based on req's result. */
   361 function getDomFromResponse(response) {
   340 function getDomFromResponse(response) {
   362     if (typeof(response) == 'string') {
   341     if (typeof(response) == 'string') {
   363 	return html2dom(response);
   342 	return html2dom(response);
   364     }
   343     }
   366     var children = doc.childNodes;
   345     var children = doc.childNodes;
   367     if (!children.length) {
   346     if (!children.length) {
   368 	// no child (error cases) => return the whole document
   347 	// no child (error cases) => return the whole document
   369 	return doc.cloneNode(true);
   348 	return doc.cloneNode(true);
   370     }
   349     }
       
   350     children = stripEmptyTextNodes(children);
   371     if (children.length == 1) {
   351     if (children.length == 1) {
   372 	// only one child => return it
   352 	// only one child => return it
   373 	return children[0].cloneNode(true);
   353 	return children[0].cloneNode(true);
   374     }
   354     }
   375     // several children => wrap them in a single node and return the wrap
   355     // several children => wrap them in a single node and return the wrap