Fossil SCM

Tweaks to the JS API, mainly to simplify a common 2-callbacks-per-event use case. Added missing json2.js.

stephan 2011-10-05 05:41 UTC json-multitag-test
Commit cf10d9d9b9368623660c15828d739034d9fb2b5b
--- ajax/i-test/rhino-test.js
+++ ajax/i-test/rhino-test.js
@@ -1,11 +1,11 @@
11
var TestApp = {
22
serverUrl:
3
- //'http://localhost:8080'
3
+ 'http://localhost:8080'
44
//'http://fjson/cgi-bin/fossil-json.cgi'
55
//'http://192.168.1.62:8080'
6
- 'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
6
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
77
,
88
verbose:true,
99
wiki:{}
1010
};
1111
(function bootstrap() {
@@ -19,30 +19,32 @@
1919
}
2020
WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
2121
TestApp.fossil = new FossilAjaj({
2222
asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
2323
timeout:0,
24
- url:TestApp.serverUrl,
25
- beforeSend:function(req,opt){
26
- if(!TestApp.verbose) return;
27
- print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
28
- if(req) print("Request envelope="+WhAjaj.stringify(req));
29
- },
30
- afterSend:function(req,opt){
31
- //if(!TestApp.verbose) return;
32
- //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
33
- //if(req) print("Request="+WhAjaj.stringify(req));
34
- },
35
- onError:function(req,opt){
36
- if(!TestApp.verbose) return;
37
- print("ERROR: "+WhAjaj.stringify(opt));
38
- },
39
- onResponse:function(resp,req){
40
- if(!TestApp.verbose) return;
41
- print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
42
- }
24
+ url:TestApp.serverUrl
4325
});
26
+ var cb = TestApp.fossil.ajaj.callbacks;
27
+ cb.beforeSend = function(req,opt){
28
+ if(!TestApp.verbose) return;
29
+ print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
30
+ if(req) print("Request envelope="+WhAjaj.stringify(req));
31
+ };
32
+ cb.afterSend = function(req,opt){
33
+ //if(!TestApp.verbose) return;
34
+ //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
35
+ //if(req) print("Request="+WhAjaj.stringify(req));
36
+ };
37
+ cb.onError = function(req,opt){
38
+ if(!TestApp.verbose) return;
39
+ print("ERROR: "+WhAjaj.stringify(opt));
40
+ };
41
+ cb.onResponse = function(resp,req){
42
+ if(!TestApp.verbose) return;
43
+ print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
44
+ };
45
+
4446
})();
4547
4648
/**
4749
Throws an exception of cond is a falsy value.
4850
*/
@@ -115,31 +117,30 @@
115117
}
116118
testHAI.description = 'Get server version info.';
117119
118120
function testIAmNobody(){
119121
TestApp.fossil.whoami('/json/whoami');
120
- assert('nobody' === TestApp.fossil.userName, 'User == nobody.' );
121
- assert(!TestApp.fossil.authToken, 'authToken is not set.' );
122
+ assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
123
+ assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );
122124
123125
}
124126
testIAmNobody.description = 'Ensure that current user is "nobody".';
125127
126128
127129
function testAnonymousLogin(){
128130
TestApp.fossil.login();
129
- assert('string' === typeof TestApp.fossil.authToken, 'authToken = '+TestApp.fossil.authToken);
130
- assert( 'string' === typeof TestApp.fossil.userName, 'User name = '+TestApp.fossil.userName);
131
+ assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
132
+ assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
131133
TestApp.fossil.userName = null;
132134
TestApp.fossil.whoami('/json/whoami');
133
- assert( 'string' === typeof TestApp.fossil.userName, 'User name = '+TestApp.fossil.userName);
135
+ assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
134136
}
135137
testAnonymousLogin.description = 'Perform anonymous login.';
136138
137139
function testAnonWiki(){
138140
TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
139141
beforeSend:function(req,opt){
140
- TestApp.fossil.ajaj.options.beforeSend(req,opt);
141142
assert( req && (req.authToken==TestApp.fossil.authToken), 'Request envelope contains expected authToken.' );
142143
},
143144
onResponse:function(resp,req){
144145
assertResponseOK(resp);
145146
assert( (typeof [] === typeof resp.payload) && resp.payload.length,
146147
--- ajax/i-test/rhino-test.js
+++ ajax/i-test/rhino-test.js
@@ -1,11 +1,11 @@
1 var TestApp = {
2 serverUrl:
3 //'http://localhost:8080'
4 //'http://fjson/cgi-bin/fossil-json.cgi'
5 //'http://192.168.1.62:8080'
6 'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
7 ,
8 verbose:true,
9 wiki:{}
10 };
11 (function bootstrap() {
@@ -19,30 +19,32 @@
19 }
20 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
21 TestApp.fossil = new FossilAjaj({
22 asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
23 timeout:0,
24 url:TestApp.serverUrl,
25 beforeSend:function(req,opt){
26 if(!TestApp.verbose) return;
27 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
28 if(req) print("Request envelope="+WhAjaj.stringify(req));
29 },
30 afterSend:function(req,opt){
31 //if(!TestApp.verbose) return;
32 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
33 //if(req) print("Request="+WhAjaj.stringify(req));
34 },
35 onError:function(req,opt){
36 if(!TestApp.verbose) return;
37 print("ERROR: "+WhAjaj.stringify(opt));
38 },
39 onResponse:function(resp,req){
40 if(!TestApp.verbose) return;
41 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
42 }
43 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44 })();
45
46 /**
47 Throws an exception of cond is a falsy value.
48 */
@@ -115,31 +117,30 @@
115 }
116 testHAI.description = 'Get server version info.';
117
118 function testIAmNobody(){
119 TestApp.fossil.whoami('/json/whoami');
120 assert('nobody' === TestApp.fossil.userName, 'User == nobody.' );
121 assert(!TestApp.fossil.authToken, 'authToken is not set.' );
122
123 }
124 testIAmNobody.description = 'Ensure that current user is "nobody".';
125
126
127 function testAnonymousLogin(){
128 TestApp.fossil.login();
129 assert('string' === typeof TestApp.fossil.authToken, 'authToken = '+TestApp.fossil.authToken);
130 assert( 'string' === typeof TestApp.fossil.userName, 'User name = '+TestApp.fossil.userName);
131 TestApp.fossil.userName = null;
132 TestApp.fossil.whoami('/json/whoami');
133 assert( 'string' === typeof TestApp.fossil.userName, 'User name = '+TestApp.fossil.userName);
134 }
135 testAnonymousLogin.description = 'Perform anonymous login.';
136
137 function testAnonWiki(){
138 TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
139 beforeSend:function(req,opt){
140 TestApp.fossil.ajaj.options.beforeSend(req,opt);
141 assert( req && (req.authToken==TestApp.fossil.authToken), 'Request envelope contains expected authToken.' );
142 },
143 onResponse:function(resp,req){
144 assertResponseOK(resp);
145 assert( (typeof [] === typeof resp.payload) && resp.payload.length,
146
--- ajax/i-test/rhino-test.js
+++ ajax/i-test/rhino-test.js
@@ -1,11 +1,11 @@
1 var TestApp = {
2 serverUrl:
3 'http://localhost:8080'
4 //'http://fjson/cgi-bin/fossil-json.cgi'
5 //'http://192.168.1.62:8080'
6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
7 ,
8 verbose:true,
9 wiki:{}
10 };
11 (function bootstrap() {
@@ -19,30 +19,32 @@
19 }
20 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
21 TestApp.fossil = new FossilAjaj({
22 asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
23 timeout:0,
24 url:TestApp.serverUrl
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25 });
26 var cb = TestApp.fossil.ajaj.callbacks;
27 cb.beforeSend = function(req,opt){
28 if(!TestApp.verbose) return;
29 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
30 if(req) print("Request envelope="+WhAjaj.stringify(req));
31 };
32 cb.afterSend = function(req,opt){
33 //if(!TestApp.verbose) return;
34 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
35 //if(req) print("Request="+WhAjaj.stringify(req));
36 };
37 cb.onError = function(req,opt){
38 if(!TestApp.verbose) return;
39 print("ERROR: "+WhAjaj.stringify(opt));
40 };
41 cb.onResponse = function(resp,req){
42 if(!TestApp.verbose) return;
43 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
44 };
45
46 })();
47
48 /**
49 Throws an exception of cond is a falsy value.
50 */
@@ -115,31 +117,30 @@
117 }
118 testHAI.description = 'Get server version info.';
119
120 function testIAmNobody(){
121 TestApp.fossil.whoami('/json/whoami');
122 assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
123 assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );
124
125 }
126 testIAmNobody.description = 'Ensure that current user is "nobody".';
127
128
129 function testAnonymousLogin(){
130 TestApp.fossil.login();
131 assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
132 assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
133 TestApp.fossil.userName = null;
134 TestApp.fossil.whoami('/json/whoami');
135 assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
136 }
137 testAnonymousLogin.description = 'Perform anonymous login.';
138
139 function testAnonWiki(){
140 TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
141 beforeSend:function(req,opt){
 
142 assert( req && (req.authToken==TestApp.fossil.authToken), 'Request envelope contains expected authToken.' );
143 },
144 onResponse:function(resp,req){
145 assertResponseOK(resp);
146 assert( (typeof [] === typeof resp.payload) && resp.payload.length,
147
+10 -9
--- ajax/index.html
+++ ajax/index.html
@@ -137,12 +137,13 @@
137137
end:function(){
138138
this._tend = (new Date()).getTime();
139139
return this.duration = this._tend - this._tstart;
140140
}
141141
};
142
- var opt = TheApp.cgi.ajaj.options;
143
- opt.beforeSend = function(req,opt) {
142
+
143
+ var ajcb = TheApp.cgi.ajaj.callbacks;
144
+ ajcb.beforeSend = function(req,opt) {
144145
TheApp.timer.start();
145146
var val =
146147
req ?
147148
(('string'===typeof req) ? req : WhAjaj.stringify(req))
148149
: '';
@@ -149,16 +150,16 @@
149150
TheApp.jqe.taResponse.val('');
150151
TheApp.jqe.taRequest.val( val );
151152
TheApp.jqe.taRequestOpt.val( opt ? WhAjaj.stringify(opt) : '' );
152153
TheApp.startAjaxNotif();
153154
};
154
- opt.afterSend = function(req,opt) {
155
+ ajcb.afterSend = function(req,opt) {
155156
TheApp.timer.end();
156157
TheApp.endAjaxNotif();
157158
TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
158159
};
159
- opt.onResponse = function(resp,req) {
160
+ ajcb.onResponse = function(resp,req) {
160161
var val;
161162
if(this.jsonp) return /*was already handled*/;
162163
try {
163164
val = WhAjaj.stringify(resp);
164165
}
@@ -169,23 +170,23 @@
169170
//alert("val="+val);
170171
// FIXME: this.url is hosed for login because of how i overload onResponse()
171172
if( this.url ) TheApp.jqe.textPath.val(this.url.replace(WhAjaj.Connector.options.ajax.url,''));
172173
TheApp.jqe.taResponse.val( val );
173174
};
174
- opt.onError = function(req,opt) {
175
+ ajcb.onError = function(req,opt) {
175176
TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
176177
};
177178
178179
TheApp.cgi.onLogin = function(){
179
- TheApp.jqe.taResponse.val( "Logged in. Auth token =\n"+this.authToken );
180
- TheApp.jqe.currentAuthToken.text("Auth token: "+(this.authToken || "not logged in"));
180
+ TheApp.jqe.taResponse.val( "Logged in:\n"+WhAjaj.stringify(this.auth));
181
+ TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
181182
};
182183
TheApp.cgi.onLogout = function(){
183184
TheApp.jqe.taResponse.val( "Logged out!" );
184
- TheApp.jqe.currentAuthToken.text("");
185
+ TheApp.jqe.currentAuthToken.text("Not logged in");
185186
};
186
-
187
+ TheApp.cgi.whoami();
187188
});
188189
189190
</script>
190191
191192
</head>
192193
--- ajax/index.html
+++ ajax/index.html
@@ -137,12 +137,13 @@
137 end:function(){
138 this._tend = (new Date()).getTime();
139 return this.duration = this._tend - this._tstart;
140 }
141 };
142 var opt = TheApp.cgi.ajaj.options;
143 opt.beforeSend = function(req,opt) {
 
144 TheApp.timer.start();
145 var val =
146 req ?
147 (('string'===typeof req) ? req : WhAjaj.stringify(req))
148 : '';
@@ -149,16 +150,16 @@
149 TheApp.jqe.taResponse.val('');
150 TheApp.jqe.taRequest.val( val );
151 TheApp.jqe.taRequestOpt.val( opt ? WhAjaj.stringify(opt) : '' );
152 TheApp.startAjaxNotif();
153 };
154 opt.afterSend = function(req,opt) {
155 TheApp.timer.end();
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 }
@@ -169,23 +170,23 @@
169 //alert("val="+val);
170 // FIXME: this.url is hosed for login because of how i overload onResponse()
171 if( this.url ) TheApp.jqe.textPath.val(this.url.replace(WhAjaj.Connector.options.ajax.url,''));
172 TheApp.jqe.taResponse.val( val );
173 };
174 opt.onError = function(req,opt) {
175 TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
176 };
177
178 TheApp.cgi.onLogin = function(){
179 TheApp.jqe.taResponse.val( "Logged in. Auth token =\n"+this.authToken );
180 TheApp.jqe.currentAuthToken.text("Auth token: "+(this.authToken || "not logged in"));
181 };
182 TheApp.cgi.onLogout = function(){
183 TheApp.jqe.taResponse.val( "Logged out!" );
184 TheApp.jqe.currentAuthToken.text("");
185 };
186
187 });
188
189 </script>
190
191 </head>
192
--- ajax/index.html
+++ ajax/index.html
@@ -137,12 +137,13 @@
137 end:function(){
138 this._tend = (new Date()).getTime();
139 return this.duration = this._tend - this._tstart;
140 }
141 };
142
143 var ajcb = TheApp.cgi.ajaj.callbacks;
144 ajcb.beforeSend = function(req,opt) {
145 TheApp.timer.start();
146 var val =
147 req ?
148 (('string'===typeof req) ? req : WhAjaj.stringify(req))
149 : '';
@@ -149,16 +150,16 @@
150 TheApp.jqe.taResponse.val('');
151 TheApp.jqe.taRequest.val( val );
152 TheApp.jqe.taRequestOpt.val( opt ? WhAjaj.stringify(opt) : '' );
153 TheApp.startAjaxNotif();
154 };
155 ajcb.afterSend = function(req,opt) {
156 TheApp.timer.end();
157 TheApp.endAjaxNotif();
158 TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
159 };
160 ajcb.onResponse = function(resp,req) {
161 var val;
162 if(this.jsonp) return /*was already handled*/;
163 try {
164 val = WhAjaj.stringify(resp);
165 }
@@ -169,23 +170,23 @@
170 //alert("val="+val);
171 // FIXME: this.url is hosed for login because of how i overload onResponse()
172 if( this.url ) TheApp.jqe.textPath.val(this.url.replace(WhAjaj.Connector.options.ajax.url,''));
173 TheApp.jqe.taResponse.val( val );
174 };
175 ajcb.onError = function(req,opt) {
176 TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
177 };
178
179 TheApp.cgi.onLogin = function(){
180 TheApp.jqe.taResponse.val( "Logged in:\n"+WhAjaj.stringify(this.auth));
181 TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
182 };
183 TheApp.cgi.onLogout = function(){
184 TheApp.jqe.taResponse.val( "Logged out!" );
185 TheApp.jqe.currentAuthToken.text("Not logged in");
186 };
187 TheApp.cgi.whoami();
188 });
189
190 </script>
191
192 </head>
193
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -69,13 +69,17 @@
6969
to sendCommand().
7070
7171
After the response returns, this.authToken will be
7272
set to the response payload.
7373
74
- If name === 'anonymous' (the default if none is passed in) then
75
- this function must make two requests - the first one gets the
76
- captcha code and the second one submits it.
74
+ If name === 'anonymous' (the default if none is passed in) then this
75
+ function ignores the pw argument and must make two requests - the first
76
+ one gets the captcha code and the second one submits it.
77
+ ajajOpt.onResponse() (if set) is only called for the actual login
78
+ response (the 2nd one), as opposed to being called for both requests.
79
+ However, this.ajaj.callbacks.onResponse() _is_ called for both (because
80
+ it happens at a lower level).
7781
7882
If this object has an onLogin() function it is called (with
7983
no arguments) before the onResponse() handler of the login is called
8084
(that is the 2nd request for anonymous logins).
8185
@@ -91,13 +95,14 @@
9195
var oldOnResponse = ajajOpt.onResponse;
9296
ajajOpt.onResponse = function(resp,req) {
9397
var thisOpt = this;
9498
//alert('login response:\n'+WhAjaj.stringify(resp));
9599
if( resp && resp.payload ) {
96
- self.authToken = resp.payload.authToken;
97
- self.userName = resp.payload.name;
98
- self.capabilities = resp.payload.capabilities;
100
+ /*deprecated*/self.authToken = resp.payload.authToken;
101
+ //self.userName = resp.payload.name;
102
+ //self.capabilities = resp.payload.capabilities;
103
+ self.auth = resp.payload;
99104
}
100105
if( WhAjaj.isFunction( self.onLogin ) ){
101106
try{ self.onLogin(); }
102107
catch(e){}
103108
}
@@ -110,13 +115,15 @@
110115
self.sendCommand('/json/login', loginReq, ajajOpt);
111116
}
112117
if( 'anonymous' === name ){
113118
this.sendCommand('/json/anonymousPassword',undefined,{
114119
onResponse:function(resp,req){
120
+/*
115121
if( WhAjaj.isFunction(oldOnResponse) ){
116122
oldOnResponse.apply(this, [resp,req]);
117123
};
124
+*/
118125
if(resp && !resp.resultCode){
119126
//alert("Got PW. Trying to log in..."+WhAjaj.stringify(resp));
120127
loginReq.anonymousSeed = resp.payload.seed;
121128
loginReq.password = resp.payload.password;
122129
doLogin();
@@ -141,11 +148,14 @@
141148
var self = this;
142149
ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
143150
var oldOnResponse = ajajOpt.onResponse;
144151
ajajOpt.onResponse = function(resp,req) {
145152
var thisOpt = this;
146
- if( resp && !resp.payload ) delete self.authToken;
153
+ if( resp && !resp.payload ){
154
+ delete self.authToken;
155
+ delete self.auth;
156
+ }
147157
if( WhAjaj.isFunction( self.onLogout ) ){
148158
try{ self.onLogout(); }
149159
catch(e){}
150160
}
151161
if( WhAjaj.isFunction(oldOnResponse) ) {
@@ -165,29 +175,30 @@
165175
this.sendCommand('/json/HAI', undefined, ajajOpt);
166176
};
167177
168178
169179
/**
170
- Sends a /json/whoami request. Updates this.userName and this.authToken
171
- based on the response, removing them if the response does not contain
180
+ Sends a /json/whoami request. Updates this.auth to contain
181
+ the login info, removing them if the response does not contain
172182
that data.
173183
*/
174184
FossilAjaj.prototype.whoami = function(ajajOpt) {
175185
var self = this;
176186
ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
177187
var oldOnResponse = ajajOpt.onResponse;
178188
ajajOpt.onResponse = function(resp,req) {
179189
var thisOpt = this;
180190
if( resp && resp.payload ){
181
- self.authToken = resp.payload.authToken;
182
- self.userName = resp.payload.name;
183
- }
184
- else {
185
- delete self.userName;
186
- delete self.authToken
191
+ if(!self.auth || (self.auth.authToken!=resp.payload.authToken)){
192
+ self.auth = resp.payload;
193
+ if( WhAjaj.isFunction(self.onLogin) ){
194
+ self.onLogin();
195
+ }
196
+ }
187197
}
198
+ else { delete self.auth; }
188199
if( WhAjaj.isFunction(oldOnResponse) ) {
189200
oldOnResponse.apply(thisOpt,[resp,req]);
190201
}
191202
};
192203
self.sendCommand('/json/whoami', undefined, ajajOpt);
193204
};
194205
195206
ADDED ajax/js/json2.js
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -69,13 +69,17 @@
69 to sendCommand().
70
71 After the response returns, this.authToken will be
72 set to the response payload.
73
74 If name === 'anonymous' (the default if none is passed in) then
75 this function must make two requests - the first one gets the
76 captcha code and the second one submits it.
 
 
 
 
77
78 If this object has an onLogin() function it is called (with
79 no arguments) before the onResponse() handler of the login is called
80 (that is the 2nd request for anonymous logins).
81
@@ -91,13 +95,14 @@
91 var oldOnResponse = ajajOpt.onResponse;
92 ajajOpt.onResponse = function(resp,req) {
93 var thisOpt = this;
94 //alert('login response:\n'+WhAjaj.stringify(resp));
95 if( resp && resp.payload ) {
96 self.authToken = resp.payload.authToken;
97 self.userName = resp.payload.name;
98 self.capabilities = resp.payload.capabilities;
 
99 }
100 if( WhAjaj.isFunction( self.onLogin ) ){
101 try{ self.onLogin(); }
102 catch(e){}
103 }
@@ -110,13 +115,15 @@
110 self.sendCommand('/json/login', loginReq, ajajOpt);
111 }
112 if( 'anonymous' === name ){
113 this.sendCommand('/json/anonymousPassword',undefined,{
114 onResponse:function(resp,req){
 
115 if( WhAjaj.isFunction(oldOnResponse) ){
116 oldOnResponse.apply(this, [resp,req]);
117 };
 
118 if(resp && !resp.resultCode){
119 //alert("Got PW. Trying to log in..."+WhAjaj.stringify(resp));
120 loginReq.anonymousSeed = resp.payload.seed;
121 loginReq.password = resp.payload.password;
122 doLogin();
@@ -141,11 +148,14 @@
141 var self = this;
142 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
143 var oldOnResponse = ajajOpt.onResponse;
144 ajajOpt.onResponse = function(resp,req) {
145 var thisOpt = this;
146 if( resp && !resp.payload ) delete self.authToken;
 
 
 
147 if( WhAjaj.isFunction( self.onLogout ) ){
148 try{ self.onLogout(); }
149 catch(e){}
150 }
151 if( WhAjaj.isFunction(oldOnResponse) ) {
@@ -165,29 +175,30 @@
165 this.sendCommand('/json/HAI', undefined, ajajOpt);
166 };
167
168
169 /**
170 Sends a /json/whoami request. Updates this.userName and this.authToken
171 based on the response, removing them if the response does not contain
172 that data.
173 */
174 FossilAjaj.prototype.whoami = function(ajajOpt) {
175 var self = this;
176 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
177 var oldOnResponse = ajajOpt.onResponse;
178 ajajOpt.onResponse = function(resp,req) {
179 var thisOpt = this;
180 if( resp && resp.payload ){
181 self.authToken = resp.payload.authToken;
182 self.userName = resp.payload.name;
183 }
184 else {
185 delete self.userName;
186 delete self.authToken
187 }
 
188 if( WhAjaj.isFunction(oldOnResponse) ) {
189 oldOnResponse.apply(thisOpt,[resp,req]);
190 }
191 };
192 self.sendCommand('/json/whoami', undefined, ajajOpt);
193 };
194
195 DDED ajax/js/json2.js
--- ajax/js/fossil-ajaj.js
+++ ajax/js/fossil-ajaj.js
@@ -69,13 +69,17 @@
69 to sendCommand().
70
71 After the response returns, this.authToken will be
72 set to the response payload.
73
74 If name === 'anonymous' (the default if none is passed in) then this
75 function ignores the pw argument and must make two requests - the first
76 one gets the captcha code and the second one submits it.
77 ajajOpt.onResponse() (if set) is only called for the actual login
78 response (the 2nd one), as opposed to being called for both requests.
79 However, this.ajaj.callbacks.onResponse() _is_ called for both (because
80 it happens at a lower level).
81
82 If this object has an onLogin() function it is called (with
83 no arguments) before the onResponse() handler of the login is called
84 (that is the 2nd request for anonymous logins).
85
@@ -91,13 +95,14 @@
95 var oldOnResponse = ajajOpt.onResponse;
96 ajajOpt.onResponse = function(resp,req) {
97 var thisOpt = this;
98 //alert('login response:\n'+WhAjaj.stringify(resp));
99 if( resp && resp.payload ) {
100 /*deprecated*/self.authToken = resp.payload.authToken;
101 //self.userName = resp.payload.name;
102 //self.capabilities = resp.payload.capabilities;
103 self.auth = resp.payload;
104 }
105 if( WhAjaj.isFunction( self.onLogin ) ){
106 try{ self.onLogin(); }
107 catch(e){}
108 }
@@ -110,13 +115,15 @@
115 self.sendCommand('/json/login', loginReq, ajajOpt);
116 }
117 if( 'anonymous' === name ){
118 this.sendCommand('/json/anonymousPassword',undefined,{
119 onResponse:function(resp,req){
120 /*
121 if( WhAjaj.isFunction(oldOnResponse) ){
122 oldOnResponse.apply(this, [resp,req]);
123 };
124 */
125 if(resp && !resp.resultCode){
126 //alert("Got PW. Trying to log in..."+WhAjaj.stringify(resp));
127 loginReq.anonymousSeed = resp.payload.seed;
128 loginReq.password = resp.payload.password;
129 doLogin();
@@ -141,11 +148,14 @@
148 var self = this;
149 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
150 var oldOnResponse = ajajOpt.onResponse;
151 ajajOpt.onResponse = function(resp,req) {
152 var thisOpt = this;
153 if( resp && !resp.payload ){
154 delete self.authToken;
155 delete self.auth;
156 }
157 if( WhAjaj.isFunction( self.onLogout ) ){
158 try{ self.onLogout(); }
159 catch(e){}
160 }
161 if( WhAjaj.isFunction(oldOnResponse) ) {
@@ -165,29 +175,30 @@
175 this.sendCommand('/json/HAI', undefined, ajajOpt);
176 };
177
178
179 /**
180 Sends a /json/whoami request. Updates this.auth to contain
181 the login info, removing them if the response does not contain
182 that data.
183 */
184 FossilAjaj.prototype.whoami = function(ajajOpt) {
185 var self = this;
186 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
187 var oldOnResponse = ajajOpt.onResponse;
188 ajajOpt.onResponse = function(resp,req) {
189 var thisOpt = this;
190 if( resp && resp.payload ){
191 if(!self.auth || (self.auth.authToken!=resp.payload.authToken)){
192 self.auth = resp.payload;
193 if( WhAjaj.isFunction(self.onLogin) ){
194 self.onLogin();
195 }
196 }
197 }
198 else { delete self.auth; }
199 if( WhAjaj.isFunction(oldOnResponse) ) {
200 oldOnResponse.apply(thisOpt,[resp,req]);
201 }
202 };
203 self.sendCommand('/json/whoami', undefined, ajajOpt);
204 };
205
206 DDED ajax/js/json2.js
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
1
+/*
2
+ http://www.JSON.org/json2.js
3
+ 2009-06-29
4
+
5
+ Public Domain.
6
+
7
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
+
9
+ See http://www.JSON.org/js.html
10
+
11
+ This file creates a global JSON object containing two methods: stringify
12
+ and parse.
13
+
14
+ JSON.stringify(value, replacer, space)
15
+ value any JavaScript value, usually an object or array.
16
+
17
+ replacer an optional parameter that determines how object
18
+ values are stringified for objects. It can be a
19
+ function or an array of strings.
20
+
21
+ space an optional parameter that specifies the indentation
22
+ of nested structures. If it is omitted, the text will
23
+ be packed without extra whitespace. If it is a number,
24
+ it will specify the number of spaces to indent at each
25
+ level. If it is a string (such as '\t' or '&nbsp;'),
26
+ it contains the characters used to indent at each level.
27
+
28
+ This method produces a JSON text from a JavaScript value.
29
+
30
+ When an object value is found, if the object contains a toJSON
31
+ method, its toJSON method will be called and the result will be
32
+ stringified. A toJSON method does not serialize: it returns the
33
+ value represented by the name/value pair that should be serialized,
34
+ or undefined if nothing should be serialized. The toJSON method
35
+ will be passed the key associated with the value, and this will be
36
+ bound to the object holding the key.
37
+
38
+ For example, this would serialize Dates as ISO strings.
39
+
40
+ Date.prototype.toJSON = function (key) {
41
+ function f(n) {
42
+ // Format integers to have at least two digits.
43
+ return n < 10 ? '0' + n : n;
44
+ }
45
+
46
+ return this.getUTCFullYear() + '-' +
47
+ f(this.getUTCMonth() + 1) + '-' +
48
+ f(this.getUTCDate()) + 'T' +
49
+ f(this.getUTCHours()) + ':' +
50
+ f(this.getUTCMinutes()) + ':' +
51
+ f(this.getUTCSeconds()) + 'Z';
52
+ };
53
+
54
+ You can provide an optional replacer method. It will be passed the
55
+ key and value of each member, with this bound to the containing
56
+ object. The value that is returned from your method will be
57
+ serialized. If your method returns undefined, then the member will
58
+ be excluded from the serialization.
59
+
60
+ If the replacer parameter is an array of strings, then it will be
61
+ used to select the members to be serialized. It filters the results
62
+ such that only members with keys listed in the replacer array are
63
+ stringified.
64
+
65
+ Values that do not have JSON representations, such as undefined or
66
+ functions, will not be serialized. Such values in objects will be
67
+ dropped; in arrays they will be replaced with null. You can use
68
+ a replacer function to replace those with JSON values.
69
+ JSON.stringify(undefined) returns undefined.
70
+
71
+ The optional space parameter produces a stringification of the
72
+ value that is filled with line breaks and indentation to make it
73
+ easier to read.
74
+
75
+ If the space parameter is a non-empty string, then that string will
76
+ be used for indentation. If the space parameter is a number, then
77
+ the indentation will be that many spaces.
78
+
79
+ Example:
80
+
81
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
82
+ // text is '["e",{"pluribus":"unum"}]'
83
+
84
+
85
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87
+
88
+ text = JSON.stringify([new Date()], function (key, value) {
89
+ return this[key] instanceof Date ?
90
+ 'Date(' + this[key] + ')' : value;
91
+ });
92
+ // text is '["Date(---current time---)"]'
93
+
94
+
95
+ JSON.parse(text, reviver)
96
+ This method parses a JSON text to produce an object or array.
97
+ It can throw a SyntaxError exception.
98
+
99
+ The optional reviver parameter is a function that can filter and
100
+ transform the results. It receives each of the keys and values,
101
+ and its return value is used instead of the original value.
102
+ If it returns what it received, then the structure is not modified.
103
+ If it returns undefined then the member is deleted.
104
+
105
+ Example:
106
+
107
+ // Parse the text. Values that look like ISO date strings will
108
+ // be converted to Date objects.
109
+
110
+ myData = JSON.parse(text, function (key, value) {
111
+ var a;
112
+ if (typeof value === 'string') {
113
+ a =
114
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115
+ if (a) {
116
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117
+ +a[5], +a[6]));
118
+ }
119
+ }
120
+ return value;
121
+ });
122
+
123
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124
+ var d;
125
+ if (typeof value === 'string' &&
126
+ value.slice(0, 5) === 'Date(' &&
127
+ value.slice(-1) === ')') {
128
+ d = new Date(value.slice(5, -1));
129
+ if (d) {
130
+ return d;
131
+ }
132
+ }
133
+ return value;
134
+ });
135
+
136
+
137
+ This is a reference implementation. You are free to copy, modify, or
138
+ redistribute.
139
+
140
+ This code should be minified before deployment.
141
+ See http://javascript.crockford.com/jsmin.html
142
+
143
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144
+ NOT CONTROL.
145
+*/
146
+
147
+/*jslint evil: true */
148
+
149
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
153
+ test, toJSON, toString, valueOf
154
+*/
155
+
156
+// Create a JSON object only if one does not already exist. We create the
157
+// methods in a closure to avoid creating global variables.
158
+
159
+var JSON = JSON || {};
160
+
161
+(function () {
162
+
163
+ function f(n) {
164
+ // Format integers to have at least two digits.
165
+ return n < 10 ? '0' + n : n;
166
+ }
167
+
168
+ if (typeof Date.prototype.toJSON !== 'function') {
169
+
170
+ Date.prototype.toJSON = function (key) {
171
+
172
+ return isFinite(this.valueOf()) ?
173
+ this.getUTCFullYear() + '-' +
174
+ f(this.getUTCMonth() + 1) + '-' +
175
+ f(this.getUTCDate()) + 'T' +
176
+ f(this.getUTCHours()) + ':' +
177
+ f(this.getUTCMinutes()) + ':' +
178
+ f(this.getUTCSeconds()) + 'Z' : null;
179
+ };
180
+
181
+ String.prototype.toJSON =
182
+ Number.prototype.toJSON =
183
+ Boolean.prototype.toJSON = function (key) {
184
+ return this.valueOf();
185
+ };
186
+ }
187
+
188
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
189
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
190
+ gap,
191
+ indent,
192
+ meta = { // table of character substitutions
193
+ '\b': '\\b',
194
+ '\t': '\\t',
195
+ '\n': '\\n',
196
+ '\f': '\\f',
197
+ '\r': '\\r',
198
+ '"' : '\\"',
199
+ '\\': '\\\\'
200
+ },
201
+ rep;
202
+
203
+
204
+ function quote(string) {
205
+
206
+// If the string contains no control characters, no quote characters, and no
207
+// backslash characters, then we can safely slap some quotes around it.
208
+// Otherwise we must also replace the offending characters with safe escape
209
+// sequences.
210
+
211
+ escapable.lastIndex = 0;
212
+ return escapable.test(string) ?
213
+ '"' + string.replace(escapable, function (a) {
214
+ var c = meta[a];
215
+ return typeof c === 'string' ? c :
216
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
217
+ }) + '"' :
218
+ '"' + string + '"';
219
+ }
220
+
221
+
222
+ function str(key, holder) {
223
+
224
+// Produce a string from holder[key].
225
+
226
+ var i, // The loop counter.
227
+ k, // The member key.
228
+ v, // The member value.
229
+ length,
230
+ mind = gap,
231
+ partial,
232
+ value = holder[key];
233
+
234
+// If the value has a toJSON method, call it to obtain a replacement value.
235
+
236
+ if (value && typeof value === 'object' &&
237
+ typeof value.toJSON === 'function') {
238
+ value = value.toJSON(key);
239
+ }
240
+
241
+// If we were called with a replacer function, then call the replacer to
242
+// obtain a replacement value.
243
+
244
+ if (typeof rep === 'function') {
245
+ value = rep.call(holder, key, value);
246
+ }
247
+
248
+// What happens next depends on the value's type.
249
+
250
+ switch (typeof value) {
251
+ case 'string':
252
+ return quote(value);
253
+
254
+ case 'number':
255
+
256
+// JSON numbers must be finite. Encode non-finite numbers as null.
257
+
258
+ return isFinite(value) ? String(value) : 'null';
259
+
260
+ case 'boolean':
261
+ case 'null':
262
+
263
+// If the value is a boolean or null, convert it to a string. Note:
264
+// typeof null does not produce 'null'. The case is included here in
265
+// the remote chance that this gets fixed someday.
266
+
267
+ return String(value);
268
+
269
+// If the type is 'object', we might be dealing with an object or an array or
270
+// null.
271
+
272
+ case 'object':
273
+
274
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
275
+// so watch out for that case.
276
+
277
+ if (!value) {
278
+ return 'null';
279
+ }
280
+
281
+// Make an array to hold the partial results of stringifying this object value.
282
+
283
+ gap += indent;
284
+ partial = [];
285
+
286
+// Is the value an array?
287
+
288
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
289
+
290
+// The value is an array. Stringify every element. Use null as a placeholder
291
+// for non-JSON values.
292
+
293
+ length = value.length;
294
+ for (i = 0; i < length; i += 1) {
295
+ partial[i] = str(i, value) || 'null';
296
+ }
297
+
298
+// Join all of the elements together, separated with commas, and wrap them in
299
+// brackets.
300
+
301
+ v = partial.length === 0 ? '[]' :
302
+ gap ? '[\n' + gap +
303
+ partial.join(',\n' + gap) + '\n' +
304
+ mind + ']' :
305
+ '[' + partial.join(',') + ']';
306
+ gap = mind;
307
+ return v;
308
+ }
309
+
310
+// If the replacer is an array, use it to select the members to be stringified.
311
+
312
+ if (rep && typeof rep === 'object') {
313
+ length = rep.length;
314
+ for (i = 0; i < length; i += 1) {
315
+ k = rep[i];
316
+ if (typeof k === 'string') {
317
+ v = str(k, value);
318
+ if (v) {
319
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
320
+ }
321
+ }
322
+ }
323
+ } else {
324
+
325
+// Otherwise, iterate through all of the keys in the object.
326
+
327
+ for (k in value) {
328
+ if (Object.hasOwnProperty.call(value, k)) {
329
+ v = str(k, value);
330
+ if (v) {
331
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+// Join all of the member texts together, separated with commas,
338
+// and wrap them in braces.
339
+
340
+ v = partial.length === 0 ? '{}' :
341
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
342
+ mind + '}' : '{' + partial.join(',') + '}';
343
+ gap = mind;
344
+ return v;
345
+ }
346
+ }
347
+
348
+// If the JSON object does not yet have a stringify method, give it one.
349
+
350
+ if (typeof JSON.stringify !== 'function') {
351
+ JSON.stringify = function (value, replacer, space) {
352
+
353
+// The stringify method takes a value and an optional replacer, and an optional
354
+// space parameter, and returns a JSON text. The replacer can be a function
355
+// that can replace values, or an array of strings that will select the keys.
356
+// A default replacer method can be provided. Use of the space parameter can
357
+// produce text that is more easily readable.
358
+
359
+ var i;
360
+ gap = '';
361
+ indent = '';
362
+
363
+// If the space parameter is a number, make an indent string containing that
364
+// many spaces.
365
+
366
+ if (typeof space === 'number') {
367
+ for (i = 0; i < space; i += 1) {
368
+ indent += ' ';
369
+ }
370
+
371
+// If the space parameter is a string, it will be used as the indent string.
372
+
373
+ } else if (typeof space === 'string') {
374
+ indent = space;
375
+ }
376
+
377
+// If there is a replacer, it must be a function or an array.
378
+// Otherwise, throw an error.
379
+
380
+ rep = replacer;
381
+ if (replacer && typeof replacer !== 'function' &&
382
+ (typeof replacer !== 'object' ||
383
+ typeof replacer.length !== 'number')) {
384
+ throw new Error('JSON.stringify');
385
+ }
386
+
387
+// Make a fake root object containing our value under the key of ''.
388
+// Return the result of stringifying the value.
389
+
390
+ return str('', {'': value});
391
+ };
392
+ }
393
+
394
+
395
+// If the JSON object does not yet have a parse method, give it one.
396
+
397
+ if (typeof JSON.parse !== 'function') {
398
+ JSON.parse = function (text, reviver) {
399
+
400
+// The parse method takes a text and an optional reviver function, and returns
401
+// a JavaScript value if the text is a valid JSON text.
402
+
403
+ var j;
404
+
405
+ function walk(holder, key) {
406
+
407
+// The walk method is used to recursively walk the resulting structure so
408
+// that modifications can be made.
409
+
410
+ var k, v, value = holder[key];
411
+ if (value && typeof value === 'object') {
412
+ for (k in value) {
413
+ if (Object.hasOwnProperty.call(value, k)) {
414
+ v = walk(value, k);
415
+ if (v !== undefined) {
416
+ value[k] = v;
417
+ } else {
418
+ delete value[k];
419
+ }
420
+ }
421
+ }
422
+ }
423
+ return reviver.call(holder, key, value);
424
+ }
425
+
426
+
427
+// Parsing happens in four stages. In the first stage, we replace certain
428
+// Unicode characters with escape sequences. JavaScript handles many characters
429
+// incorrectly, either silently deleting them, or treating them as line endings.
430
+
431
+ cx.lastIndex = 0;
432
+ if (cx.test(text)) {
433
+ text = text.replace(cx, function (a) {
434
+ return '\\u' +
435
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
436
+ });
437
+ }
438
+
439
+// In the second stage, we run the text against regular expressions that look
440
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
441
+// because they can cause invocation, and '=' because it can cause mutation.
442
+// But just to be safe, we want to reject all unexpected forms.
443
+
444
+// We split the second stage into 4 regexp operations in order to work around
445
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
446
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
447
+// replace all simple value tokens with ']' characters. Third, we delete all
448
+// open brackets that follow a colon or comma or that begin the text. Finally,
449
+// we look to see that the remaining characters are only whitespace or ']' or
450
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
451
+
452
+ if (/^[\],:{}\s]*$/.
453
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
454
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
455
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
456
+
457
+// In the third stage we use the eval function to compile the text into a
458
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
459
+// in JavaScript: it can begin a block or an object literal. We wrap the text
460
+// in parens to eliminate the ambiguity.
461
+
462
+ j = eval('(' + text + ')');
463
+
464
+// In the optional fourth stage, we recursively walk the new structure, passing
465
+// each name/value pair to a reviver function for possible transformation.
466
+
467
+ return typeof reviver === 'function' ?
468
+ walk({'': j}, '') : j;
469
+ }
470
+
471
+// If the text is not JSON parseable, then a SyntaxError is thrown.
472
+
473
+ throw new SyntaxError('JSON.parse');
474
+ };
475
+ }
476
+}());
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
1 /*
2 http://www.JSON.org/json2.js
3 2009-06-29
4
5 Public Domain.
6
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
9 See http://www.JSON.org/js.html
10
11 This file creates a global JSON object containing two methods: stringify
12 and parse.
13
14 JSON.stringify(value, replacer, space)
15 value any JavaScript value, usually an object or array.
16
17 replacer an optional parameter that determines how object
18 values are stringified for objects. It can be a
19 function or an array of strings.
20
21 space an optional parameter that specifies the indentation
22 of nested structures. If it is omitted, the text will
23 be packed without extra whitespace. If it is a number,
24 it will specify the number of spaces to indent at each
25 level. If it is a string (such as '\t' or '&nbsp;'),
26 it contains the characters used to indent at each level.
27
28 This method produces a JSON text from a JavaScript value.
29
30 When an object value is found, if the object contains a toJSON
31 method, its toJSON method will be called and the result will be
32 stringified. A toJSON method does not serialize: it returns the
33 value represented by the name/value pair that should be serialized,
34 or undefined if nothing should be serialized. The toJSON method
35 will be passed the key associated with the value, and this will be
36 bound to the object holding the key.
37
38 For example, this would serialize Dates as ISO strings.
39
40 Date.prototype.toJSON = function (key) {
41 function f(n) {
42 // Format integers to have at least two digits.
43 return n < 10 ? '0' + n : n;
44 }
45
46 return this.getUTCFullYear() + '-' +
47 f(this.getUTCMonth() + 1) + '-' +
48 f(this.getUTCDate()) + 'T' +
49 f(this.getUTCHours()) + ':' +
50 f(this.getUTCMinutes()) + ':' +
51 f(this.getUTCSeconds()) + 'Z';
52 };
53
54 You can provide an optional replacer method. It will be passed the
55 key and value of each member, with this bound to the containing
56 object. The value that is returned from your method will be
57 serialized. If your method returns undefined, then the member will
58 be excluded from the serialization.
59
60 If the replacer parameter is an array of strings, then it will be
61 used to select the members to be serialized. It filters the results
62 such that only members with keys listed in the replacer array are
63 stringified.
64
65 Values that do not have JSON representations, such as undefined or
66 functions, will not be serialized. Such values in objects will be
67 dropped; in arrays they will be replaced with null. You can use
68 a replacer function to replace those with JSON values.
69 JSON.stringify(undefined) returns undefined.
70
71 The optional space parameter produces a stringification of the
72 value that is filled with line breaks and indentation to make it
73 easier to read.
74
75 If the space parameter is a non-empty string, then that string will
76 be used for indentation. If the space parameter is a number, then
77 the indentation will be that many spaces.
78
79 Example:
80
81 text = JSON.stringify(['e', {pluribus: 'unum'}]);
82 // text is '["e",{"pluribus":"unum"}]'
83
84
85 text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86 // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87
88 text = JSON.stringify([new Date()], function (key, value) {
89 return this[key] instanceof Date ?
90 'Date(' + this[key] + ')' : value;
91 });
92 // text is '["Date(---current time---)"]'
93
94
95 JSON.parse(text, reviver)
96 This method parses a JSON text to produce an object or array.
97 It can throw a SyntaxError exception.
98
99 The optional reviver parameter is a function that can filter and
100 transform the results. It receives each of the keys and values,
101 and its return value is used instead of the original value.
102 If it returns what it received, then the structure is not modified.
103 If it returns undefined then the member is deleted.
104
105 Example:
106
107 // Parse the text. Values that look like ISO date strings will
108 // be converted to Date objects.
109
110 myData = JSON.parse(text, function (key, value) {
111 var a;
112 if (typeof value === 'string') {
113 a =
114 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115 if (a) {
116 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117 +a[5], +a[6]));
118 }
119 }
120 return value;
121 });
122
123 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124 var d;
125 if (typeof value === 'string' &&
126 value.slice(0, 5) === 'Date(' &&
127 value.slice(-1) === ')') {
128 d = new Date(value.slice(5, -1));
129 if (d) {
130 return d;
131 }
132 }
133 return value;
134 });
135
136
137 This is a reference implementation. You are free to copy, modify, or
138 redistribute.
139
140 This code should be minified before deployment.
141 See http://javascript.crockford.com/jsmin.html
142
143 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144 NOT CONTROL.
145 */
146
147 /*jslint evil: true */
148
149 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150 call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152 lastIndex, length, parse, prototype, push, replace, slice, stringify,
153 test, toJSON, toString, valueOf
154 */
155
156 // Create a JSON object only if one does not already exist. We create the
157 // methods in a closure to avoid creating global variables.
158
159 var JSON = JSON || {};
160
161 (function () {
162
163 function f(n) {
164 // Format integers to have at least two digits.
165 return n < 10 ? '0' + n : n;
166 }
167
168 if (typeof Date.prototype.toJSON !== 'function') {
169
170 Date.prototype.toJSON = function (key) {
171
172 return isFinite(this.valueOf()) ?
173 this.getUTCFullYear() + '-' +
174 f(this.getUTCMonth() + 1) + '-' +
175 f(this.getUTCDate()) + 'T' +
176 f(this.getUTCHours()) + ':' +
177 f(this.getUTCMinutes()) + ':' +
178 f(this.getUTCSeconds()) + 'Z' : null;
179 };
180
181 String.prototype.toJSON =
182 Number.prototype.toJSON =
183 Boolean.prototype.toJSON = function (key) {
184 return this.valueOf();
185 };
186 }
187
188 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
189 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
190 gap,
191 indent,
192 meta = { // table of character substitutions
193 '\b': '\\b',
194 '\t': '\\t',
195 '\n': '\\n',
196 '\f': '\\f',
197 '\r': '\\r',
198 '"' : '\\"',
199 '\\': '\\\\'
200 },
201 rep;
202
203
204 function quote(string) {
205
206 // If the string contains no control characters, no quote characters, and no
207 // backslash characters, then we can safely slap some quotes around it.
208 // Otherwise we must also replace the offending characters with safe escape
209 // sequences.
210
211 escapable.lastIndex = 0;
212 return escapable.test(string) ?
213 '"' + string.replace(escapable, function (a) {
214 var c = meta[a];
215 return typeof c === 'string' ? c :
216 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
217 }) + '"' :
218 '"' + string + '"';
219 }
220
221
222 function str(key, holder) {
223
224 // Produce a string from holder[key].
225
226 var i, // The loop counter.
227 k, // The member key.
228 v, // The member value.
229 length,
230 mind = gap,
231 partial,
232 value = holder[key];
233
234 // If the value has a toJSON method, call it to obtain a replacement value.
235
236 if (value && typeof value === 'object' &&
237 typeof value.toJSON === 'function') {
238 value = value.toJSON(key);
239 }
240
241 // If we were called with a replacer function, then call the replacer to
242 // obtain a replacement value.
243
244 if (typeof rep === 'function') {
245 value = rep.call(holder, key, value);
246 }
247
248 // What happens next depends on the value's type.
249
250 switch (typeof value) {
251 case 'string':
252 return quote(value);
253
254 case 'number':
255
256 // JSON numbers must be finite. Encode non-finite numbers as null.
257
258 return isFinite(value) ? String(value) : 'null';
259
260 case 'boolean':
261 case 'null':
262
263 // If the value is a boolean or null, convert it to a string. Note:
264 // typeof null does not produce 'null'. The case is included here in
265 // the remote chance that this gets fixed someday.
266
267 return String(value);
268
269 // If the type is 'object', we might be dealing with an object or an array or
270 // null.
271
272 case 'object':
273
274 // Due to a specification blunder in ECMAScript, typeof null is 'object',
275 // so watch out for that case.
276
277 if (!value) {
278 return 'null';
279 }
280
281 // Make an array to hold the partial results of stringifying this object value.
282
283 gap += indent;
284 partial = [];
285
286 // Is the value an array?
287
288 if (Object.prototype.toString.apply(value) === '[object Array]') {
289
290 // The value is an array. Stringify every element. Use null as a placeholder
291 // for non-JSON values.
292
293 length = value.length;
294 for (i = 0; i < length; i += 1) {
295 partial[i] = str(i, value) || 'null';
296 }
297
298 // Join all of the elements together, separated with commas, and wrap them in
299 // brackets.
300
301 v = partial.length === 0 ? '[]' :
302 gap ? '[\n' + gap +
303 partial.join(',\n' + gap) + '\n' +
304 mind + ']' :
305 '[' + partial.join(',') + ']';
306 gap = mind;
307 return v;
308 }
309
310 // If the replacer is an array, use it to select the members to be stringified.
311
312 if (rep && typeof rep === 'object') {
313 length = rep.length;
314 for (i = 0; i < length; i += 1) {
315 k = rep[i];
316 if (typeof k === 'string') {
317 v = str(k, value);
318 if (v) {
319 partial.push(quote(k) + (gap ? ': ' : ':') + v);
320 }
321 }
322 }
323 } else {
324
325 // Otherwise, iterate through all of the keys in the object.
326
327 for (k in value) {
328 if (Object.hasOwnProperty.call(value, k)) {
329 v = str(k, value);
330 if (v) {
331 partial.push(quote(k) + (gap ? ': ' : ':') + v);
332 }
333 }
334 }
335 }
336
337 // Join all of the member texts together, separated with commas,
338 // and wrap them in braces.
339
340 v = partial.length === 0 ? '{}' :
341 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
342 mind + '}' : '{' + partial.join(',') + '}';
343 gap = mind;
344 return v;
345 }
346 }
347
348 // If the JSON object does not yet have a stringify method, give it one.
349
350 if (typeof JSON.stringify !== 'function') {
351 JSON.stringify = function (value, replacer, space) {
352
353 // The stringify method takes a value and an optional replacer, and an optional
354 // space parameter, and returns a JSON text. The replacer can be a function
355 // that can replace values, or an array of strings that will select the keys.
356 // A default replacer method can be provided. Use of the space parameter can
357 // produce text that is more easily readable.
358
359 var i;
360 gap = '';
361 indent = '';
362
363 // If the space parameter is a number, make an indent string containing that
364 // many spaces.
365
366 if (typeof space === 'number') {
367 for (i = 0; i < space; i += 1) {
368 indent += ' ';
369 }
370
371 // If the space parameter is a string, it will be used as the indent string.
372
373 } else if (typeof space === 'string') {
374 indent = space;
375 }
376
377 // If there is a replacer, it must be a function or an array.
378 // Otherwise, throw an error.
379
380 rep = replacer;
381 if (replacer && typeof replacer !== 'function' &&
382 (typeof replacer !== 'object' ||
383 typeof replacer.length !== 'number')) {
384 throw new Error('JSON.stringify');
385 }
386
387 // Make a fake root object containing our value under the key of ''.
388 // Return the result of stringifying the value.
389
390 return str('', {'': value});
391 };
392 }
393
394
395 // If the JSON object does not yet have a parse method, give it one.
396
397 if (typeof JSON.parse !== 'function') {
398 JSON.parse = function (text, reviver) {
399
400 // The parse method takes a text and an optional reviver function, and returns
401 // a JavaScript value if the text is a valid JSON text.
402
403 var j;
404
405 function walk(holder, key) {
406
407 // The walk method is used to recursively walk the resulting structure so
408 // that modifications can be made.
409
410 var k, v, value = holder[key];
411 if (value && typeof value === 'object') {
412 for (k in value) {
413 if (Object.hasOwnProperty.call(value, k)) {
414 v = walk(value, k);
415 if (v !== undefined) {
416 value[k] = v;
417 } else {
418 delete value[k];
419 }
420 }
421 }
422 }
423 return reviver.call(holder, key, value);
424 }
425
426
427 // Parsing happens in four stages. In the first stage, we replace certain
428 // Unicode characters with escape sequences. JavaScript handles many characters
429 // incorrectly, either silently deleting them, or treating them as line endings.
430
431 cx.lastIndex = 0;
432 if (cx.test(text)) {
433 text = text.replace(cx, function (a) {
434 return '\\u' +
435 ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
436 });
437 }
438
439 // In the second stage, we run the text against regular expressions that look
440 // for non-JSON patterns. We are especially concerned with '()' and 'new'
441 // because they can cause invocation, and '=' because it can cause mutation.
442 // But just to be safe, we want to reject all unexpected forms.
443
444 // We split the second stage into 4 regexp operations in order to work around
445 // crippling inefficiencies in IE's and Safari's regexp engines. First we
446 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
447 // replace all simple value tokens with ']' characters. Third, we delete all
448 // open brackets that follow a colon or comma or that begin the text. Finally,
449 // we look to see that the remaining characters are only whitespace or ']' or
450 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
451
452 if (/^[\],:{}\s]*$/.
453 test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
454 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
455 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
456
457 // In the third stage we use the eval function to compile the text into a
458 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
459 // in JavaScript: it can begin a block or an object literal. We wrap the text
460 // in parens to eliminate the ambiguity.
461
462 j = eval('(' + text + ')');
463
464 // In the optional fourth stage, we recursively walk the new structure, passing
465 // each name/value pair to a reviver function for possible transformation.
466
467 return typeof reviver === 'function' ?
468 walk({'': j}, '') : j;
469 }
470
471 // If the text is not JSON parseable, then a SyntaxError is thrown.
472
473 throw new SyntaxError('JSON.parse');
474 };
475 }
476 }());
+56 -28
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -204,13 +204,15 @@
204204
can bend those conventions a bit (where it won't break my other
205205
apps unduly).
206206
*/
207207
WhAjaj.Connector = function(opt)
208208
{
209
- this.options = WhAjaj.isObject(opt) ? opt : {};
209
+ if(WhAjaj.isObject(opt)) this.options = opt;
210210
//TODO?: this.$cache = {};
211211
};
212
+WhAjaj.Connector.prototype.callbacks = {};
213
+WhAjaj.Connector.prototype.options = {};
212214
213215
/**
214216
The core options used by WhAjaj.Connector instances for performing
215217
network operations. These options can (and some _should_)
216218
be changed by a client application. They can also be changed
@@ -556,44 +558,60 @@
556558
its own response/error handling, which is not recommended
557559
because getting the documented semantics of the
558560
onError/onResponse/afterSend handling correct can be tedious.
559561
*/
560562
onSendSuccess:function(request,resp,opt) {
563
+ var cb = this.callbacks || {};
564
+ if( WhAjaj.isFunction(cb.afterSend) ) {
565
+ try {cb.afterSend( request, opt );}
566
+ catch(e){}
567
+ }
561568
if( WhAjaj.isFunction(opt.afterSend) ) {
562
- try {
563
- opt.afterSend( request, opt );
564
- } catch(e){}
569
+ try {opt.afterSend( request, opt );}
570
+ catch(e){}
565571
}
566
- var onError = WhAjaj.isFunction(opt.onError) ? opt.onError : function(){};
572
+ function doErr(){
573
+ if( WhAjaj.isFunction(cb.onError) ) {
574
+ try {cb.onError( request, opt );}
575
+ catch(e){}
576
+ }
577
+ if( WhAjaj.isFunction(opt.onError) ) {
578
+ try {opt.onError( request, opt );}
579
+ catch(e){}
580
+ }
581
+ }
567582
if( ! resp ) {
568583
opt.errorMessage = "Sending of request succeeded but returned no data!";
569
- onError.apply( opt, [request, opt] );
584
+ doErr();
570585
return false;
571586
}
572587
573588
if( 'string' === typeof resp ) {
574589
try {
575590
resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
576591
} catch(e) {
577592
opt.errorMessage = e.toString();
578
- onError.apply( opt, [request, opt] );
593
+ doErr();
579594
return;
580595
}
581596
}
582597
try {
598
+ if( WhAjaj.isFunction( cb.onResponse ) ) {
599
+ cb.onResponse( resp, request );
600
+ }
583601
if( WhAjaj.isFunction( opt.onResponse ) ) {
584602
opt.onResponse( resp, request );
585603
}
586604
return true;
587605
}
588606
catch(e) {
589607
opt.errorMessage = "Exception while handling inbound JSON response:\n"
590608
+ e
591
- +"\nOriginal response data:\n"+JSON.stringify(resp)
609
+ +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
592610
;
593611
;
594
- onError.apply( opt, [request, opt] );
612
+ doErr();
595613
return false;
596614
}
597615
},
598616
/**
599617
Should be called by sendImpl() implementations after a response
@@ -613,20 +631,25 @@
613631
614632
The sendImpl() should return immediately after calling this. The
615633
return value from this function is unspecified.
616634
*/
617635
onSendError: function(request,opt) {
636
+ var cb = this.callbacks || {};
637
+ if( WhAjaj.isFunction(cb.afterSend) ) {
638
+ try {cb.afterSend( request, opt );}
639
+ catch(e){}
640
+ }
618641
if( WhAjaj.isFunction(opt.afterSend) ) {
619
- try {
620
- opt.afterSend( request, opt );
621
- }
642
+ try {opt.afterSend( request, opt );}
622643
catch(e){}
644
+ }
645
+ if( WhAjaj.isFunction( cb.onError ) ) {
646
+ try {cb.onError( request, opt );}
647
+ catch(e) {/*ignore*/}
623648
}
624649
if( WhAjaj.isFunction( opt.onError ) ) {
625
- try {
626
- opt.onError( request, opt );
627
- }
650
+ try {opt.onError( request, opt );}
628651
catch(e) {/*ignore*/}
629652
}
630653
}
631654
};
632655
@@ -694,10 +717,11 @@
694717
var startTime = (new Date()).getTime();
695718
var timeout = args.timeout || 10000/*arbitrary!*/;
696719
var hitTimeout = false;
697720
var done = false;
698721
var tmid /* setTimeout() ID */;
722
+ var whself = this;
699723
//if( json ) json = json.replace(/ö/g,"\\u00f6") /* ONLY FOR A SPECIFIC TEST */;
700724
//alert( 'json=\n'+json );
701725
function handleTimeout()
702726
{
703727
hitTimeout = true;
@@ -705,11 +729,11 @@
705729
{
706730
var now = (new Date()).getTime();
707731
try { xhr.abort(); } catch(e) {/*ignore*/}
708732
// see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
709733
args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
710
- WhAjaj.Connector.sendHelper.onSendError( request, args );
734
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
711735
}
712736
return;
713737
}
714738
function onStateChange()
715739
{ // reminder to self: apparently 'this' is-not-a XHR :/
@@ -726,11 +750,11 @@
726750
clearTimeout( tmid );
727751
tmid = null;
728752
}
729753
if( (xhr.status >= 200) && (xhr.status < 300) )
730754
{
731
- WhAjaj.Connector.sendHelper.onSendSuccess( request, xhr.responseText, args );
755
+ WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
732756
return;
733757
}
734758
else
735759
{
736760
if( undefined === args.errorMessage )
@@ -737,11 +761,11 @@
737761
{
738762
args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
739763
+"["+args.url+"]: "
740764
+"Status text=["+xhr.statusText+"]"
741765
;
742
- WhAjaj.Connector.sendHelper.onSendError( request, args );
766
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
743767
}
744768
else { /*maybe it was was set by the timeout handler. */ }
745769
return;
746770
}
747771
}
@@ -785,11 +809,11 @@
785809
return xhr;
786810
}
787811
catch(e)
788812
{
789813
args.errorMessage = e.toString();
790
- WhAjaj.Connector.sendHelper.onSendError( request, args );
814
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
791815
return undefined;
792816
}
793817
}/*XMLHttpRequest()*/,
794818
/**
795819
This is a concrete implementation of
@@ -815,17 +839,18 @@
815839
WhAjaj.Connector.
816840
*/
817841
jQuery:function(request,args)
818842
{
819843
var data = request || undefined;
844
+ var whself = this;
820845
if( data ) {
821846
if('string'!==typeof data) {
822847
try {
823848
data = JSON.stringify(data);
824849
}
825850
catch(e) {
826
- WhAjaj.Connector.sendHelper.onSendError( request, args );
851
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
827852
return;
828853
}
829854
}
830855
}
831856
var ajopt = {
@@ -841,15 +866,15 @@
841866
//this === the options for this ajax request
842867
args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
843868
+"Status text=["+textStatus+"]"
844869
+(errorThrown ? ("Error=["+errorThrown+"]") : "")
845870
;
846
- WhAjaj.Connector.sendHelper.onSendError( request, args );
871
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
847872
},
848873
success: function(data)
849874
{
850
- WhAjaj.Connector.sendHelper.onSendSuccess( request, data, args );
875
+ WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
851876
},
852877
/* Set dataType=text instead of json for deeply archaic reasons which
853878
might no longer apply.
854879
*/
855880
dataType: 'text'
@@ -868,11 +893,11 @@
868893
return xhr;
869894
}
870895
catch(e)
871896
{
872897
args.errorMessage = e.toString();
873
- WhAjaj.Connector.sendHelper.onSendError( request, args );
898
+ WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
874899
return undefined;
875900
}
876901
}/*jQuery()*/,
877902
/**
878903
This is a concrete implementation of
@@ -895,11 +920,11 @@
895920
if('string'!==typeof data) {
896921
try {
897922
data = JSON.stringify(data);
898923
}
899924
catch(e) {
900
- WhAjaj.Connector.sendHelper.onSendError( request, args );
925
+ WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
901926
return;
902927
}
903928
}
904929
}
905930
var url;
@@ -961,18 +986,18 @@
961986
json.push(line);
962987
}
963988
setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
964989
}catch(e){
965990
args.errorMessage = e.toString();
966
- WhAjaj.Connector.sendHelper.onSendError( request, args );
991
+ WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
967992
return undefined;
968993
}
969994
try { if(wr) wr.close(); } catch(e) { /*ignore*/}
970995
try { if(rd) rd.close(); } catch(e) { /*ignore*/}
971996
json = json.join('');
972997
//print("READ IN JSON: "+json);
973
- WhAjaj.Connector.sendHelper.onSendSuccess( request, json, args );
998
+ WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
974999
}/*rhino()*/
9751000
};
9761001
9771002
/**
9781003
An internal function which takes an object containing properties
@@ -1110,12 +1135,15 @@
11101135
(de)JSON-izing fails.
11111136
*/
11121137
var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
11131138
norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
11141139
if( ! request ) norm.method = 'GET';
1115
- if( WhAjaj.isFunction(norm.beforeSend) )
1116
- {
1140
+ var cb = this.callbacks || {};
1141
+ if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
1142
+ this.callbacks.beforeSend( request, norm );
1143
+ }
1144
+ if( WhAjaj.isFunction(norm.beforeSend) ){
11171145
norm.beforeSend( request, norm );
11181146
}
11191147
//alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
11201148
try { this.sendImpl( request, norm ); }
11211149
catch(e) { ex = e; }
11221150
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -204,13 +204,15 @@
204 can bend those conventions a bit (where it won't break my other
205 apps unduly).
206 */
207 WhAjaj.Connector = function(opt)
208 {
209 this.options = WhAjaj.isObject(opt) ? opt : {};
210 //TODO?: this.$cache = {};
211 };
 
 
212
213 /**
214 The core options used by WhAjaj.Connector instances for performing
215 network operations. These options can (and some _should_)
216 be changed by a client application. They can also be changed
@@ -556,44 +558,60 @@
556 its own response/error handling, which is not recommended
557 because getting the documented semantics of the
558 onError/onResponse/afterSend handling correct can be tedious.
559 */
560 onSendSuccess:function(request,resp,opt) {
 
 
 
 
 
561 if( WhAjaj.isFunction(opt.afterSend) ) {
562 try {
563 opt.afterSend( request, opt );
564 } catch(e){}
565 }
566 var onError = WhAjaj.isFunction(opt.onError) ? opt.onError : function(){};
 
 
 
 
 
 
 
 
 
567 if( ! resp ) {
568 opt.errorMessage = "Sending of request succeeded but returned no data!";
569 onError.apply( opt, [request, opt] );
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;
587 }
588 catch(e) {
589 opt.errorMessage = "Exception while handling inbound JSON response:\n"
590 + e
591 +"\nOriginal response data:\n"+JSON.stringify(resp)
592 ;
593 ;
594 onError.apply( opt, [request, opt] );
595 return false;
596 }
597 },
598 /**
599 Should be called by sendImpl() implementations after a response
@@ -613,20 +631,25 @@
613
614 The sendImpl() should return immediately after calling this. The
615 return value from this function is unspecified.
616 */
617 onSendError: function(request,opt) {
 
 
 
 
 
618 if( WhAjaj.isFunction(opt.afterSend) ) {
619 try {
620 opt.afterSend( request, opt );
621 }
622 catch(e){}
 
 
 
 
623 }
624 if( WhAjaj.isFunction( opt.onError ) ) {
625 try {
626 opt.onError( request, opt );
627 }
628 catch(e) {/*ignore*/}
629 }
630 }
631 };
632
@@ -694,10 +717,11 @@
694 var startTime = (new Date()).getTime();
695 var timeout = args.timeout || 10000/*arbitrary!*/;
696 var hitTimeout = false;
697 var done = false;
698 var tmid /* setTimeout() ID */;
 
699 //if( json ) json = json.replace(/ö/g,"\\u00f6") /* ONLY FOR A SPECIFIC TEST */;
700 //alert( 'json=\n'+json );
701 function handleTimeout()
702 {
703 hitTimeout = true;
@@ -705,11 +729,11 @@
705 {
706 var now = (new Date()).getTime();
707 try { xhr.abort(); } catch(e) {/*ignore*/}
708 // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
709 args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
710 WhAjaj.Connector.sendHelper.onSendError( request, args );
711 }
712 return;
713 }
714 function onStateChange()
715 { // reminder to self: apparently 'this' is-not-a XHR :/
@@ -726,11 +750,11 @@
726 clearTimeout( tmid );
727 tmid = null;
728 }
729 if( (xhr.status >= 200) && (xhr.status < 300) )
730 {
731 WhAjaj.Connector.sendHelper.onSendSuccess( request, xhr.responseText, args );
732 return;
733 }
734 else
735 {
736 if( undefined === args.errorMessage )
@@ -737,11 +761,11 @@
737 {
738 args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
739 +"["+args.url+"]: "
740 +"Status text=["+xhr.statusText+"]"
741 ;
742 WhAjaj.Connector.sendHelper.onSendError( request, args );
743 }
744 else { /*maybe it was was set by the timeout handler. */ }
745 return;
746 }
747 }
@@ -785,11 +809,11 @@
785 return xhr;
786 }
787 catch(e)
788 {
789 args.errorMessage = e.toString();
790 WhAjaj.Connector.sendHelper.onSendError( request, args );
791 return undefined;
792 }
793 }/*XMLHttpRequest()*/,
794 /**
795 This is a concrete implementation of
@@ -815,17 +839,18 @@
815 WhAjaj.Connector.
816 */
817 jQuery:function(request,args)
818 {
819 var data = request || undefined;
 
820 if( data ) {
821 if('string'!==typeof data) {
822 try {
823 data = JSON.stringify(data);
824 }
825 catch(e) {
826 WhAjaj.Connector.sendHelper.onSendError( request, args );
827 return;
828 }
829 }
830 }
831 var ajopt = {
@@ -841,15 +866,15 @@
841 //this === the options for this ajax request
842 args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
843 +"Status text=["+textStatus+"]"
844 +(errorThrown ? ("Error=["+errorThrown+"]") : "")
845 ;
846 WhAjaj.Connector.sendHelper.onSendError( request, args );
847 },
848 success: function(data)
849 {
850 WhAjaj.Connector.sendHelper.onSendSuccess( request, data, args );
851 },
852 /* Set dataType=text instead of json for deeply archaic reasons which
853 might no longer apply.
854 */
855 dataType: 'text'
@@ -868,11 +893,11 @@
868 return xhr;
869 }
870 catch(e)
871 {
872 args.errorMessage = e.toString();
873 WhAjaj.Connector.sendHelper.onSendError( request, args );
874 return undefined;
875 }
876 }/*jQuery()*/,
877 /**
878 This is a concrete implementation of
@@ -895,11 +920,11 @@
895 if('string'!==typeof data) {
896 try {
897 data = JSON.stringify(data);
898 }
899 catch(e) {
900 WhAjaj.Connector.sendHelper.onSendError( request, args );
901 return;
902 }
903 }
904 }
905 var url;
@@ -961,18 +986,18 @@
961 json.push(line);
962 }
963 setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
964 }catch(e){
965 args.errorMessage = e.toString();
966 WhAjaj.Connector.sendHelper.onSendError( request, args );
967 return undefined;
968 }
969 try { if(wr) wr.close(); } catch(e) { /*ignore*/}
970 try { if(rd) rd.close(); } catch(e) { /*ignore*/}
971 json = json.join('');
972 //print("READ IN JSON: "+json);
973 WhAjaj.Connector.sendHelper.onSendSuccess( request, json, args );
974 }/*rhino()*/
975 };
976
977 /**
978 An internal function which takes an object containing properties
@@ -1110,12 +1135,15 @@
1110 (de)JSON-izing fails.
1111 */
1112 var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
1113 norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
1114 if( ! request ) norm.method = 'GET';
1115 if( WhAjaj.isFunction(norm.beforeSend) )
1116 {
 
 
 
1117 norm.beforeSend( request, norm );
1118 }
1119 //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
1120 try { this.sendImpl( request, norm ); }
1121 catch(e) { ex = e; }
1122
--- ajax/js/whajaj.js
+++ ajax/js/whajaj.js
@@ -204,13 +204,15 @@
204 can bend those conventions a bit (where it won't break my other
205 apps unduly).
206 */
207 WhAjaj.Connector = function(opt)
208 {
209 if(WhAjaj.isObject(opt)) this.options = opt;
210 //TODO?: this.$cache = {};
211 };
212 WhAjaj.Connector.prototype.callbacks = {};
213 WhAjaj.Connector.prototype.options = {};
214
215 /**
216 The core options used by WhAjaj.Connector instances for performing
217 network operations. These options can (and some _should_)
218 be changed by a client application. They can also be changed
@@ -556,44 +558,60 @@
558 its own response/error handling, which is not recommended
559 because getting the documented semantics of the
560 onError/onResponse/afterSend handling correct can be tedious.
561 */
562 onSendSuccess:function(request,resp,opt) {
563 var cb = this.callbacks || {};
564 if( WhAjaj.isFunction(cb.afterSend) ) {
565 try {cb.afterSend( request, opt );}
566 catch(e){}
567 }
568 if( WhAjaj.isFunction(opt.afterSend) ) {
569 try {opt.afterSend( request, opt );}
570 catch(e){}
 
571 }
572 function doErr(){
573 if( WhAjaj.isFunction(cb.onError) ) {
574 try {cb.onError( request, opt );}
575 catch(e){}
576 }
577 if( WhAjaj.isFunction(opt.onError) ) {
578 try {opt.onError( request, opt );}
579 catch(e){}
580 }
581 }
582 if( ! resp ) {
583 opt.errorMessage = "Sending of request succeeded but returned no data!";
584 doErr();
585 return false;
586 }
587
588 if( 'string' === typeof resp ) {
589 try {
590 resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
591 } catch(e) {
592 opt.errorMessage = e.toString();
593 doErr();
594 return;
595 }
596 }
597 try {
598 if( WhAjaj.isFunction( cb.onResponse ) ) {
599 cb.onResponse( resp, request );
600 }
601 if( WhAjaj.isFunction( opt.onResponse ) ) {
602 opt.onResponse( resp, request );
603 }
604 return true;
605 }
606 catch(e) {
607 opt.errorMessage = "Exception while handling inbound JSON response:\n"
608 + e
609 +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
610 ;
611 ;
612 doErr();
613 return false;
614 }
615 },
616 /**
617 Should be called by sendImpl() implementations after a response
@@ -613,20 +631,25 @@
631
632 The sendImpl() should return immediately after calling this. The
633 return value from this function is unspecified.
634 */
635 onSendError: function(request,opt) {
636 var cb = this.callbacks || {};
637 if( WhAjaj.isFunction(cb.afterSend) ) {
638 try {cb.afterSend( request, opt );}
639 catch(e){}
640 }
641 if( WhAjaj.isFunction(opt.afterSend) ) {
642 try {opt.afterSend( request, opt );}
 
 
643 catch(e){}
644 }
645 if( WhAjaj.isFunction( cb.onError ) ) {
646 try {cb.onError( request, opt );}
647 catch(e) {/*ignore*/}
648 }
649 if( WhAjaj.isFunction( opt.onError ) ) {
650 try {opt.onError( request, opt );}
 
 
651 catch(e) {/*ignore*/}
652 }
653 }
654 };
655
@@ -694,10 +717,11 @@
717 var startTime = (new Date()).getTime();
718 var timeout = args.timeout || 10000/*arbitrary!*/;
719 var hitTimeout = false;
720 var done = false;
721 var tmid /* setTimeout() ID */;
722 var whself = this;
723 //if( json ) json = json.replace(/ö/g,"\\u00f6") /* ONLY FOR A SPECIFIC TEST */;
724 //alert( 'json=\n'+json );
725 function handleTimeout()
726 {
727 hitTimeout = true;
@@ -705,11 +729,11 @@
729 {
730 var now = (new Date()).getTime();
731 try { xhr.abort(); } catch(e) {/*ignore*/}
732 // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
733 args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
734 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
735 }
736 return;
737 }
738 function onStateChange()
739 { // reminder to self: apparently 'this' is-not-a XHR :/
@@ -726,11 +750,11 @@
750 clearTimeout( tmid );
751 tmid = null;
752 }
753 if( (xhr.status >= 200) && (xhr.status < 300) )
754 {
755 WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
756 return;
757 }
758 else
759 {
760 if( undefined === args.errorMessage )
@@ -737,11 +761,11 @@
761 {
762 args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
763 +"["+args.url+"]: "
764 +"Status text=["+xhr.statusText+"]"
765 ;
766 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
767 }
768 else { /*maybe it was was set by the timeout handler. */ }
769 return;
770 }
771 }
@@ -785,11 +809,11 @@
809 return xhr;
810 }
811 catch(e)
812 {
813 args.errorMessage = e.toString();
814 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
815 return undefined;
816 }
817 }/*XMLHttpRequest()*/,
818 /**
819 This is a concrete implementation of
@@ -815,17 +839,18 @@
839 WhAjaj.Connector.
840 */
841 jQuery:function(request,args)
842 {
843 var data = request || undefined;
844 var whself = this;
845 if( data ) {
846 if('string'!==typeof data) {
847 try {
848 data = JSON.stringify(data);
849 }
850 catch(e) {
851 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
852 return;
853 }
854 }
855 }
856 var ajopt = {
@@ -841,15 +866,15 @@
866 //this === the options for this ajax request
867 args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
868 +"Status text=["+textStatus+"]"
869 +(errorThrown ? ("Error=["+errorThrown+"]") : "")
870 ;
871 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
872 },
873 success: function(data)
874 {
875 WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
876 },
877 /* Set dataType=text instead of json for deeply archaic reasons which
878 might no longer apply.
879 */
880 dataType: 'text'
@@ -868,11 +893,11 @@
893 return xhr;
894 }
895 catch(e)
896 {
897 args.errorMessage = e.toString();
898 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
899 return undefined;
900 }
901 }/*jQuery()*/,
902 /**
903 This is a concrete implementation of
@@ -895,11 +920,11 @@
920 if('string'!==typeof data) {
921 try {
922 data = JSON.stringify(data);
923 }
924 catch(e) {
925 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
926 return;
927 }
928 }
929 }
930 var url;
@@ -961,18 +986,18 @@
986 json.push(line);
987 }
988 setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
989 }catch(e){
990 args.errorMessage = e.toString();
991 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
992 return undefined;
993 }
994 try { if(wr) wr.close(); } catch(e) { /*ignore*/}
995 try { if(rd) rd.close(); } catch(e) { /*ignore*/}
996 json = json.join('');
997 //print("READ IN JSON: "+json);
998 WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
999 }/*rhino()*/
1000 };
1001
1002 /**
1003 An internal function which takes an object containing properties
@@ -1110,12 +1135,15 @@
1135 (de)JSON-izing fails.
1136 */
1137 var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
1138 norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
1139 if( ! request ) norm.method = 'GET';
1140 var cb = this.callbacks || {};
1141 if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
1142 this.callbacks.beforeSend( request, norm );
1143 }
1144 if( WhAjaj.isFunction(norm.beforeSend) ){
1145 norm.beforeSend( request, norm );
1146 }
1147 //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
1148 try { this.sendImpl( request, norm ); }
1149 catch(e) { ex = e; }
1150

Keyboard Shortcuts

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