Fossil SCM

minor jsonp tweaks. Added some test code for it in the demo app, but there is still some jsonp disconnect between the two AJAJ layers, and i may need to consolidate them to work around it.

stephan 2011-09-29 17:03 UTC json
Commit f48b687a9a1ac0218d604c5a68cac99ea5011a02
+10 -3
--- ajax/index.html
+++ ajax/index.html
@@ -52,11 +52,11 @@
5252
this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
5353
if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
5454
};
5555
5656
TheApp.responseContainsError = function(resp) {
57
- if( !resp || resp.resultCode ) {
57
+ if( resp && resp.resultCode ) {
5858
//alert("Error response:\n"+JSON.stringify(resp,0,4));
5959
TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
6060
//TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
6161
//TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
6262
TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
@@ -99,21 +99,25 @@
9999
{
100100
var current = this.css( 'color' );
101101
this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
102102
this.animate( { color: current }, duration / 2 );
103103
};
104
+
105
+function myJsonPCallback(obj){
106
+ alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
107
+}
104108
105109
jQuery(document).ready(function(){
106
- var ids = [
110
+ var ids = [// list of HTML element IDs we use often.
107111
'btnSend',
108112
'ajaxNotification',
109113
'currentAuthToken',
110114
'responseContainer',
111115
'taRequest',
112116
'taRequestOpt',
113117
'taResponse',
114
- 'textPath', // list of HTML element IDs we use often.
118
+ 'textPath',
115119
'timer'
116120
];
117121
var i, k;
118122
for( i = 0; i < ids.length; ++i ) {
119123
k = ids[i];
@@ -152,10 +156,11 @@
152156
TheApp.endAjaxNotif();
153157
TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
154158
};
155159
opt.onResponse = function(resp,req) {
156160
var val;
161
+ if(this.jsonp) return /*was already handled*/;
157162
try {
158163
val = WhAjaj.stringify(resp);
159164
}
160165
catch(e) {
161166
val = WhAjaj.stringify(e)
@@ -201,10 +206,12 @@
201206
<input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
202207
If the POST textarea is not empty then it will be posted with the request.
203208
<hr/>
204209
<strong>Quick-posts:</strong><br/>
205210
<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
211
+<input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
212
+
206213
<input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
207214
<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
208215
<input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' />
209216
<input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
210217
<input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
211218
--- ajax/index.html
+++ ajax/index.html
@@ -52,11 +52,11 @@
52 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
53 if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
54 };
55
56 TheApp.responseContainsError = function(resp) {
57 if( !resp || resp.resultCode ) {
58 //alert("Error response:\n"+JSON.stringify(resp,0,4));
59 TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
60 //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
61 //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
62 TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
@@ -99,21 +99,25 @@
99 {
100 var current = this.css( 'color' );
101 this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
102 this.animate( { color: current }, duration / 2 );
103 };
 
 
 
 
104
105 jQuery(document).ready(function(){
106 var ids = [
107 'btnSend',
108 'ajaxNotification',
109 'currentAuthToken',
110 'responseContainer',
111 'taRequest',
112 'taRequestOpt',
113 'taResponse',
114 'textPath', // list of HTML element IDs we use often.
115 'timer'
116 ];
117 var i, k;
118 for( i = 0; i < ids.length; ++i ) {
119 k = ids[i];
@@ -152,10 +156,11 @@
152 TheApp.endAjaxNotif();
153 TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
154 };
155 opt.onResponse = function(resp,req) {
156 var val;
 
157 try {
158 val = WhAjaj.stringify(resp);
159 }
160 catch(e) {
161 val = WhAjaj.stringify(e)
@@ -201,10 +206,12 @@
201 <input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
202 If the POST textarea is not empty then it will be posted with the request.
203 <hr/>
204 <strong>Quick-posts:</strong><br/>
205 <input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
 
 
206 <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
207 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
208 <input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' />
209 <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
210 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
211
--- ajax/index.html
+++ ajax/index.html
@@ -52,11 +52,11 @@
52 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
53 if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
54 };
55
56 TheApp.responseContainsError = function(resp) {
57 if( resp && resp.resultCode ) {
58 //alert("Error response:\n"+JSON.stringify(resp,0,4));
59 TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
60 //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
61 //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
62 TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
@@ -99,21 +99,25 @@
99 {
100 var current = this.css( 'color' );
101 this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
102 this.animate( { color: current }, duration / 2 );
103 };
104
105 function myJsonPCallback(obj){
106 alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
107 }
108
109 jQuery(document).ready(function(){
110 var ids = [// list of HTML element IDs we use often.
111 'btnSend',
112 'ajaxNotification',
113 'currentAuthToken',
114 'responseContainer',
115 'taRequest',
116 'taRequestOpt',
117 'taResponse',
118 'textPath',
119 'timer'
120 ];
121 var i, k;
122 for( i = 0; i < ids.length; ++i ) {
123 k = ids[i];
@@ -152,10 +156,11 @@
156 TheApp.endAjaxNotif();
157 TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
158 };
159 opt.onResponse = function(resp,req) {
160 var val;
161 if(this.jsonp) return /*was already handled*/;
162 try {
163 val = WhAjaj.stringify(resp);
164 }
165 catch(e) {
166 val = WhAjaj.stringify(e)
@@ -201,10 +206,12 @@
206 <input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
207 If the POST textarea is not empty then it will be posted with the request.
208 <hr/>
209 <strong>Quick-posts:</strong><br/>
210 <input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
211 <input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
212
213 <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
214 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
215 <input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' />
216 <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
217 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
218
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -5,19 +5,20 @@
55
66
License: Public Domain
77
*/
88
99
/**
10
- Constructor for a new AJAJ client. ajajOpt may be an optional object
11
- suitable for passing to the WhAjaj.Connector() constructor.
10
+ Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
11
+ object suitable for passing to the WhAjaj.Connector() constructor.
1212
1313
On returning, this.ajaj is-a WhAjaj.Connector instance which can
1414
be used to send requests to the back-end (though the convenience
1515
functions of this class are the preferred way to do it). Clients
16
- are encouraged to use FossilAjaj.sendRequest() (and friends) instead
16
+ are encouraged to use FossilAjaj.sendCommand() (and friends) instead
1717
of the underlying WhAjaj.Connector API, since this class' API
18
- contains SP-specific request-calling functions.
18
+ contains Fossil-specific request-calling handling (e.g. of authentication
19
+ info) whereas WhAjaj is more generic.
1920
*/
2021
function FossilAjaj(ajajOpt)
2122
{
2223
this.ajaj = new WhAjaj.Connector(ajajOpt);
2324
return this;
@@ -44,19 +45,20 @@
4445
on the given arguments and adds this.authToken and a requestId
4546
to it.
4647
*/
4748
FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
4849
var req;
49
-
50
- if(payload || this.authToken) {
50
+ ajajOpt = ajajOpt || {};
51
+ if(payload || this.authToken || ajajOpt.jsonp) {
5152
req = {
52
- payload:payload || undefined,
53
- requestId:this.generateRequestId(),
54
- authToken:this.authToken || undefined
53
+ payload:payload,
54
+ requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55
+ authToken:this.authToken || undefined,
56
+ jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
5557
};
5658
}
57
- ajajOpt = ajajOpt || {};
59
+
5860
if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
5961
if( 0 && this.authToken ) {
6062
if( req ) req.authToken = this.authToken;
6163
else { // GET request: extend ajajOpt.urlParam
6264
var urlArgs = ajajOpt.urlParam;
6365
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -5,19 +5,20 @@
5
6 License: Public Domain
7 */
8
9 /**
10 Constructor for a new AJAJ client. ajajOpt may be an optional object
11 suitable for passing to the WhAjaj.Connector() constructor.
12
13 On returning, this.ajaj is-a WhAjaj.Connector instance which can
14 be used to send requests to the back-end (though the convenience
15 functions of this class are the preferred way to do it). Clients
16 are encouraged to use FossilAjaj.sendRequest() (and friends) instead
17 of the underlying WhAjaj.Connector API, since this class' API
18 contains SP-specific request-calling functions.
 
19 */
20 function FossilAjaj(ajajOpt)
21 {
22 this.ajaj = new WhAjaj.Connector(ajajOpt);
23 return this;
@@ -44,19 +45,20 @@
44 on the given arguments and adds this.authToken and a requestId
45 to it.
46 */
47 FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
48 var req;
49
50 if(payload || this.authToken) {
51 req = {
52 payload:payload || undefined,
53 requestId:this.generateRequestId(),
54 authToken:this.authToken || undefined
 
55 };
56 }
57 ajajOpt = ajajOpt || {};
58 if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
59 if( 0 && this.authToken ) {
60 if( req ) req.authToken = this.authToken;
61 else { // GET request: extend ajajOpt.urlParam
62 var urlArgs = ajajOpt.urlParam;
63
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -5,19 +5,20 @@
5
6 License: Public Domain
7 */
8
9 /**
10 Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
11 object suitable for passing to the WhAjaj.Connector() constructor.
12
13 On returning, this.ajaj is-a WhAjaj.Connector instance which can
14 be used to send requests to the back-end (though the convenience
15 functions of this class are the preferred way to do it). Clients
16 are encouraged to use FossilAjaj.sendCommand() (and friends) instead
17 of the underlying WhAjaj.Connector API, since this class' API
18 contains Fossil-specific request-calling handling (e.g. of authentication
19 info) whereas WhAjaj is more generic.
20 */
21 function FossilAjaj(ajajOpt)
22 {
23 this.ajaj = new WhAjaj.Connector(ajajOpt);
24 return this;
@@ -44,19 +45,20 @@
45 on the given arguments and adds this.authToken and a requestId
46 to it.
47 */
48 FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
49 var req;
50 ajajOpt = ajajOpt || {};
51 if(payload || this.authToken || ajajOpt.jsonp) {
52 req = {
53 payload:payload,
54 requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55 authToken:this.authToken || undefined,
56 jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
57 };
58 }
59
60 if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
61 if( 0 && this.authToken ) {
62 if( req ) req.authToken = this.authToken;
63 else { // GET request: extend ajajOpt.urlParam
64 var urlArgs = ajajOpt.urlParam;
65
+50 -33
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -1,11 +1,15 @@
11
/**
22
This file provides a JS interface into the core functionality of
3
- a whiki CGI back-end.
3
+ JSON-centric back-ends. It sends GET or JSON POST requests to
4
+ a back-end and expects JSON responses. The exact semantics of
5
+ the underlying back-end and overlying front-end are not its concern,
6
+ and it leaves the interpretation of the data up to the client/server
7
+ insofar as possible.
48
59
All functionality is part of a class named WhAjaj, and that class
6
- acts as namespace for the framework.
10
+ acts as namespace for this framework.
711
812
Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
913
1014
License: Public Domain
1115
@@ -94,13 +98,11 @@
9498
On success it returns an Object containing the key/value pairs
9599
parsed from the string.
96100
97101
FIXME: for keys in the form "name[]", build an array of results,
98102
like PHP does.
99
-
100
- FIXME: keys w/o '=', or with no value, "should probably" be
101
- treated as boolean flags with a value of true.
103
+
102104
*/
103105
WhAjaj.processUrlArgs = function(str) {
104106
if( 0 === arguments.length ) {
105107
if( (undefined === typeof window) ||
106108
!window.location ||
@@ -269,12 +271,15 @@
269271
override any client-defined setting.
270272
271273
Must be one of 'GET' or 'POST'. For custom connection
272274
implementation, it may optionally be some
273275
implementation-specified value.
276
+
277
+ Normally the API can derive this value automatically - if the
278
+ request uses JSON data it is POSTed, else it is GETted.
274279
*/
275
- method:'POST',
280
+ method:'GET',
276281
277282
/**
278283
A hint whether to run the operation asynchronously or
279284
not. Not all concrete WhAjaj.Connector.sendImpl()
280285
implementations can support this. Interestingly, at
@@ -291,40 +296,38 @@
291296
implementations can support this.
292297
*/
293298
loginName:undefined,
294299
295300
/**
296
- An HTTP authentication login password for the AJAX
301
+ An HTTP authentication login password for the AJAJ
297302
connection. Not all concrete WhAjaj.Connector.sendImpl()
298303
implementations can support this.
299304
*/
300305
loginPassword:undefined,
301306
302307
/**
303308
A connection timeout, in milliseconds, for establishing
304
- an AJAX connection. Not all concrete
309
+ an AJAJ connection. Not all concrete
305310
WhAjaj.Connector.sendImpl() implementations can support this.
306311
*/
307
- timeout:6000,
308
-
309
- /**
310
- If an AJAX request receives JSON data from the back-end,
311
- that data is passed as a plain Object as the response
312
- parameter. The initiating request object is passed as
313
- the second parameter, but clients can normally ignore it
314
- (only those which need a way to map specific requests to
315
- responses will need it).
316
-
317
- Note that the response might contain error information
318
- which comes from the back-end. The difference between
319
- this error info and the info passed to the onError()
320
- callback is that this data indicates an application-level
321
- error, whereas onError() is used to report
322
- connection-level problems or when the backend produces
323
- non-JSON data (which is unexpected and is as fatal to the
324
- request as a connection error).
325
-
312
+ timeout:10000,
313
+
314
+ /**
315
+ If an AJAJ request receives JSON data from the back-end, that
316
+ data is passed as a plain Object as the response parameter
317
+ (exception: in jsonp mode it is passed a string). The initiating
318
+ request object is passed as the second parameter, but clients
319
+ can normally ignore it (only those which need a way to map
320
+ specific requests to responses will need it).
321
+
322
+ Note that the response might contain error information which
323
+ comes from the back-end. The difference between this error info
324
+ and the info passed to the onError() callback is that this data
325
+ indicates an application-level error, whereas onError() is used
326
+ to report connection-level problems or when the backend produces
327
+ non-JSON data (which, when not in jsonp mode, is unexpected and
328
+ is as fatal to the request as a connection error).
326329
*/
327330
onResponse: function(response, request){},
328331
329332
/**
330333
If an AJAX request fails to establish a connection or it
@@ -340,11 +343,11 @@
340343
implementation with something which integrates into
341344
their application.
342345
*/
343346
onError: function(request, connectOpt)
344347
{
345
- alert('AJAX request failed:\n'
348
+ alert('AJAJ request failed:\n'
346349
+'Connection information:\n'
347350
+JSON.stringify(connectOpt,0,4)
348351
);
349352
},
350353
@@ -360,11 +363,11 @@
360363
propagates the exception back to the caller.
361364
*/
362365
beforeSend: function(request,opt){},
363366
364367
/**
365
- Called after an AJAX connection attempt completes,
368
+ Called after an AJAJ connection attempt completes,
366369
regardless of success or failure. Passed the same
367370
parameters as beforeSend() (see that function for
368371
details).
369372
370373
Here's an example of setting up a visual notification on
@@ -389,11 +392,20 @@
389392
@endcode
390393
391394
Set the beforeSend/afterSend properties to those
392395
functions to enable the notifications by default.
393396
*/
394
- afterSend: function(request,opt){}
397
+ afterSend: function(request,opt){},
398
+
399
+ /**
400
+ If jsonp is a string then the WhAjaj-internal response
401
+ handling code ASSUMES that the response contains a JSONP-style
402
+ construct and eval()s it after afterSend() but before onResponse().
403
+ In this case, onResponse() will get a string value for the response
404
+ instead of a response object parsed from JSON.
405
+ */
406
+ jsonp:undefined
395407
}
396408
};
397409
398410
/**
399411
Tries to find the given key in any of the following, returning
@@ -529,10 +541,15 @@
529541
530542
- Calling opt.onSuccess()
531543
532544
- Calling opt.onError() in several common (potential) error
533545
cases.
546
+
547
+ - If resp is-a String and opt.jsonp then resp is assumed to be
548
+ a JSONP-form construct and is eval()d BEFORE opt.onResponse()
549
+ is called. It is arguable to eval() it first, but the logic
550
+ integrates better with the non-jsonp handler.
534551
535552
The sendImpl() should return immediately after calling this.
536553
537554
The sendImpl() must call only one of onSendSuccess() or
538555
onSendError(). It must call one of them or it must implement
@@ -553,17 +570,17 @@
553570
return false;
554571
}
555572
556573
if( 'string' === typeof resp ) {
557574
try {
558
- resp = JSON.parse(resp);
575
+ resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
559576
} catch(e) {
577
+ opt.errorMessage = e.toString();
560578
onError.apply( opt, [request, opt] );
561579
return;
562580
}
563581
}
564
-
565582
try {
566583
if( WhAjaj.isFunction( opt.onResponse ) ) {
567584
opt.onResponse( resp, request );
568585
}
569586
return true;
@@ -1009,9 +1026,9 @@
10091026
10101027
/**
10111028
sendImpl() holds a concrete back-end connection implementation. It
10121029
can be replaced with a custom implementation if one follows the rules
10131030
described throughout this API. See WhAjaj.Connector.sendImpls for
1014
- the concrete implementatios included with this API.
1031
+ the concrete implementations included with this API.
10151032
*/
10161033
WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
10171034
//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
10181035
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -1,11 +1,15 @@
1 /**
2 This file provides a JS interface into the core functionality of
3 a whiki CGI back-end.
 
 
 
 
4
5 All functionality is part of a class named WhAjaj, and that class
6 acts as namespace for the framework.
7
8 Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
9
10 License: Public Domain
11
@@ -94,13 +98,11 @@
94 On success it returns an Object containing the key/value pairs
95 parsed from the string.
96
97 FIXME: for keys in the form "name[]", build an array of results,
98 like PHP does.
99
100 FIXME: keys w/o '=', or with no value, "should probably" be
101 treated as boolean flags with a value of true.
102 */
103 WhAjaj.processUrlArgs = function(str) {
104 if( 0 === arguments.length ) {
105 if( (undefined === typeof window) ||
106 !window.location ||
@@ -269,12 +271,15 @@
269 override any client-defined setting.
270
271 Must be one of 'GET' or 'POST'. For custom connection
272 implementation, it may optionally be some
273 implementation-specified value.
 
 
 
274 */
275 method:'POST',
276
277 /**
278 A hint whether to run the operation asynchronously or
279 not. Not all concrete WhAjaj.Connector.sendImpl()
280 implementations can support this. Interestingly, at
@@ -291,40 +296,38 @@
291 implementations can support this.
292 */
293 loginName:undefined,
294
295 /**
296 An HTTP authentication login password for the AJAX
297 connection. Not all concrete WhAjaj.Connector.sendImpl()
298 implementations can support this.
299 */
300 loginPassword:undefined,
301
302 /**
303 A connection timeout, in milliseconds, for establishing
304 an AJAX connection. Not all concrete
305 WhAjaj.Connector.sendImpl() implementations can support this.
306 */
307 timeout:6000,
308
309 /**
310 If an AJAX request receives JSON data from the back-end,
311 that data is passed as a plain Object as the response
312 parameter. The initiating request object is passed as
313 the second parameter, but clients can normally ignore it
314 (only those which need a way to map specific requests to
315 responses will need it).
316
317 Note that the response might contain error information
318 which comes from the back-end. The difference between
319 this error info and the info passed to the onError()
320 callback is that this data indicates an application-level
321 error, whereas onError() is used to report
322 connection-level problems or when the backend produces
323 non-JSON data (which is unexpected and is as fatal to the
324 request as a connection error).
325
326 */
327 onResponse: function(response, request){},
328
329 /**
330 If an AJAX request fails to establish a connection or it
@@ -340,11 +343,11 @@
340 implementation with something which integrates into
341 their application.
342 */
343 onError: function(request, connectOpt)
344 {
345 alert('AJAX request failed:\n'
346 +'Connection information:\n'
347 +JSON.stringify(connectOpt,0,4)
348 );
349 },
350
@@ -360,11 +363,11 @@
360 propagates the exception back to the caller.
361 */
362 beforeSend: function(request,opt){},
363
364 /**
365 Called after an AJAX connection attempt completes,
366 regardless of success or failure. Passed the same
367 parameters as beforeSend() (see that function for
368 details).
369
370 Here's an example of setting up a visual notification on
@@ -389,11 +392,20 @@
389 @endcode
390
391 Set the beforeSend/afterSend properties to those
392 functions to enable the notifications by default.
393 */
394 afterSend: function(request,opt){}
 
 
 
 
 
 
 
 
 
395 }
396 };
397
398 /**
399 Tries to find the given key in any of the following, returning
@@ -529,10 +541,15 @@
529
530 - Calling opt.onSuccess()
531
532 - Calling opt.onError() in several common (potential) error
533 cases.
 
 
 
 
 
534
535 The sendImpl() should return immediately after calling this.
536
537 The sendImpl() must call only one of onSendSuccess() or
538 onSendError(). It must call one of them or it must implement
@@ -553,17 +570,17 @@
553 return false;
554 }
555
556 if( 'string' === typeof resp ) {
557 try {
558 resp = JSON.parse(resp);
559 } catch(e) {
 
560 onError.apply( opt, [request, opt] );
561 return;
562 }
563 }
564
565 try {
566 if( WhAjaj.isFunction( opt.onResponse ) ) {
567 opt.onResponse( resp, request );
568 }
569 return true;
@@ -1009,9 +1026,9 @@
1009
1010 /**
1011 sendImpl() holds a concrete back-end connection implementation. It
1012 can be replaced with a custom implementation if one follows the rules
1013 described throughout this API. See WhAjaj.Connector.sendImpls for
1014 the concrete implementatios included with this API.
1015 */
1016 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1017 //WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1018
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -1,11 +1,15 @@
1 /**
2 This file provides a JS interface into the core functionality of
3 JSON-centric back-ends. It sends GET or JSON POST requests to
4 a back-end and expects JSON responses. The exact semantics of
5 the underlying back-end and overlying front-end are not its concern,
6 and it leaves the interpretation of the data up to the client/server
7 insofar as possible.
8
9 All functionality is part of a class named WhAjaj, and that class
10 acts as namespace for this framework.
11
12 Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
13
14 License: Public Domain
15
@@ -94,13 +98,11 @@
98 On success it returns an Object containing the key/value pairs
99 parsed from the string.
100
101 FIXME: for keys in the form "name[]", build an array of results,
102 like PHP does.
103
 
 
104 */
105 WhAjaj.processUrlArgs = function(str) {
106 if( 0 === arguments.length ) {
107 if( (undefined === typeof window) ||
108 !window.location ||
@@ -269,12 +271,15 @@
271 override any client-defined setting.
272
273 Must be one of 'GET' or 'POST'. For custom connection
274 implementation, it may optionally be some
275 implementation-specified value.
276
277 Normally the API can derive this value automatically - if the
278 request uses JSON data it is POSTed, else it is GETted.
279 */
280 method:'GET',
281
282 /**
283 A hint whether to run the operation asynchronously or
284 not. Not all concrete WhAjaj.Connector.sendImpl()
285 implementations can support this. Interestingly, at
@@ -291,40 +296,38 @@
296 implementations can support this.
297 */
298 loginName:undefined,
299
300 /**
301 An HTTP authentication login password for the AJAJ
302 connection. Not all concrete WhAjaj.Connector.sendImpl()
303 implementations can support this.
304 */
305 loginPassword:undefined,
306
307 /**
308 A connection timeout, in milliseconds, for establishing
309 an AJAJ connection. Not all concrete
310 WhAjaj.Connector.sendImpl() implementations can support this.
311 */
312 timeout:10000,
313
314 /**
315 If an AJAJ request receives JSON data from the back-end, that
316 data is passed as a plain Object as the response parameter
317 (exception: in jsonp mode it is passed a string). The initiating
318 request object is passed as the second parameter, but clients
319 can normally ignore it (only those which need a way to map
320 specific requests to responses will need it).
321
322 Note that the response might contain error information which
323 comes from the back-end. The difference between this error info
324 and the info passed to the onError() callback is that this data
325 indicates an application-level error, whereas onError() is used
326 to report connection-level problems or when the backend produces
327 non-JSON data (which, when not in jsonp mode, is unexpected and
328 is as fatal to the request as a connection error).
 
 
329 */
330 onResponse: function(response, request){},
331
332 /**
333 If an AJAX request fails to establish a connection or it
@@ -340,11 +343,11 @@
343 implementation with something which integrates into
344 their application.
345 */
346 onError: function(request, connectOpt)
347 {
348 alert('AJAJ request failed:\n'
349 +'Connection information:\n'
350 +JSON.stringify(connectOpt,0,4)
351 );
352 },
353
@@ -360,11 +363,11 @@
363 propagates the exception back to the caller.
364 */
365 beforeSend: function(request,opt){},
366
367 /**
368 Called after an AJAJ connection attempt completes,
369 regardless of success or failure. Passed the same
370 parameters as beforeSend() (see that function for
371 details).
372
373 Here's an example of setting up a visual notification on
@@ -389,11 +392,20 @@
392 @endcode
393
394 Set the beforeSend/afterSend properties to those
395 functions to enable the notifications by default.
396 */
397 afterSend: function(request,opt){},
398
399 /**
400 If jsonp is a string then the WhAjaj-internal response
401 handling code ASSUMES that the response contains a JSONP-style
402 construct and eval()s it after afterSend() but before onResponse().
403 In this case, onResponse() will get a string value for the response
404 instead of a response object parsed from JSON.
405 */
406 jsonp:undefined
407 }
408 };
409
410 /**
411 Tries to find the given key in any of the following, returning
@@ -529,10 +541,15 @@
541
542 - Calling opt.onSuccess()
543
544 - Calling opt.onError() in several common (potential) error
545 cases.
546
547 - If resp is-a String and opt.jsonp then resp is assumed to be
548 a JSONP-form construct and is eval()d BEFORE opt.onResponse()
549 is called. It is arguable to eval() it first, but the logic
550 integrates better with the non-jsonp handler.
551
552 The sendImpl() should return immediately after calling this.
553
554 The sendImpl() must call only one of onSendSuccess() or
555 onSendError(). It must call one of them or it must implement
@@ -553,17 +570,17 @@
570 return false;
571 }
572
573 if( 'string' === typeof resp ) {
574 try {
575 resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
576 } catch(e) {
577 opt.errorMessage = e.toString();
578 onError.apply( opt, [request, opt] );
579 return;
580 }
581 }
 
582 try {
583 if( WhAjaj.isFunction( opt.onResponse ) ) {
584 opt.onResponse( resp, request );
585 }
586 return true;
@@ -1009,9 +1026,9 @@
1026
1027 /**
1028 sendImpl() holds a concrete back-end connection implementation. It
1029 can be replaced with a custom implementation if one follows the rules
1030 described throughout this API. See WhAjaj.Connector.sendImpls for
1031 the concrete implementations included with this API.
1032 */
1033 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1034 //WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1035
+18 -11
--- src/json.c
+++ src/json.c
@@ -886,20 +886,10 @@
886886
if( !g.isHTTP && g.fullHttpReply ){
887887
/* workaround for server mode, so we see it as CGI mode. */
888888
g.isHTTP = 1;
889889
}
890890
891
- if(!g.json.jsonp && g.json.post.o){
892
- g.json.jsonp = cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")));
893
- }
894
- if( !g.isHTTP ){
895
- g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
896
- if(!g.json.jsonp){
897
- g.json.jsonp = find_option("jsonp",NULL,1);
898
- }
899
- }
900
-
901891
/* FIXME: do some sanity checking on g.json.jsonp and ignore it
902892
if it is not halfway reasonable.
903893
*/
904894
cgi_set_content_type(json_guess_content_type())
905895
/* reminder: must be done after g.json.jsonp is initialized */
@@ -948,10 +938,27 @@
948938
g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
949939
/* g.json.reqPayload.o may legally be NULL, which means only that
950940
g.json.reqPayload.v is-not-a Object.
951941
*/;
952942
}
943
+
944
+ /* Anything which needs json_getenv() and friends should go after
945
+ this point.
946
+ */
947
+
948
+ if(!g.json.jsonp && g.json.post.o){
949
+ g.json.jsonp =
950
+ json_getenv_cstr("jsonp")
951
+ /*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
952
+ ;
953
+ }
954
+ if( !g.isHTTP ){
955
+ g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
956
+ if(!g.json.jsonp){
957
+ g.json.jsonp = find_option("jsonp",NULL,1);
958
+ }
959
+ }
953960
954961
{/* set up JSON output formatting options. */
955962
unsigned char indent = g.isHTTP ? 0 : 1;
956963
char const * indentStr = NULL;
957964
if( g.isHTTP ){
@@ -1677,11 +1684,11 @@
16771684
16781685
/*
16791686
** Impl of /json/rebuild. Requires admin previleges.
16801687
*/
16811688
static cson_value * json_page_rebuild(){
1682
-if( !g.perm.Admin ){
1689
+ if( !g.perm.Admin ){
16831690
g.json.resultCode = FSL_JSON_E_DENIED;
16841691
return NULL;
16851692
}else{
16861693
/* Reminder: the db_xxx() ops "should" fail via
16871694
the fossil core error handlers, which will cause
16881695
--- src/json.c
+++ src/json.c
@@ -886,20 +886,10 @@
886 if( !g.isHTTP && g.fullHttpReply ){
887 /* workaround for server mode, so we see it as CGI mode. */
888 g.isHTTP = 1;
889 }
890
891 if(!g.json.jsonp && g.json.post.o){
892 g.json.jsonp = cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")));
893 }
894 if( !g.isHTTP ){
895 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
896 if(!g.json.jsonp){
897 g.json.jsonp = find_option("jsonp",NULL,1);
898 }
899 }
900
901 /* FIXME: do some sanity checking on g.json.jsonp and ignore it
902 if it is not halfway reasonable.
903 */
904 cgi_set_content_type(json_guess_content_type())
905 /* reminder: must be done after g.json.jsonp is initialized */
@@ -948,10 +938,27 @@
948 g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
949 /* g.json.reqPayload.o may legally be NULL, which means only that
950 g.json.reqPayload.v is-not-a Object.
951 */;
952 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
953
954 {/* set up JSON output formatting options. */
955 unsigned char indent = g.isHTTP ? 0 : 1;
956 char const * indentStr = NULL;
957 if( g.isHTTP ){
@@ -1677,11 +1684,11 @@
1677
1678 /*
1679 ** Impl of /json/rebuild. Requires admin previleges.
1680 */
1681 static cson_value * json_page_rebuild(){
1682 if( !g.perm.Admin ){
1683 g.json.resultCode = FSL_JSON_E_DENIED;
1684 return NULL;
1685 }else{
1686 /* Reminder: the db_xxx() ops "should" fail via
1687 the fossil core error handlers, which will cause
1688
--- src/json.c
+++ src/json.c
@@ -886,20 +886,10 @@
886 if( !g.isHTTP && g.fullHttpReply ){
887 /* workaround for server mode, so we see it as CGI mode. */
888 g.isHTTP = 1;
889 }
890
 
 
 
 
 
 
 
 
 
 
891 /* FIXME: do some sanity checking on g.json.jsonp and ignore it
892 if it is not halfway reasonable.
893 */
894 cgi_set_content_type(json_guess_content_type())
895 /* reminder: must be done after g.json.jsonp is initialized */
@@ -948,10 +938,27 @@
938 g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
939 /* g.json.reqPayload.o may legally be NULL, which means only that
940 g.json.reqPayload.v is-not-a Object.
941 */;
942 }
943
944 /* Anything which needs json_getenv() and friends should go after
945 this point.
946 */
947
948 if(!g.json.jsonp && g.json.post.o){
949 g.json.jsonp =
950 json_getenv_cstr("jsonp")
951 /*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
952 ;
953 }
954 if( !g.isHTTP ){
955 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
956 if(!g.json.jsonp){
957 g.json.jsonp = find_option("jsonp",NULL,1);
958 }
959 }
960
961 {/* set up JSON output formatting options. */
962 unsigned char indent = g.isHTTP ? 0 : 1;
963 char const * indentStr = NULL;
964 if( g.isHTTP ){
@@ -1677,11 +1684,11 @@
1684
1685 /*
1686 ** Impl of /json/rebuild. Requires admin previleges.
1687 */
1688 static cson_value * json_page_rebuild(){
1689 if( !g.perm.Admin ){
1690 g.json.resultCode = FSL_JSON_E_DENIED;
1691 return NULL;
1692 }else{
1693 /* Reminder: the db_xxx() ops "should" fail via
1694 the fossil core error handlers, which will cause
1695

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button