Fossil SCM

Removed the long-unused art/ and ajax/ dirs, per /chat discussion.

stephan 2021-12-25 14:55 code-movement
Commit d49bdb6cc223ad583682cc8beab280077eca0fa8bc9a4ea519aab876687659af
D ajax/README
-38
--- a/ajax/README
+++ b/ajax/README
@@ -1,38 +0,0 @@
1
-This is the README for how to set up the Fossil/JSON test web page
2
-under Apache on Unix systems. This is only intended only for
3
-Fossil/JSON developers/tinkerers:
4
-
5
-First, copy cgi-bin/fossil-json.cgi.example to
6
-cgi-bin/fossil-json.cgi. Edit it and correct the paths to the fossil
7
-binary and the repo you want to serve. Make it executable.
8
-
9
-MAKE SURE that the fossil repo you use is world-writable OR that your
10
-Web/CGI server is set up to run as the user ID of the owner of the
11
-fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
12
-writable by the CGI process.
13
-
14
-Next, set up an apache vhost entry. Mine looks like:
15
-
16
-<VirtualHost *:80>
17
- ServerAlias fjson
18
- ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
19
- DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
20
-</VirtualHost>
21
-
22
-Now add your preferred vhost name (fjson in the above example) to /etc/hosts:
23
-
24
- 127.0.0.1 ...other aliases... fjson
25
-
26
-Restart your Apache.
27
-
28
-Now visit: http://fjson/
29
-
30
-that will show the test/demo page. If it doesn't, edit index.html and
31
-make sure that:
32
-
33
- WhAjaj.Connector.options.ajax.url = ...;
34
-
35
-points to your CGI script. In theory you can also do this over fossil
36
-standalone server mode, but i haven't yet tested that particular test
37
-page in that mode.
38
-
--- a/ajax/README
+++ b/ajax/README
@@ -1,38 +0,0 @@
1 This is the README for how to set up the Fossil/JSON test web page
2 under Apache on Unix systems. This is only intended only for
3 Fossil/JSON developers/tinkerers:
4
5 First, copy cgi-bin/fossil-json.cgi.example to
6 cgi-bin/fossil-json.cgi. Edit it and correct the paths to the fossil
7 binary and the repo you want to serve. Make it executable.
8
9 MAKE SURE that the fossil repo you use is world-writable OR that your
10 Web/CGI server is set up to run as the user ID of the owner of the
11 fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
12 writable by the CGI process.
13
14 Next, set up an apache vhost entry. Mine looks like:
15
16 <VirtualHost *:80>
17 ServerAlias fjson
18 ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
19 DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
20 </VirtualHost>
21
22 Now add your preferred vhost name (fjson in the above example) to /etc/hosts:
23
24 127.0.0.1 ...other aliases... fjson
25
26 Restart your Apache.
27
28 Now visit: http://fjson/
29
30 that will show the test/demo page. If it doesn't, edit index.html and
31 make sure that:
32
33 WhAjaj.Connector.options.ajax.url = ...;
34
35 points to your CGI script. In theory you can also do this over fossil
36 standalone server mode, but i haven't yet tested that particular test
37 page in that mode.
38
--- a/ajax/README
+++ b/ajax/README
@@ -1,38 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/cgi-bin/fossil-json.cgi.example
-2
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -1,2 +0,0 @@
1
-#!/path/to/fossil/binary
2
-repository: /path/to/repo.fsl
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -1,2 +0,0 @@
1 #!/path/to/fossil/binary
2 repository: /path/to/repo.fsl
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -1,2 +0,0 @@
 
 
D ajax/i-test/rhino-shell.js
-208
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -1,208 +0,0 @@
1
-var FShell = {
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:false,
9
- prompt:"fossil shell > ",
10
- wiki:{},
11
- consol:java.lang.System.console(),
12
- v:function(msg){
13
- if(this.verbose){
14
- print("VERBOSE: "+msg);
15
- }
16
- }
17
-};
18
-(function bootstrap() {
19
- var srcdir = '../js/';
20
- var includes = [srcdir+'json2.js',
21
- srcdir+'whajaj.js',
22
- srcdir+'fossil-ajaj.js'
23
- ];
24
- for( var i in includes ) {
25
- load(includes[i]);
26
- }
27
- WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
28
- FShell.fossil = new FossilAjaj({
29
- asynchronous:false, /* rhino-based impl doesn't support async. */
30
- timeout:10000,
31
- url:FShell.serverUrl
32
- });
33
- print("Server: "+FShell.serverUrl);
34
- var cb = FShell.fossil.ajaj.callbacks;
35
- cb.beforeSend = function(req,opt){
36
- if(!FShell.verbose) return;
37
- print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
38
- if(req) print("Request envelope="+WhAjaj.stringify(req));
39
- };
40
- cb.afterSend = function(req,opt){
41
- //if(!FShell.verbose) return;
42
- //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
43
- //if(req) print("Request="+WhAjaj.stringify(req));
44
- };
45
- cb.onError = function(req,opt){
46
- //if(!FShell.verbose) return;
47
- print("ERROR: "+WhAjaj.stringify(opt));
48
- };
49
- cb.onResponse = function(resp,req){
50
- if(!FShell.verbose) return;
51
- if(resp && resp.resultCode){
52
- print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
53
- }
54
- print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
55
- };
56
- FShell.fossil.HAI({
57
- onResponse:function(resp,opt){
58
- assertResponseOK(resp);
59
- }
60
- });
61
-})();
62
-
63
-/**
64
- Throws an exception of cond is a falsy value.
65
-*/
66
-function assert(cond, descr){
67
- descr = descr || "Undescribed condition.";
68
- if(!cond){
69
- throw new Error("Assertion failed: "+descr);
70
- }else{
71
- //print("Assertion OK: "+descr);
72
- }
73
-}
74
-
75
-/**
76
- Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
77
-*/
78
-function send(command,payload, ajajOpt){
79
- FShell.fossil.sendCommand(command,payload,ajajOpt);
80
-}
81
-
82
-/**
83
- Asserts that resp is-a Object, resp.fossil is-a string, and
84
- !resp.resultCode.
85
-*/
86
-function assertResponseOK(resp){
87
- assert('object' === typeof resp,'Response is-a object.');
88
- assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
89
- assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
90
-}
91
-/**
92
- Asserts that resp is-a Object, resp.fossil is-a string, and
93
- resp.resultCode is a truthy value. If expectCode is set then
94
- it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
95
-*/
96
-function assertResponseError(resp,expectCode){
97
- assert('object' === typeof resp,'Response is-a object.');
98
- assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99
- assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
100
- if(expectCode){
101
- assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
102
- }
103
-}
104
-
105
-FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
106
- importPackage(java.io);
107
- importPackage(java.lang);
108
- var stdin = new BufferedReader(new InputStreamReader(System['in']));
109
- var self = this;
110
- return function(prompt) {
111
- if(prompt) print(prompt);
112
- var x = stdin.readLine();
113
- return null===x ? x : String(x) /*convert to JS string!*/;
114
- };
115
-}());
116
-
117
-FShell.dispatchLine = function(line){
118
- var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
119
- var cmd = av[0];
120
- var key, h;
121
- if('/' == cmd[0]) key = '/';
122
- else key = this.commandAliases[cmd];
123
- if(!key) key = cmd;
124
- h = this.commandHandlers[key];
125
- if(!h){
126
- print("Command not known: "+cmd +" ("+key+")");
127
- }else if(!WhAjaj.isFunction(h)){
128
- print("Not a function: "+key);
129
- }
130
- else{
131
- print("Sending ["+key+"] command... ");
132
- try{h(av);}
133
- catch(e){ print("EXCEPTION: "+e); }
134
- }
135
-};
136
-
137
-FShell.onResponseDefault = function(callback){
138
- return function(resp,req){
139
- assertResponseOK(resp);
140
- print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
141
- if(WhAjaj.isFunction(callback)){
142
- callback(resp,req);
143
- }
144
- };
145
-};
146
-FShell.commandHandlers = {
147
- "?":function(args){
148
- var k;
149
- print("Available commands...\n");
150
- var o = FShell.commandHandlers;
151
- for(k in o){
152
- if(! o.hasOwnProperty(k)) continue;
153
- print("\t"+k);
154
- }
155
- },
156
- "/":function(args){
157
- FShell.fossil.sendCommand('/json'+args[0],undefined,{
158
- beforeSend:function(req,opt){
159
- print("Sending to: "+opt.url);
160
- },
161
- onResponse:FShell.onResponseDefault()
162
- });
163
- },
164
- "eval":function(args){
165
- eval(args.join(' '));
166
- },
167
- "login":function(args){
168
- FShell.fossil.login(args[1], args[2], {
169
- onResponse:FShell.onResponseDefault()
170
- });
171
- },
172
- "whoami":function(args){
173
- FShell.fossil.whoami({
174
- onResponse:FShell.onResponseDefault()
175
- });
176
- },
177
- "HAI":function(args){
178
- FShell.fossil.HAI({
179
- onResponse:FShell.onResponseDefault()
180
- });
181
- }
182
-
183
-};
184
-FShell.commandAliases = {
185
- "li":"login",
186
- "lo":"logout",
187
- "who":"whoami",
188
- "hi":"HAI",
189
- "tci":"/timeline/ci?limit=3"
190
-};
191
-FShell.mainLoop = function(){
192
- var line;
193
- var check = /\S/;
194
- //var isJavaNull = /java\.lang\.null/;
195
- //print(typeof java.lang['null']);
196
- while( null != (line=this.readline(this.prompt)) ){
197
- if(null===line) break /*EOF*/;
198
- else if( "" === line ) continue;
199
- //print("Got line: "+line);
200
- else if(!check.test(line)) continue;
201
- print('typeof line = '+typeof line);
202
- this.dispatchLine(line);
203
- print("");
204
- }
205
- print("Bye!");
206
-};
207
-
208
-FShell.mainLoop();
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -1,208 +0,0 @@
1 var FShell = {
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:false,
9 prompt:"fossil shell > ",
10 wiki:{},
11 consol:java.lang.System.console(),
12 v:function(msg){
13 if(this.verbose){
14 print("VERBOSE: "+msg);
15 }
16 }
17 };
18 (function bootstrap() {
19 var srcdir = '../js/';
20 var includes = [srcdir+'json2.js',
21 srcdir+'whajaj.js',
22 srcdir+'fossil-ajaj.js'
23 ];
24 for( var i in includes ) {
25 load(includes[i]);
26 }
27 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
28 FShell.fossil = new FossilAjaj({
29 asynchronous:false, /* rhino-based impl doesn't support async. */
30 timeout:10000,
31 url:FShell.serverUrl
32 });
33 print("Server: "+FShell.serverUrl);
34 var cb = FShell.fossil.ajaj.callbacks;
35 cb.beforeSend = function(req,opt){
36 if(!FShell.verbose) return;
37 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
38 if(req) print("Request envelope="+WhAjaj.stringify(req));
39 };
40 cb.afterSend = function(req,opt){
41 //if(!FShell.verbose) return;
42 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
43 //if(req) print("Request="+WhAjaj.stringify(req));
44 };
45 cb.onError = function(req,opt){
46 //if(!FShell.verbose) return;
47 print("ERROR: "+WhAjaj.stringify(opt));
48 };
49 cb.onResponse = function(resp,req){
50 if(!FShell.verbose) return;
51 if(resp && resp.resultCode){
52 print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
53 }
54 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
55 };
56 FShell.fossil.HAI({
57 onResponse:function(resp,opt){
58 assertResponseOK(resp);
59 }
60 });
61 })();
62
63 /**
64 Throws an exception of cond is a falsy value.
65 */
66 function assert(cond, descr){
67 descr = descr || "Undescribed condition.";
68 if(!cond){
69 throw new Error("Assertion failed: "+descr);
70 }else{
71 //print("Assertion OK: "+descr);
72 }
73 }
74
75 /**
76 Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
77 */
78 function send(command,payload, ajajOpt){
79 FShell.fossil.sendCommand(command,payload,ajajOpt);
80 }
81
82 /**
83 Asserts that resp is-a Object, resp.fossil is-a string, and
84 !resp.resultCode.
85 */
86 function assertResponseOK(resp){
87 assert('object' === typeof resp,'Response is-a object.');
88 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
89 assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
90 }
91 /**
92 Asserts that resp is-a Object, resp.fossil is-a string, and
93 resp.resultCode is a truthy value. If expectCode is set then
94 it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
95 */
96 function assertResponseError(resp,expectCode){
97 assert('object' === typeof resp,'Response is-a object.');
98 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99 assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
100 if(expectCode){
101 assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
102 }
103 }
104
105 FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
106 importPackage(java.io);
107 importPackage(java.lang);
108 var stdin = new BufferedReader(new InputStreamReader(System['in']));
109 var self = this;
110 return function(prompt) {
111 if(prompt) print(prompt);
112 var x = stdin.readLine();
113 return null===x ? x : String(x) /*convert to JS string!*/;
114 };
115 }());
116
117 FShell.dispatchLine = function(line){
118 var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
119 var cmd = av[0];
120 var key, h;
121 if('/' == cmd[0]) key = '/';
122 else key = this.commandAliases[cmd];
123 if(!key) key = cmd;
124 h = this.commandHandlers[key];
125 if(!h){
126 print("Command not known: "+cmd +" ("+key+")");
127 }else if(!WhAjaj.isFunction(h)){
128 print("Not a function: "+key);
129 }
130 else{
131 print("Sending ["+key+"] command... ");
132 try{h(av);}
133 catch(e){ print("EXCEPTION: "+e); }
134 }
135 };
136
137 FShell.onResponseDefault = function(callback){
138 return function(resp,req){
139 assertResponseOK(resp);
140 print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
141 if(WhAjaj.isFunction(callback)){
142 callback(resp,req);
143 }
144 };
145 };
146 FShell.commandHandlers = {
147 "?":function(args){
148 var k;
149 print("Available commands...\n");
150 var o = FShell.commandHandlers;
151 for(k in o){
152 if(! o.hasOwnProperty(k)) continue;
153 print("\t"+k);
154 }
155 },
156 "/":function(args){
157 FShell.fossil.sendCommand('/json'+args[0],undefined,{
158 beforeSend:function(req,opt){
159 print("Sending to: "+opt.url);
160 },
161 onResponse:FShell.onResponseDefault()
162 });
163 },
164 "eval":function(args){
165 eval(args.join(' '));
166 },
167 "login":function(args){
168 FShell.fossil.login(args[1], args[2], {
169 onResponse:FShell.onResponseDefault()
170 });
171 },
172 "whoami":function(args){
173 FShell.fossil.whoami({
174 onResponse:FShell.onResponseDefault()
175 });
176 },
177 "HAI":function(args){
178 FShell.fossil.HAI({
179 onResponse:FShell.onResponseDefault()
180 });
181 }
182
183 };
184 FShell.commandAliases = {
185 "li":"login",
186 "lo":"logout",
187 "who":"whoami",
188 "hi":"HAI",
189 "tci":"/timeline/ci?limit=3"
190 };
191 FShell.mainLoop = function(){
192 var line;
193 var check = /\S/;
194 //var isJavaNull = /java\.lang\.null/;
195 //print(typeof java.lang['null']);
196 while( null != (line=this.readline(this.prompt)) ){
197 if(null===line) break /*EOF*/;
198 else if( "" === line ) continue;
199 //print("Got line: "+line);
200 else if(!check.test(line)) continue;
201 print('typeof line = '+typeof line);
202 this.dispatchLine(line);
203 print("");
204 }
205 print("Bye!");
206 };
207
208 FShell.mainLoop();
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -1,208 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/i-test/rhino-test.js
-279
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -1,279 +0,0 @@
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
- fossilBinary:'fossil',
10
- wiki:{}
11
-};
12
-(function bootstrap() {
13
- var srcdir = '../js/';
14
- var includes = [srcdir+'json2.js',
15
- srcdir+'whajaj.js',
16
- srcdir+'fossil-ajaj.js'
17
- ];
18
- for( var i in includes ) {
19
- load(includes[i]);
20
- }
21
- WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
22
- TestApp.fossil = new FossilAjaj({
23
- asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
24
- timeout:0,
25
- url:TestApp.serverUrl,
26
- fossilBinary:TestApp.fossilBinary
27
- });
28
- var cb = TestApp.fossil.ajaj.callbacks;
29
- cb.beforeSend = function(req,opt){
30
- if(!TestApp.verbose) return;
31
- print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
32
- if(req) print("Request envelope="+WhAjaj.stringify(req));
33
- };
34
- cb.afterSend = function(req,opt){
35
- //if(!TestApp.verbose) return;
36
- //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
37
- //if(req) print("Request="+WhAjaj.stringify(req));
38
- };
39
- cb.onError = function(req,opt){
40
- if(!TestApp.verbose) return;
41
- print("ERROR: "+WhAjaj.stringify(opt));
42
- };
43
- cb.onResponse = function(resp,req){
44
- if(!TestApp.verbose) return;
45
- print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
46
- };
47
-
48
-})();
49
-
50
-/**
51
- Throws an exception of cond is a falsy value.
52
-*/
53
-function assert(cond, descr){
54
- descr = descr || "Undescribed condition.";
55
- if(!cond){
56
- print("Assertion FAILED: "+descr);
57
- throw new Error("Assertion failed: "+descr);
58
- // aarrgghh. Exceptions are of course swallowed by
59
- // the AJAX layer, to keep from killing a browser's
60
- // script environment.
61
- }else{
62
- if(TestApp.verbose) print("Assertion OK: "+descr);
63
- }
64
-}
65
-
66
-/**
67
- Calls func() in a try/catch block and throws an exception if
68
- func() does NOT throw.
69
-*/
70
-function assertThrows(func, descr){
71
- descr = descr || "Undescribed condition failed.";
72
- var ex;
73
- try{
74
- func();
75
- }catch(e){
76
- ex = e;
77
- }
78
- if(!ex){
79
- throw new Error("Function did not throw (as expected): "+descr);
80
- }else{
81
- if(TestApp.verbose) print("Function threw (as expected): "+descr+": "+ex);
82
- }
83
-}
84
-
85
-/**
86
- Convenience form of TestApp.fossil.sendCommand(command,payload,ajajOpt).
87
-*/
88
-function send(command,payload, ajajOpt){
89
- TestApp.fossil.sendCommand(command,payload,ajajOpt);
90
-}
91
-
92
-/**
93
- Asserts that resp is-a Object, resp.fossil is-a string, and
94
- !resp.resultCode.
95
-*/
96
-function assertResponseOK(resp){
97
- assert('object' === typeof resp,'Response is-a object.');
98
- assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99
- assert( undefined === resp.resultCode, 'resp.resultCode is not set');
100
-}
101
-/**
102
- Asserts that resp is-a Object, resp.fossil is-a string, and
103
- resp.resultCode is a truthy value. If expectCode is set then
104
- it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
105
-*/
106
-function assertResponseError(resp,expectCode){
107
- assert('object' === typeof resp,'Response is-a object.');
108
- assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
109
- assert( !!resp.resultCode, 'resp.resultCode='+resp.resultCode);
110
- if(expectCode){
111
- assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
112
- }
113
-}
114
-
115
-function testHAI(){
116
- var rs;
117
- TestApp.fossil.HAI({
118
- onResponse:function(resp,req){
119
- rs = resp;
120
- }
121
- });
122
- assertResponseOK(rs);
123
- TestApp.serverVersion = rs.fossil;
124
- assert( 'string' === typeof TestApp.serverVersion, 'server version = '+TestApp.serverVersion);
125
-}
126
-testHAI.description = 'Get server version info.';
127
-
128
-function testIAmNobody(){
129
- TestApp.fossil.whoami('/json/whoami');
130
- assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
131
- assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );
132
-
133
-}
134
-testIAmNobody.description = 'Ensure that current user is "nobody".';
135
-
136
-
137
-function testAnonymousLogin(){
138
- TestApp.fossil.login();
139
- assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
140
- assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
141
- TestApp.fossil.userName = null;
142
- TestApp.fossil.whoami('/json/whoami');
143
- assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
144
-}
145
-testAnonymousLogin.description = 'Perform anonymous login.';
146
-
147
-function testAnonWiki(){
148
- var rs;
149
- TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
150
- beforeSend:function(req,opt){
151
- assert( req && (req.authToken==TestApp.fossil.auth.authToken), 'Request envelope contains expected authToken.' );
152
- },
153
- onResponse:function(resp,req){
154
- rs = resp;
155
- }
156
- });
157
- assertResponseOK(rs);
158
- assert( (typeof [] === typeof rs.payload) && rs.payload.length,
159
- "Wiki list seems to be okay.");
160
- TestApp.wiki.list = rs.payload;
161
-
162
- TestApp.fossil.sendCommand('/json/wiki/get',{
163
- name:TestApp.wiki.list[0]
164
- },{
165
- onResponse:function(resp,req){
166
- rs = resp;
167
- }
168
- });
169
- assertResponseOK(rs);
170
- assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches expectations.");
171
- print("Got first wiki page: "+WhAjaj.stringify(rs.payload));
172
-
173
-}
174
-testAnonWiki.description = 'Fetch wiki list as anonymous user.';
175
-
176
-function testFetchCheckinArtifact(){
177
- var art = '18dd383e5e7684ece';
178
- var rs;
179
- TestApp.fossil.sendCommand('/json/artifact',{
180
- 'name': art
181
- },
182
- {
183
- onResponse:function(resp,req){
184
- rs = resp;
185
- }
186
- });
187
- assertResponseOK(rs);
188
- assert(3 == rs.payload.parents.length, 'Got 3 parent artifacts.');
189
-}
190
-testFetchCheckinArtifact.description = '/json/artifact/CHECKIN';
191
-
192
-function testAnonLogout(){
193
- var rs;
194
- TestApp.fossil.logout({
195
- onResponse:function(resp,req){
196
- rs = resp;
197
- }
198
- });
199
- assertResponseOK(rs);
200
- print("Ensure that second logout attempt fails...");
201
- TestApp.fossil.logout({
202
- onResponse:function(resp,req){
203
- rs = resp;
204
- }
205
- });
206
- assertResponseError(rs);
207
-}
208
-testAnonLogout.description = 'Log out anonymous user.';
209
-
210
-function testExternalProcess(){
211
-
212
- var req = { command:"HAI", requestId:'testExternalProcess()' };
213
- var args = [TestApp.fossilBinary, 'json', '--json-input', '-'];
214
- var p = java.lang.Runtime.getRuntime().exec(args);
215
- var outs = p.getOutputStream();
216
- var osr = new java.io.OutputStreamWriter(outs);
217
- var osb = new java.io.BufferedWriter(osr);
218
- var json = JSON.stringify(req);
219
- osb.write(json,0, json.length);
220
- osb.close();
221
- req = json = outs = osr = osb = undefined;
222
- var ins = p.getInputStream();
223
- var isr = new java.io.InputStreamReader(ins);
224
- var br = new java.io.BufferedReader(isr);
225
- var line;
226
-
227
- while( null !== (line=br.readLine())){
228
- print(line);
229
- }
230
- br.close();
231
- isr.close();
232
- ins.close();
233
- p.waitFor();
234
-}
235
-testExternalProcess.description = 'Run fossil as external process.';
236
-
237
-function testExternalProcessHandler(){
238
- var aj = TestApp.fossil.ajaj;
239
- var oldImpl = aj.sendImpl;
240
- aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl;
241
- var rs;
242
- TestApp.fossil.sendCommand('/json/HAI',undefined,{
243
- onResponse:function(resp,opt){
244
- rs = resp;
245
- }
246
- });
247
- aj.sendImpl = oldImpl;
248
- assertResponseOK(rs);
249
- print("Using local fossil binary via AJAX interface, we fetched: "+
250
- WhAjaj.stringify(rs));
251
-}
252
-testExternalProcessHandler.description = 'Try local fossil binary via AJAX interface.';
253
-
254
-(function runAllTests(){
255
- var testList = [
256
- testHAI,
257
- testIAmNobody,
258
- testAnonymousLogin,
259
- testAnonWiki,
260
- testFetchCheckinArtifact,
261
- testAnonLogout,
262
- testExternalProcess,
263
- testExternalProcessHandler
264
- ];
265
- var i, f;
266
- for( i = 0; i < testList.length; ++i ){
267
- f = testList[i];
268
- try{
269
- print("Running test #"+(i+1)+": "+(f.description || "no description."));
270
- f();
271
- }catch(e){
272
- print("Test #"+(i+1)+" failed: "+e);
273
- throw e;
274
- }
275
- }
276
-
277
-})();
278
-
279
-print("Done! If you don't see an exception message in the last few lines, you win!");
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -1,279 +0,0 @@
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 fossilBinary:'fossil',
10 wiki:{}
11 };
12 (function bootstrap() {
13 var srcdir = '../js/';
14 var includes = [srcdir+'json2.js',
15 srcdir+'whajaj.js',
16 srcdir+'fossil-ajaj.js'
17 ];
18 for( var i in includes ) {
19 load(includes[i]);
20 }
21 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
22 TestApp.fossil = new FossilAjaj({
23 asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
24 timeout:0,
25 url:TestApp.serverUrl,
26 fossilBinary:TestApp.fossilBinary
27 });
28 var cb = TestApp.fossil.ajaj.callbacks;
29 cb.beforeSend = function(req,opt){
30 if(!TestApp.verbose) return;
31 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
32 if(req) print("Request envelope="+WhAjaj.stringify(req));
33 };
34 cb.afterSend = function(req,opt){
35 //if(!TestApp.verbose) return;
36 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
37 //if(req) print("Request="+WhAjaj.stringify(req));
38 };
39 cb.onError = function(req,opt){
40 if(!TestApp.verbose) return;
41 print("ERROR: "+WhAjaj.stringify(opt));
42 };
43 cb.onResponse = function(resp,req){
44 if(!TestApp.verbose) return;
45 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
46 };
47
48 })();
49
50 /**
51 Throws an exception of cond is a falsy value.
52 */
53 function assert(cond, descr){
54 descr = descr || "Undescribed condition.";
55 if(!cond){
56 print("Assertion FAILED: "+descr);
57 throw new Error("Assertion failed: "+descr);
58 // aarrgghh. Exceptions are of course swallowed by
59 // the AJAX layer, to keep from killing a browser's
60 // script environment.
61 }else{
62 if(TestApp.verbose) print("Assertion OK: "+descr);
63 }
64 }
65
66 /**
67 Calls func() in a try/catch block and throws an exception if
68 func() does NOT throw.
69 */
70 function assertThrows(func, descr){
71 descr = descr || "Undescribed condition failed.";
72 var ex;
73 try{
74 func();
75 }catch(e){
76 ex = e;
77 }
78 if(!ex){
79 throw new Error("Function did not throw (as expected): "+descr);
80 }else{
81 if(TestApp.verbose) print("Function threw (as expected): "+descr+": "+ex);
82 }
83 }
84
85 /**
86 Convenience form of TestApp.fossil.sendCommand(command,payload,ajajOpt).
87 */
88 function send(command,payload, ajajOpt){
89 TestApp.fossil.sendCommand(command,payload,ajajOpt);
90 }
91
92 /**
93 Asserts that resp is-a Object, resp.fossil is-a string, and
94 !resp.resultCode.
95 */
96 function assertResponseOK(resp){
97 assert('object' === typeof resp,'Response is-a object.');
98 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99 assert( undefined === resp.resultCode, 'resp.resultCode is not set');
100 }
101 /**
102 Asserts that resp is-a Object, resp.fossil is-a string, and
103 resp.resultCode is a truthy value. If expectCode is set then
104 it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
105 */
106 function assertResponseError(resp,expectCode){
107 assert('object' === typeof resp,'Response is-a object.');
108 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
109 assert( !!resp.resultCode, 'resp.resultCode='+resp.resultCode);
110 if(expectCode){
111 assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
112 }
113 }
114
115 function testHAI(){
116 var rs;
117 TestApp.fossil.HAI({
118 onResponse:function(resp,req){
119 rs = resp;
120 }
121 });
122 assertResponseOK(rs);
123 TestApp.serverVersion = rs.fossil;
124 assert( 'string' === typeof TestApp.serverVersion, 'server version = '+TestApp.serverVersion);
125 }
126 testHAI.description = 'Get server version info.';
127
128 function testIAmNobody(){
129 TestApp.fossil.whoami('/json/whoami');
130 assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
131 assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );
132
133 }
134 testIAmNobody.description = 'Ensure that current user is "nobody".';
135
136
137 function testAnonymousLogin(){
138 TestApp.fossil.login();
139 assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
140 assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
141 TestApp.fossil.userName = null;
142 TestApp.fossil.whoami('/json/whoami');
143 assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
144 }
145 testAnonymousLogin.description = 'Perform anonymous login.';
146
147 function testAnonWiki(){
148 var rs;
149 TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
150 beforeSend:function(req,opt){
151 assert( req && (req.authToken==TestApp.fossil.auth.authToken), 'Request envelope contains expected authToken.' );
152 },
153 onResponse:function(resp,req){
154 rs = resp;
155 }
156 });
157 assertResponseOK(rs);
158 assert( (typeof [] === typeof rs.payload) && rs.payload.length,
159 "Wiki list seems to be okay.");
160 TestApp.wiki.list = rs.payload;
161
162 TestApp.fossil.sendCommand('/json/wiki/get',{
163 name:TestApp.wiki.list[0]
164 },{
165 onResponse:function(resp,req){
166 rs = resp;
167 }
168 });
169 assertResponseOK(rs);
170 assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches expectations.");
171 print("Got first wiki page: "+WhAjaj.stringify(rs.payload));
172
173 }
174 testAnonWiki.description = 'Fetch wiki list as anonymous user.';
175
176 function testFetchCheckinArtifact(){
177 var art = '18dd383e5e7684ece';
178 var rs;
179 TestApp.fossil.sendCommand('/json/artifact',{
180 'name': art
181 },
182 {
183 onResponse:function(resp,req){
184 rs = resp;
185 }
186 });
187 assertResponseOK(rs);
188 assert(3 == rs.payload.parents.length, 'Got 3 parent artifacts.');
189 }
190 testFetchCheckinArtifact.description = '/json/artifact/CHECKIN';
191
192 function testAnonLogout(){
193 var rs;
194 TestApp.fossil.logout({
195 onResponse:function(resp,req){
196 rs = resp;
197 }
198 });
199 assertResponseOK(rs);
200 print("Ensure that second logout attempt fails...");
201 TestApp.fossil.logout({
202 onResponse:function(resp,req){
203 rs = resp;
204 }
205 });
206 assertResponseError(rs);
207 }
208 testAnonLogout.description = 'Log out anonymous user.';
209
210 function testExternalProcess(){
211
212 var req = { command:"HAI", requestId:'testExternalProcess()' };
213 var args = [TestApp.fossilBinary, 'json', '--json-input', '-'];
214 var p = java.lang.Runtime.getRuntime().exec(args);
215 var outs = p.getOutputStream();
216 var osr = new java.io.OutputStreamWriter(outs);
217 var osb = new java.io.BufferedWriter(osr);
218 var json = JSON.stringify(req);
219 osb.write(json,0, json.length);
220 osb.close();
221 req = json = outs = osr = osb = undefined;
222 var ins = p.getInputStream();
223 var isr = new java.io.InputStreamReader(ins);
224 var br = new java.io.BufferedReader(isr);
225 var line;
226
227 while( null !== (line=br.readLine())){
228 print(line);
229 }
230 br.close();
231 isr.close();
232 ins.close();
233 p.waitFor();
234 }
235 testExternalProcess.description = 'Run fossil as external process.';
236
237 function testExternalProcessHandler(){
238 var aj = TestApp.fossil.ajaj;
239 var oldImpl = aj.sendImpl;
240 aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl;
241 var rs;
242 TestApp.fossil.sendCommand('/json/HAI',undefined,{
243 onResponse:function(resp,opt){
244 rs = resp;
245 }
246 });
247 aj.sendImpl = oldImpl;
248 assertResponseOK(rs);
249 print("Using local fossil binary via AJAX interface, we fetched: "+
250 WhAjaj.stringify(rs));
251 }
252 testExternalProcessHandler.description = 'Try local fossil binary via AJAX interface.';
253
254 (function runAllTests(){
255 var testList = [
256 testHAI,
257 testIAmNobody,
258 testAnonymousLogin,
259 testAnonWiki,
260 testFetchCheckinArtifact,
261 testAnonLogout,
262 testExternalProcess,
263 testExternalProcessHandler
264 ];
265 var i, f;
266 for( i = 0; i < testList.length; ++i ){
267 f = testList[i];
268 try{
269 print("Running test #"+(i+1)+": "+(f.description || "no description."));
270 f();
271 }catch(e){
272 print("Test #"+(i+1)+" failed: "+e);
273 throw e;
274 }
275 }
276
277 })();
278
279 print("Done! If you don't see an exception message in the last few lines, you win!");
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -1,279 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/index.html
-330
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -1,332 +0,0 @@
1
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
-
4
-<head>
5
- <title>Fossil/JSON raw request sending</title>
6
- <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
8
- <script type="text/javascript" src="js/whajaj.js"></script>
9
- <script type="text/javascript" src="js/fossil-ajaj.js"></script>
10
-
11
-<style type='text/css'>
12
-th {
13
- text-align: left;
14
- background-color: #ececec;
15
-}
16
-
17
-.dangerWillRobinson {
18
- background-color: yellow;
19
-}
20
-</style>
21
-
22
-<script type='text/javascript'>
23
-WhAjaj.Connector.options.ajax.url =
24
-/*
25
- Change this to your CGI/server root path:
26
-*/
27
- //'http://fjson/cgi-bin/fossil.cgi'
28
- //'/repos/fossil-sgb/json.cgi'
29
- '/cgi-bin/fossil-json.cgi'
30
- ;
31
-var TheApp = {
32
- response:null,
33
- sessionID:null,
34
- jqe:{}/*jqe==jQuery Elements*/,
35
- ajaxCount:0,
36
- cgi: new FossilAjaj()
37
-};
38
-
39
-
40
-TheApp.startAjaxNotif = function()
41
-{
42
- ++this.ajaxCount;
43
- TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
44
- this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
45
- if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
46
-};
47
-
48
-TheApp.endAjaxNotif = function()
49
-{
50
- --this.ajaxCount;
51
- this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
52
- if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
53
-};
54
-
55
-TheApp.responseContainsError = function(resp) {
56
- if( resp && resp.resultCode ) {
57
- //alert("Error response:\n"+JSON.stringify(resp,0,4));
58
- TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
59
- //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
60
- //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
61
- TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
62
- return true;
63
- }
64
- return false;
65
-};
66
-
67
-
68
-TheApp.sendRequest = function() {
69
- var path = this.jqe.textPath.val();
70
- var self = this;
71
- var data = this.jqe.taRequest.val();
72
- var doPost = (data && data.length);
73
- var req;
74
- if( doPost ) try {
75
- req = JSON.parse(data);
76
- }
77
- catch(e) {
78
- TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
79
- return;
80
- }
81
- if( req ) {
82
- req.requestId = this.cgi.generateRequestId();
83
- }
84
- var self = this;
85
- var opt = {
86
- url: WhAjaj.Connector.options.ajax.url + path,
87
- method: doPost ? 'POST' : 'GET'
88
- };
89
- this.cgi.sendRequest( req, opt );
90
-};
91
-jQuery.fn.animateHighlight = function(highlightColor, duration) {
92
- var highlightBg = highlightColor || "#FFFF9C";
93
- var animateMs = duration || 1500;
94
- var originalBg = this.css("backgroundColor");
95
- this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
96
-};
97
-jQuery.fn.flash = function( color, duration )
98
-{
99
- var current = this.css( 'color' );
100
- this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
101
- this.animate( { color: current }, duration / 2 );
102
-};
103
-
104
-function myJsonPCallback(obj){
105
- alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
106
-}
107
-
108
-jQuery(document).ready(function(){
109
- var ids = [// list of HTML element IDs we use often.
110
- 'btnSend',
111
- 'ajaxNotification',
112
- 'currentAuthToken',
113
- 'responseContainer',
114
- 'taRequest',
115
- 'taRequestOpt',
116
- 'taResponse',
117
- 'textPath',
118
- 'timer'
119
- ];
120
- var i, k;
121
- for( i = 0; i < ids.length; ++i ) {
122
- k = ids[i];
123
- TheApp.jqe[k] = jQuery('#'+k);
124
- }
125
- TheApp.jqe.textPath.
126
- keyup(function(event){
127
- if(event.keyCode == 13){
128
- TheApp.sendRequest();
129
- }
130
- });
131
- TheApp.timer = {
132
- _tstart:0,_tend:0,duration:0,
133
- start:function(){
134
- this._tstart = (new Date()).getTime();
135
- },
136
- end:function(){
137
- this._tend = (new Date()).getTime();
138
- return this.duration = this._tend - this._tstart;
139
- }
140
- };
141
-
142
- var ajcb = TheApp.cgi.ajaj.callbacks;
143
- ajcb.beforeSend = function(req,opt) {
144
- TheApp.timer.start();
145
- var val =
146
- req ?
147
- (('string'===typeof req) ? req : WhAjaj.stringify(req))
148
- : '';
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
- ajcb.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
- ajcb.onResponse = function(resp,req, opt) {
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)
167
- }
168
- //alert("onResponse this:"+WhAjaj.stringify(this));
169
- //alert("val="+val);
170
- // FIXME: this.url is hosed for login because of how i overload onResponse()
171
- if( opt.url ) TheApp.jqe.textPath.val(opt.url.replace(WhAjaj.Connector.options.ajax.url,''));
172
- TheApp.jqe.taResponse.val( val );
173
- };
174
- ajcb.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:\n"+WhAjaj.stringify(this.auth));
180
- TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
181
- };
182
- TheApp.cgi.onLogout = function(){
183
- TheApp.jqe.taResponse.val( "Logged out!" );
184
- TheApp.jqe.currentAuthToken.text("Not logged in");
185
- };
186
- TheApp.cgi.whoami();
187
- jQuery('#headerArea').click(function(){
188
- jQuery(this).slideUp('fast',function(){
189
- jQuery(this).remove();
190
- });
191
- });
192
-});
193
-
194
-</script>
195
-
196
-</head>
197
-
198
-<body>
199
-<span id='ajaxNotification'></span>
200
-<div id='headerArea'>
201
-<h1>You know, for sending raw JSON requests to Fossil...</h1>
202
-
203
-If you're actually using this page, then you know what you're doing and don't
204
-need help text, hoverhelp, and a snazzy interface.
205
-
206
-<br><br>
207
-
208
-
209
-JSON API docs: <a href='https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit'>https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit</a>
210
-
211
-</div><!-- #headerArea -->
212
-See also: <a href='wiki-editor.html'>prototype wiki editor</a>.
213
-
214
-<h2>Request...</h2>
215
-
216
-Path: <input type='text' size='40' id='textPath' value='/json/HAI'/>
217
-<input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
218
-If the POST textarea is not empty then it will be posted with the request.
219
-<hr/>
220
-<strong>Quick-posts:</strong><br/>
221
-<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
222
-<input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
223
-<input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
224
-<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
225
-<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
226
-<input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
227
-<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
228
-<input type='button' value='g' onclick='TheApp.cgi.sendCommand("/json/g")' />
229
-
230
-<br/>
231
-
232
-<input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
233
-<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
234
-<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
235
-<input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
236
-<input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/json/timeline/branch")' />
237
-
238
-<br/>
239
-
240
-<input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
241
-<input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand("/json/wiki/list",{verbose:1})' />
242
-<input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{name:"Fossil"})' />
243
-<input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
244
-<input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wiki/diff/e32ccdcda59e930c77c/e15992f475760cdf3a9")' />
245
-
246
-<br/>
247
-
248
-<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
249
-<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
250
-<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
251
-<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
252
-<input type='button' value='tag/add'
253
- onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
254
-<input type='button' value='tag/cancel'
255
- onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
256
-<input type='button' value='tag/find'
257
- onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
258
-
259
-<br/>
260
-
261
-<input type='button' value='diff'
262
- onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
263
-<input type='button' value='diff/A/B'
264
- onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />
265
-
266
-<input type='button' value='query'
267
- onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />
268
-
269
-<input type='button' value='report list'
270
- onclick='TheApp.cgi.sendCommand("/json/report/list")' />
271
-<input type='button' value='report get'
272
- onclick='TheApp.cgi.sendCommand("/json/report/get",2)' />
273
-
274
-<input type='button' value='report run'
275
- onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"})' />
276
-
277
-<input type='button' value='config/get' onclick='TheApp.cgi.sendCommand("/json/config/get")' />
278
-
279
-<!-- not yet ready...
280
-<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
--->
281
-
282
-<!--
283
-<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
284
-<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
285
-<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
286
-<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
--->
287
-<hr/>
288
-<b>Login:</b>
289
-<br/>
290
-<input type='button' value='Anon. PW' onclick='TheApp.cgi.sendCommand("/json/anonymousPassword")' />
291
-<input type='button' value='Anon. PW+Login' onclick='TheApp.cgi.login()' />
292
-<br/>
293
-name:<input type='text' id='textUser' value='json-demo' size='12'/>
294
-pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
295
-<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
296
-<input type='button' value='logout' onclick='TheApp.cgi.logout()' />
297
-<br/>
298
-<span id='currentAuthToken' style='font-family:monospaced'></span>
299
-
300
-<br/>
301
-
302
-<hr/>
303
-
304
-<table>
305
- <tr>
306
- <th>POST data</th>
307
- <th>Request AJAJ options</th>
308
- </tr>
309
- <tr>
310
- <td width='50%' valign='top'>
311
- <textarea id='taRequest' rows='10' cols='50'></textarea>
312
- </td>
313
- <td width='50%' valign='top'>
314
- <textarea id='taRequestOpt' rows='10' cols='40' readonly></textarea>
315
- </td>
316
- </tr>
317
- <tr>
318
- <th colspan='2'>Response <span id='timer'></span></th>
319
- </tr>
320
- <tr>
321
- <td colspan='2' id='responseContainer' valign='top'>
322
- <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
323
- </td>
324
- </tr>
325
-</table>
326
-<div></div>
327
-<div></div>
328
-<div></div>
329
-
330
-</body></html>
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -1,332 +0,0 @@
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
4 <head>
5 <title>Fossil/JSON raw request sending</title>
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
8 <script type="text/javascript" src="js/whajaj.js"></script>
9 <script type="text/javascript" src="js/fossil-ajaj.js"></script>
10
11 <style type='text/css'>
12 th {
13 text-align: left;
14 background-color: #ececec;
15 }
16
17 .dangerWillRobinson {
18 background-color: yellow;
19 }
20 </style>
21
22 <script type='text/javascript'>
23 WhAjaj.Connector.options.ajax.url =
24 /*
25 Change this to your CGI/server root path:
26 */
27 //'http://fjson/cgi-bin/fossil.cgi'
28 //'/repos/fossil-sgb/json.cgi'
29 '/cgi-bin/fossil-json.cgi'
30 ;
31 var TheApp = {
32 response:null,
33 sessionID:null,
34 jqe:{}/*jqe==jQuery Elements*/,
35 ajaxCount:0,
36 cgi: new FossilAjaj()
37 };
38
39
40 TheApp.startAjaxNotif = function()
41 {
42 ++this.ajaxCount;
43 TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
44 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
45 if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
46 };
47
48 TheApp.endAjaxNotif = function()
49 {
50 --this.ajaxCount;
51 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
52 if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
53 };
54
55 TheApp.responseContainsError = function(resp) {
56 if( resp && resp.resultCode ) {
57 //alert("Error response:\n"+JSON.stringify(resp,0,4));
58 TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
59 //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
60 //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
61 TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
62 return true;
63 }
64 return false;
65 };
66
67
68 TheApp.sendRequest = function() {
69 var path = this.jqe.textPath.val();
70 var self = this;
71 var data = this.jqe.taRequest.val();
72 var doPost = (data && data.length);
73 var req;
74 if( doPost ) try {
75 req = JSON.parse(data);
76 }
77 catch(e) {
78 TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
79 return;
80 }
81 if( req ) {
82 req.requestId = this.cgi.generateRequestId();
83 }
84 var self = this;
85 var opt = {
86 url: WhAjaj.Connector.options.ajax.url + path,
87 method: doPost ? 'POST' : 'GET'
88 };
89 this.cgi.sendRequest( req, opt );
90 };
91 jQuery.fn.animateHighlight = function(highlightColor, duration) {
92 var highlightBg = highlightColor || "#FFFF9C";
93 var animateMs = duration || 1500;
94 var originalBg = this.css("backgroundColor");
95 this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
96 };
97 jQuery.fn.flash = function( color, duration )
98 {
99 var current = this.css( 'color' );
100 this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
101 this.animate( { color: current }, duration / 2 );
102 };
103
104 function myJsonPCallback(obj){
105 alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
106 }
107
108 jQuery(document).ready(function(){
109 var ids = [// list of HTML element IDs we use often.
110 'btnSend',
111 'ajaxNotification',
112 'currentAuthToken',
113 'responseContainer',
114 'taRequest',
115 'taRequestOpt',
116 'taResponse',
117 'textPath',
118 'timer'
119 ];
120 var i, k;
121 for( i = 0; i < ids.length; ++i ) {
122 k = ids[i];
123 TheApp.jqe[k] = jQuery('#'+k);
124 }
125 TheApp.jqe.textPath.
126 keyup(function(event){
127 if(event.keyCode == 13){
128 TheApp.sendRequest();
129 }
130 });
131 TheApp.timer = {
132 _tstart:0,_tend:0,duration:0,
133 start:function(){
134 this._tstart = (new Date()).getTime();
135 },
136 end:function(){
137 this._tend = (new Date()).getTime();
138 return this.duration = this._tend - this._tstart;
139 }
140 };
141
142 var ajcb = TheApp.cgi.ajaj.callbacks;
143 ajcb.beforeSend = function(req,opt) {
144 TheApp.timer.start();
145 var val =
146 req ?
147 (('string'===typeof req) ? req : WhAjaj.stringify(req))
148 : '';
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 ajcb.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 ajcb.onResponse = function(resp,req, opt) {
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)
167 }
168 //alert("onResponse this:"+WhAjaj.stringify(this));
169 //alert("val="+val);
170 // FIXME: this.url is hosed for login because of how i overload onResponse()
171 if( opt.url ) TheApp.jqe.textPath.val(opt.url.replace(WhAjaj.Connector.options.ajax.url,''));
172 TheApp.jqe.taResponse.val( val );
173 };
174 ajcb.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:\n"+WhAjaj.stringify(this.auth));
180 TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
181 };
182 TheApp.cgi.onLogout = function(){
183 TheApp.jqe.taResponse.val( "Logged out!" );
184 TheApp.jqe.currentAuthToken.text("Not logged in");
185 };
186 TheApp.cgi.whoami();
187 jQuery('#headerArea').click(function(){
188 jQuery(this).slideUp('fast',function(){
189 jQuery(this).remove();
190 });
191 });
192 });
193
194 </script>
195
196 </head>
197
198 <body>
199 <span id='ajaxNotification'></span>
200 <div id='headerArea'>
201 <h1>You know, for sending raw JSON requests to Fossil...</h1>
202
203 If you're actually using this page, then you know what you're doing and don't
204 need help text, hoverhelp, and a snazzy interface.
205
206 <br><br>
207
208
209 JSON API docs: <a href='https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit'>https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit</a>
210
211 </div><!-- #headerArea -->
212 See also: <a href='wiki-editor.html'>prototype wiki editor</a>.
213
214 <h2>Request...</h2>
215
216 Path: <input type='text' size='40' id='textPath' value='/json/HAI'/>
217 <input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
218 If the POST textarea is not empty then it will be posted with the request.
219 <hr/>
220 <strong>Quick-posts:</strong><br/>
221 <input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
222 <input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
223 <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
224 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
225 <input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
226 <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
227 <input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
228 <input type='button' value='g' onclick='TheApp.cgi.sendCommand("/json/g")' />
229
230 <br/>
231
232 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
233 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
234 <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
235 <input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
236 <input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/json/timeline/branch")' />
237
238 <br/>
239
240 <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
241 <input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand("/json/wiki/list",{verbose:1})' />
242 <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{name:"Fossil"})' />
243 <input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
244 <input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wiki/diff/e32ccdcda59e930c77c/e15992f475760cdf3a9")' />
245
246 <br/>
247
248 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
249 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
250 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
251 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
252 <input type='button' value='tag/add'
253 onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
254 <input type='button' value='tag/cancel'
255 onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
256 <input type='button' value='tag/find'
257 onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
258
259 <br/>
260
261 <input type='button' value='diff'
262 onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
263 <input type='button' value='diff/A/B'
264 onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />
265
266 <input type='button' value='query'
267 onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />
268
269 <input type='button' value='report list'
270 onclick='TheApp.cgi.sendCommand("/json/report/list")' />
271 <input type='button' value='report get'
272 onclick='TheApp.cgi.sendCommand("/json/report/get",2)' />
273
274 <input type='button' value='report run'
275 onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"})' />
276
277 <input type='button' value='config/get' onclick='TheApp.cgi.sendCommand("/json/config/get")' />
278
279 <!-- not yet ready...
280 <input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
--->
281
282 <!--
283 <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
284 <input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
285 <input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
286 <input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
--->
287 <hr/>
288 <b>Login:</b>
289 <br/>
290 <input type='button' value='Anon. PW' onclick='TheApp.cgi.sendCommand("/json/anonymousPassword")' />
291 <input type='button' value='Anon. PW+Login' onclick='TheApp.cgi.login()' />
292 <br/>
293 name:<input type='text' id='textUser' value='json-demo' size='12'/>
294 pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
295 <input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
296 <input type='button' value='logout' onclick='TheApp.cgi.logout()' />
297 <br/>
298 <span id='currentAuthToken' style='font-family:monospaced'></span>
299
300 <br/>
301
302 <hr/>
303
304 <table>
305 <tr>
306 <th>POST data</th>
307 <th>Request AJAJ options</th>
308 </tr>
309 <tr>
310 <td width='50%' valign='top'>
311 <textarea id='taRequest' rows='10' cols='50'></textarea>
312 </td>
313 <td width='50%' valign='top'>
314 <textarea id='taRequestOpt' rows='10' cols='40' readonly></textarea>
315 </td>
316 </tr>
317 <tr>
318 <th colspan='2'>Response <span id='timer'></span></th>
319 </tr>
320 <tr>
321 <td colspan='2' id='responseContainer' valign='top'>
322 <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
323 </td>
324 </tr>
325 </table>
326 <div></div>
327 <div></div>
328 <div></div>
329
330 </body></html>
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -1,332 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--->
 
 
 
 
 
 
--->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/js/fossil-ajaj.js
-274
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -1,274 +0,0 @@
1
-/**
2
- This file contains a WhAjaj extension for use with Fossil/JSON.
3
-
4
- Author: Stephan Beal ([email protected])
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;
25
-}
26
-
27
-FossilAjaj.prototype.generateRequestId = function() {
28
- return this.ajaj.generateRequestId();
29
-};
30
-
31
-/**
32
- Proxy for this.ajaj.sendRequest().
33
-*/
34
-FossilAjaj.prototype.sendRequest = function(req,opt) {
35
- return this.ajaj.sendRequest(req,opt);
36
-};
37
-
38
-/**
39
- Sends a command to the fossil back-end. Command should be the
40
- path part of the URL, e.g. /json/stat, payload is a request-specific
41
- value type (may often be null/undefined). ajajOpt is an optional object
42
- holding WhAjaj.sendRequest()-compatible options.
43
-
44
- This function constructs a Fossil/JSON request envelope based
45
- on the given arguments and adds this.auth.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.auth && this.auth.authToken) || ajajOpt.jsonp) {
52
- req = {
53
- payload:payload,
54
- requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55
- authToken:(this.auth ? this.auth.authToken : undefined),
56
- jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
57
- };
58
- }
59
- ajajOpt.method = req ? 'POST' : 'GET';
60
- // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
61
- if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
62
- this.ajaj.sendRequest(req,ajajOpt);
63
-};
64
-
65
-/**
66
- Sends a login request to the back-end.
67
-
68
- ajajOpt is an optional configuration object suitable for passing
69
- to sendCommand().
70
-
71
- After the response returns, this.auth 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) and any exceptions
85
- it throws are ignored.
86
-
87
-*/
88
-FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
89
- name = name || 'anonymous';
90
- var self = this;
91
- var loginReq = {
92
- name:name,
93
- password:pw
94
- };
95
- ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
96
- var oldOnResponse = ajajOpt.onResponse;
97
- ajajOpt.onResponse = function(resp,req) {
98
- var thisOpt = this;
99
- //alert('login response:\n'+WhAjaj.stringify(resp));
100
- if( resp && resp.payload ) {
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
- }
109
- if( WhAjaj.isFunction(oldOnResponse) ) {
110
- oldOnResponse.apply(thisOpt,[resp,req]);
111
- }
112
- };
113
- function doLogin(){
114
- //alert("Sending login request..."+WhAjaj.stringify(loginReq));
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();
130
- }
131
- }
132
- });
133
- }
134
- else doLogin();
135
-};
136
-
137
-/**
138
- Logs out of fossil, invaliding this login token.
139
-
140
- ajajOpt is an optional configuration object suitable for passing
141
- to sendCommand().
142
-
143
- If this object has an onLogout() function it is called (with
144
- no arguments) before the onResponse() handler is called.
145
- IFF the response succeeds then this.auth is unset.
146
-*/
147
-FossilAjaj.prototype.logout = function(ajajOpt) {
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
- self.auth = undefined;
154
- if( WhAjaj.isFunction( self.onLogout ) ){
155
- try{ self.onLogout(); }
156
- catch(e){}
157
- }
158
- if( WhAjaj.isFunction(oldOnResponse) ) {
159
- oldOnResponse.apply(thisOpt,[resp,req]);
160
- }
161
- };
162
- this.sendCommand('/json/logout', undefined, ajajOpt );
163
-};
164
-
165
-/**
166
- Sends a HAI request to the server. /json/HAI is an alias /json/version.
167
-
168
- ajajOpt is an optional configuration object suitable for passing
169
- to sendCommand().
170
-*/
171
-FossilAjaj.prototype.HAI = function(ajajOpt) {
172
- this.sendCommand('/json/HAI', undefined, ajajOpt);
173
-};
174
-
175
-
176
-/**
177
- Sends a /json/whoami request. Updates this.auth to contain
178
- the login info, removing them if the response does not contain
179
- that data.
180
-*/
181
-FossilAjaj.prototype.whoami = function(ajajOpt) {
182
- var self = this;
183
- ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
184
- var oldOnResponse = ajajOpt.onResponse;
185
- ajajOpt.onResponse = function(resp,req) {
186
- var thisOpt = this;
187
- if( resp && resp.payload ){
188
- if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
189
- self.auth = resp.payload;
190
- if( WhAjaj.isFunction(self.onLogin) ){
191
- self.onLogin();
192
- }
193
- }
194
- }
195
- else { delete self.auth; }
196
- if( WhAjaj.isFunction(oldOnResponse) ) {
197
- oldOnResponse.apply(thisOpt,[resp,req]);
198
- }
199
- };
200
- self.sendCommand('/json/whoami', undefined, ajajOpt);
201
-};
202
-
203
-/**
204
- EXPERIMENTAL concrete WhAjaj.Connector.sendImpl() implementation which
205
- uses Rhino to connect to a local fossil binary for input and output. Its
206
- signature and semantics are as described for
207
- WhAjaj.Connector.prototype.sendImpl(), with a few exceptions and
208
- additions:
209
-
210
- - It does not support timeouts or asynchronous mode.
211
-
212
- - The args.fossilBinary property must point to the local fossil binary
213
- (it need not be a complete path if fossil is in the $PATH). This
214
- function throws (without calling any request callbacks) if
215
- args.fossilBinary is not set. fossilBinary may be set on
216
- WhAjaj.Connector.options.ajax, in the FossilAjaj constructor call, as
217
- the ajax options parameter to any of the FossilAjaj.sendCommand() family
218
- of functions, or by setting
219
- aFossilAjajInstance.ajaj.options.fossilBinary on a specific
220
- FossilAjaj instance.
221
-
222
- - It uses the args.url field to create the "command" property of the
223
- request, constructs a request envelope, spawns a fossil process in JSON
224
- mode, feeds it the request envelope, and returns the response envelope
225
- via the same mechanisms defined for the HTTP-based implementations.
226
-
227
- The interface is otherwise compatible with the "normal"
228
- FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
229
- not back-end agnostic like the WhAjaj.sendImpl() interface intends).
230
-
231
-
232
-*/
233
-FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
234
- var self = this;
235
- request = request || {};
236
- if(!args.fossilBinary){
237
- throw new Error("fossilBinary is not set on AJAX options!");
238
- }
239
- var url = args.url.split('?')[0].split(/\/+/);
240
- if(url.length>1){
241
- // 3x shift(): protocol, host, 'json' part of path
242
- request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
243
- }
244
- delete args.url;
245
- //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));
246
- var json;
247
- try{
248
- var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
249
- var p = java.lang.Runtime.getRuntime().exec(pargs);
250
- var outs = p.getOutputStream();
251
- var osr = new java.io.OutputStreamWriter(outs);
252
- var osb = new java.io.BufferedWriter(osr);
253
-
254
- json = JSON.stringify(request);
255
- osb.write(json,0, json.length);
256
- osb.close();
257
- var ins = p.getInputStream();
258
- var isr = new java.io.InputStreamReader(ins);
259
- var br = new java.io.BufferedReader(isr);
260
- var line;
261
- json = [];
262
- while( null !== (line=br.readLine())){
263
- json.push(line);
264
- }
265
- ins.close();
266
- }catch(e){
267
- args.errorMessage = e.toString();
268
- WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
269
- return undefined;
270
- }
271
- json = json.join('');
272
- //print("READ IN JSON: "+json);
273
- WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
274
-}/*rhinoLocalBinary*/
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -1,274 +0,0 @@
1 /**
2 This file contains a WhAjaj extension for use with Fossil/JSON.
3
4 Author: Stephan Beal ([email protected])
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;
25 }
26
27 FossilAjaj.prototype.generateRequestId = function() {
28 return this.ajaj.generateRequestId();
29 };
30
31 /**
32 Proxy for this.ajaj.sendRequest().
33 */
34 FossilAjaj.prototype.sendRequest = function(req,opt) {
35 return this.ajaj.sendRequest(req,opt);
36 };
37
38 /**
39 Sends a command to the fossil back-end. Command should be the
40 path part of the URL, e.g. /json/stat, payload is a request-specific
41 value type (may often be null/undefined). ajajOpt is an optional object
42 holding WhAjaj.sendRequest()-compatible options.
43
44 This function constructs a Fossil/JSON request envelope based
45 on the given arguments and adds this.auth.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.auth && this.auth.authToken) || ajajOpt.jsonp) {
52 req = {
53 payload:payload,
54 requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55 authToken:(this.auth ? this.auth.authToken : undefined),
56 jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
57 };
58 }
59 ajajOpt.method = req ? 'POST' : 'GET';
60 // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
61 if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
62 this.ajaj.sendRequest(req,ajajOpt);
63 };
64
65 /**
66 Sends a login request to the back-end.
67
68 ajajOpt is an optional configuration object suitable for passing
69 to sendCommand().
70
71 After the response returns, this.auth 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) and any exceptions
85 it throws are ignored.
86
87 */
88 FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
89 name = name || 'anonymous';
90 var self = this;
91 var loginReq = {
92 name:name,
93 password:pw
94 };
95 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
96 var oldOnResponse = ajajOpt.onResponse;
97 ajajOpt.onResponse = function(resp,req) {
98 var thisOpt = this;
99 //alert('login response:\n'+WhAjaj.stringify(resp));
100 if( resp && resp.payload ) {
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 }
109 if( WhAjaj.isFunction(oldOnResponse) ) {
110 oldOnResponse.apply(thisOpt,[resp,req]);
111 }
112 };
113 function doLogin(){
114 //alert("Sending login request..."+WhAjaj.stringify(loginReq));
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();
130 }
131 }
132 });
133 }
134 else doLogin();
135 };
136
137 /**
138 Logs out of fossil, invaliding this login token.
139
140 ajajOpt is an optional configuration object suitable for passing
141 to sendCommand().
142
143 If this object has an onLogout() function it is called (with
144 no arguments) before the onResponse() handler is called.
145 IFF the response succeeds then this.auth is unset.
146 */
147 FossilAjaj.prototype.logout = function(ajajOpt) {
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 self.auth = undefined;
154 if( WhAjaj.isFunction( self.onLogout ) ){
155 try{ self.onLogout(); }
156 catch(e){}
157 }
158 if( WhAjaj.isFunction(oldOnResponse) ) {
159 oldOnResponse.apply(thisOpt,[resp,req]);
160 }
161 };
162 this.sendCommand('/json/logout', undefined, ajajOpt );
163 };
164
165 /**
166 Sends a HAI request to the server. /json/HAI is an alias /json/version.
167
168 ajajOpt is an optional configuration object suitable for passing
169 to sendCommand().
170 */
171 FossilAjaj.prototype.HAI = function(ajajOpt) {
172 this.sendCommand('/json/HAI', undefined, ajajOpt);
173 };
174
175
176 /**
177 Sends a /json/whoami request. Updates this.auth to contain
178 the login info, removing them if the response does not contain
179 that data.
180 */
181 FossilAjaj.prototype.whoami = function(ajajOpt) {
182 var self = this;
183 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
184 var oldOnResponse = ajajOpt.onResponse;
185 ajajOpt.onResponse = function(resp,req) {
186 var thisOpt = this;
187 if( resp && resp.payload ){
188 if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
189 self.auth = resp.payload;
190 if( WhAjaj.isFunction(self.onLogin) ){
191 self.onLogin();
192 }
193 }
194 }
195 else { delete self.auth; }
196 if( WhAjaj.isFunction(oldOnResponse) ) {
197 oldOnResponse.apply(thisOpt,[resp,req]);
198 }
199 };
200 self.sendCommand('/json/whoami', undefined, ajajOpt);
201 };
202
203 /**
204 EXPERIMENTAL concrete WhAjaj.Connector.sendImpl() implementation which
205 uses Rhino to connect to a local fossil binary for input and output. Its
206 signature and semantics are as described for
207 WhAjaj.Connector.prototype.sendImpl(), with a few exceptions and
208 additions:
209
210 - It does not support timeouts or asynchronous mode.
211
212 - The args.fossilBinary property must point to the local fossil binary
213 (it need not be a complete path if fossil is in the $PATH). This
214 function throws (without calling any request callbacks) if
215 args.fossilBinary is not set. fossilBinary may be set on
216 WhAjaj.Connector.options.ajax, in the FossilAjaj constructor call, as
217 the ajax options parameter to any of the FossilAjaj.sendCommand() family
218 of functions, or by setting
219 aFossilAjajInstance.ajaj.options.fossilBinary on a specific
220 FossilAjaj instance.
221
222 - It uses the args.url field to create the "command" property of the
223 request, constructs a request envelope, spawns a fossil process in JSON
224 mode, feeds it the request envelope, and returns the response envelope
225 via the same mechanisms defined for the HTTP-based implementations.
226
227 The interface is otherwise compatible with the "normal"
228 FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
229 not back-end agnostic like the WhAjaj.sendImpl() interface intends).
230
231
232 */
233 FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
234 var self = this;
235 request = request || {};
236 if(!args.fossilBinary){
237 throw new Error("fossilBinary is not set on AJAX options!");
238 }
239 var url = args.url.split('?')[0].split(/\/+/);
240 if(url.length>1){
241 // 3x shift(): protocol, host, 'json' part of path
242 request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
243 }
244 delete args.url;
245 //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));
246 var json;
247 try{
248 var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
249 var p = java.lang.Runtime.getRuntime().exec(pargs);
250 var outs = p.getOutputStream();
251 var osr = new java.io.OutputStreamWriter(outs);
252 var osb = new java.io.BufferedWriter(osr);
253
254 json = JSON.stringify(request);
255 osb.write(json,0, json.length);
256 osb.close();
257 var ins = p.getInputStream();
258 var isr = new java.io.InputStreamReader(ins);
259 var br = new java.io.BufferedReader(isr);
260 var line;
261 json = [];
262 while( null !== (line=br.readLine())){
263 json.push(line);
264 }
265 ins.close();
266 }catch(e){
267 args.errorMessage = e.toString();
268 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
269 return undefined;
270 }
271 json = json.join('');
272 //print("READ IN JSON: "+json);
273 WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
274 }/*rhinoLocalBinary*/
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -1,274 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/js/json2.js
-476
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -1,476 +0,0 @@
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
@@ -1,476 +0,0 @@
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
@@ -1,476 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/js/whajaj.js
-1221
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -1,1221 +0,0 @@
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
-
16
- This framework is directly derived from code originally found in
17
- http://code.google.com/p/jsonmessage, and later in
18
- http://whiki.wanderinghorse.net, where it contained quite a bit
19
- of application-specific logic. It was eventually (the 3rd time i
20
- needed it) split off into its own library to simplify inclusion
21
- into my many mini-projects.
22
-*/
23
-
24
-
25
-/**
26
- The WhAjaj function is primarily a namespace, and not intended
27
- to called or instantiated via the 'new' operator.
28
-*/
29
-function WhAjaj()
30
-{
31
-}
32
-
33
-/** Returns a millisecond Unix Epoch timestamp. */
34
-WhAjaj.msTimestamp = function()
35
-{
36
- return (new Date()).getTime();
37
-};
38
-
39
-/** Returns a Unix Epoch timestamp (in seconds) in integer format.
40
-
41
- Reminder to self: (1.1 %1.2) evaluates to a floating-point value
42
- in JS, and thus this implementation is less than optimal.
43
-*/
44
-WhAjaj.unixTimestamp = function()
45
-{
46
- var ts = (new Date()).getTime();
47
- return parseInt( ""+((ts / 1000) % ts) );
48
-};
49
-
50
-/**
51
- Returns true if v is-a Array instance.
52
-*/
53
-WhAjaj.isArray = function( v )
54
-{
55
- return (v &&
56
- (v instanceof Array) ||
57
- (Object.prototype.toString.call(v) === "[object Array]")
58
- );
59
- /* Reminders to self:
60
- typeof [] == "object"
61
- toString.call([]) == "[object Array]"
62
- ([]).toString() == empty
63
- */
64
-};
65
-
66
-/**
67
- Returns true if v is-a Object instance.
68
-*/
69
-WhAjaj.isObject = function( v )
70
-{
71
- return v &&
72
- (v instanceof Object) &&
73
- ('[object Object]' === Object.prototype.toString.apply(v) );
74
-};
75
-
76
-/**
77
- Returns true if v is-a Function instance.
78
-*/
79
-WhAjaj.isFunction = function(obj)
80
-{
81
- return obj
82
- && (
83
- (obj instanceof Function)
84
- || ('function' === typeof obj)
85
- || ("[object Function]" === Object.prototype.toString.call(obj))
86
- )
87
- ;
88
-};
89
-
90
-/**
91
- Parses window.location.search-style string into an object
92
- containing key/value pairs of URL arguments (already urldecoded).
93
-
94
- If the str argument is not passed (arguments.length==0) then
95
- window.location.search.substring(1) is used by default. If
96
- neither str is passed in nor window exists then false is returned.
97
-
98
- On success it returns an Object containing the key/value pairs
99
- parsed from the string. Keys which have no value are treated
100
- has having the boolean true value.
101
-
102
- FIXME: for keys in the form "name[]", build an array of results,
103
- like PHP does.
104
-
105
-*/
106
-WhAjaj.processUrlArgs = function(str) {
107
- if( 0 === arguments.length ) {
108
- if( ('undefined' === typeof window) ||
109
- !window.location ||
110
- !window.location.search ) return false;
111
- else str = (''+window.location.search).substring(1);
112
- }
113
- if( ! str ) return false;
114
- str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
115
- var args = {};
116
- var sp = str.split(/&+/);
117
- var rx = /^([^=]+)(=(.+))?/;
118
- var i, m;
119
- for( i in sp ) {
120
- m = rx.exec( sp[i] );
121
- if( ! m ) continue;
122
- args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
123
- }
124
- return args;
125
-};
126
-
127
-/**
128
- A simple wrapper around JSON.stringify(), using my own personal
129
- preferred values for the 2nd and 3rd parameters. To globally
130
- set its indentation level, assign WhAjaj.stringify.indent to
131
- an integer value (0 for no intendation).
132
-
133
- This function is intended only for human-readable output, not
134
- generic over-the-wire JSON output (where JSON.stringify(val) will
135
- produce smaller results).
136
-*/
137
-WhAjaj.stringify = function(val) {
138
- if( ! arguments.callee.indent ) arguments.callee.indent = 4;
139
- return JSON.stringify(val,0,arguments.callee.indent);
140
-};
141
-
142
-/**
143
- Each instance of this class holds state information for making
144
- AJAJ requests to a back-end system. While clients may use one
145
- "requester" object per connection attempt, for connections to the
146
- same back-end, using an instance configured for that back-end
147
- can simplify usage. This class is designed so that the actual
148
- connection-related details (i.e. _how_ it connects to the
149
- back-end) may be re-implemented to use a client's preferred
150
- connection mechanism (e.g. jQuery).
151
-
152
- The optional opt parameter may be an object with any (or all) of
153
- the properties documented for WhAjaj.Connector.options.ajax.
154
- Properties set here (or later via modification of the "options"
155
- property of this object) will be used in calls to
156
- WhAjaj.Connector.sendRequest(), and these override (normally) any
157
- options set in WhAjaj.Connector.options.ajax. Note that
158
- WhAjaj.Connector.sendRequest() _also_ takes an options object,
159
- and ones passed there will override, for purposes of that one
160
- request, any options passed in here or defined in
161
- WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
162
- and WhAjaj.Connector.prototype.sendRequest() for more details
163
- about the precedence of options.
164
-
165
- Sample usage:
166
-
167
- @code
168
- // Set up common connection-level options:
169
- var cgi = new WhAjaj.Connector({
170
- url: '/cgi-bin/my.cgi',
171
- timeout:10000,
172
- onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
173
- onError(req,opt) {
174
- alert(opt.errorMessage);
175
- }
176
- });
177
- // Any of those options may optionally be set globally in
178
- // WhAjaj.Connector.options.ajax (onError(), beforeSend(), and afterSend()
179
- // are often easiest/most useful to set globally).
180
-
181
- // Get list of pages...
182
- cgi.sendRequest( null, {
183
- onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
184
- });
185
- @endcode
186
-
187
- For common request types, clients can add functions to this
188
- object which act as wrappers for backend-specific functionality. As
189
- a simple example:
190
-
191
- @code
192
- cgi.login = function(name,pw,ajajOpt) {
193
- this.sendRequest(
194
- {command:"json/login",
195
- name:name,
196
- password:pw
197
- }, ajajOpt );
198
- };
199
- @endcode
200
-
201
- TODOs:
202
-
203
- - Caching of page-load requests, with a configurable lifetime.
204
-
205
- - Use-cases like the above login() function are a tiny bit
206
- problematic to implement when each request has a different URL
207
- path (i know this from the whiki and fossil implementations).
208
- This is partly a side-effect of design descisions made back in
209
- the very first days of this code's life. i need to go through
210
- and see where i can bend those conventions a bit (where it won't
211
- break my other apps unduly).
212
-*/
213
-WhAjaj.Connector = function(opt)
214
-{
215
- if(WhAjaj.isObject(opt)) this.options = opt;
216
- //TODO?: this.$cache = {};
217
-};
218
-
219
-/**
220
- The core options used by WhAjaj.Connector instances for performing
221
- network operations. These options can (and some _should_)
222
- be changed by a client application. They can also be changed
223
- on specific instances of WhAjaj.Connector, but for most applications
224
- it is simpler to set them here and not have to bother with configuring
225
- each WhAjaj.Connector instance. Apps which use multiple back-ends at one time,
226
- however, will need to customize each instance for a given back-end.
227
-*/
228
-WhAjaj.Connector.options = {
229
- /**
230
- A (meaningless) prefix to apply to WhAjaj.Connector-generated
231
- request IDs.
232
- */
233
- requestIdPrefix:'WhAjaj.Connector-',
234
- /**
235
- Default options for WhAjaj.Connector.sendRequest() connection
236
- parameters. This object holds only connection-related
237
- options and callbacks (all optional), and not options
238
- related to the required JSON structure of any given request.
239
- i.e. the page name used in a get-page request are not set
240
- here but are specified as part of the request object.
241
-
242
- These connection options are a "normalized form" of options
243
- often found in various AJAX libraries like jQuery,
244
- Prototype, dojo, etc. This approach allows us to swap out
245
- the real connection-related parts by writing a simple proxy
246
- which transforms our "normalized" form to the
247
- backend-specific form. For examples, see the various
248
- implementations stored in WhAjaj.Connector.sendImpls.
249
-
250
- The following callback options are, in practice, almost
251
- always set globally to some app-wide defaults:
252
-
253
- - onError() to report errors using a common mechanism.
254
- - beforeSend() to start a visual activity notification
255
- - afterSend() to disable the visual activity notification
256
-
257
- However, be aware that if any given WhAjaj.Connector instance is
258
- given its own before/afterSend callback then those will
259
- override these. Mixing shared/global and per-instance
260
- callbacks can potentially lead to confusing results if, e.g.,
261
- the beforeSend() and afterSend() functions have side-effects
262
- but are not used with their proper before/after partner.
263
-
264
- TODO: rename this to 'ajaj' (the name is historical). The
265
- problem with renaming it is is that the word 'ajax' is
266
- pretty prevelant in the source tree, so i can't globally
267
- swap it out.
268
- */
269
- ajax: {
270
- /**
271
- URL of the back-end server/CGI.
272
- */
273
- url: '/some/path',
274
-
275
- /**
276
- Connection method. Some connection-related functions might
277
- override any client-defined setting.
278
-
279
- Must be one of 'GET' or 'POST'. For custom connection
280
- implementation, it may optionally be some
281
- implementation-specified value.
282
-
283
- Normally the API can derive this value automatically - if the
284
- request uses JSON data it is POSTed, else it is GETted.
285
- */
286
- method:'GET',
287
-
288
- /**
289
- A hint whether to run the operation asynchronously or
290
- not. Not all concrete WhAjaj.Connector.sendImpl()
291
- implementations can support this. Interestingly, at
292
- least one popular AJAX toolkit does not document
293
- supporting _synchronous_ AJAX operations. All common
294
- browser-side implementations support async operation, but
295
- non-browser implementations might not.
296
- */
297
- asynchronous:true,
298
-
299
- /**
300
- A HTTP authentication login name for the AJAX
301
- connection. Not all concrete WhAjaj.Connector.sendImpl()
302
- implementations can support this.
303
- */
304
- loginName:undefined,
305
-
306
- /**
307
- An HTTP authentication login password for the AJAJ
308
- connection. Not all concrete WhAjaj.Connector.sendImpl()
309
- implementations can support this.
310
- */
311
- loginPassword:undefined,
312
-
313
- /**
314
- A connection timeout, in milliseconds, for establishing
315
- an AJAJ connection. Not all concrete
316
- WhAjaj.Connector.sendImpl() implementations can support this.
317
- */
318
- timeout:10000,
319
-
320
- /**
321
- If an AJAJ request receives JSON data from the back-end,
322
- that data is passed as a plain Object as the response
323
- parameter (exception: in jsonp mode it is passed a
324
- string (why???)). The initiating request object is
325
- passed as the second parameter, but clients can normally
326
- ignore it (only those which need a way to map specific
327
- requests to responses will need it). The 3rd parameter
328
- is the same as the 'this' object for the context of the
329
- callback, but is provided because the instance-level
330
- callbacks (set in (WhAjaj.Connector instance).callbacks,
331
- require it in some cases (because their 'this' is
332
- different!).
333
-
334
- Note that the response might contain error information
335
- which comes from the back-end. The difference between
336
- this error info and the info passed to the onError()
337
- callback is that this data indicates an
338
- application-level error, whereas onError() is used to
339
- report connection-level problems or when the backend
340
- produces non-JSON data (which, when not in jsonp mode,
341
- is unexpected and is as fatal to the request as a
342
- connection error).
343
- */
344
- onResponse: function(response, request, opt){},
345
-
346
- /**
347
- If an AJAX request fails to establish a connection or it
348
- receives non-JSON data from the back-end, this function
349
- is called (e.g. timeout error or host name not
350
- resolvable). It is passed the originating request and the
351
- "normalized" connection parameters used for that
352
- request. The connectOpt object "should" (or "might")
353
- have an "errorMessage" property which describes the
354
- nature of the problem.
355
-
356
- Clients will almost always want to replace the default
357
- implementation with something which integrates into
358
- their application.
359
- */
360
- onError: function(request, connectOpt)
361
- {
362
- alert('AJAJ request failed:\n'
363
- +'Connection information:\n'
364
- +JSON.stringify(connectOpt,0,4)
365
- );
366
- },
367
-
368
- /**
369
- Called before each connection attempt is made. Clients
370
- can use this to, e.g., enable a visual "network activity
371
- notification" for the user. It is passed the original
372
- request object and the normalized connection parameters
373
- for the request. If this function changes opt, those
374
- changes _are_ applied to the subsequent request. If this
375
- function throws, neither the onError() nor afterSend()
376
- callbacks are triggered and WhAjaj.Connector.sendImpl()
377
- propagates the exception back to the caller.
378
- */
379
- beforeSend: function(request,opt){},
380
-
381
- /**
382
- Called after an AJAJ connection attempt completes,
383
- regardless of success or failure. Passed the same
384
- parameters as beforeSend() (see that function for
385
- details).
386
-
387
- Here's an example of setting up a visual notification on
388
- ajax operations using jQuery (but it's also easy to do
389
- without jQuery as well):
390
-
391
- @code
392
- function startAjaxNotif(req,opt) {
393
- var me = arguments.callee;
394
- var c = ++me.ajaxCount;
395
- me.element.text( c + " pending AJAX operation(s)..." );
396
- if( 1 == c ) me.element.stop().fadeIn();
397
- }
398
- startAjaxNotif.ajaxCount = 0.
399
- startAjaxNotif.element = jQuery('#whikiAjaxNotification');
400
-
401
- function endAjaxNotif() {
402
- var c = --startAjaxNotif.ajaxCount;
403
- startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
404
- if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
405
- }
406
- @endcode
407
-
408
- Set the beforeSend/afterSend properties to those
409
- functions to enable the notifications by default.
410
- */
411
- afterSend: function(request,opt){},
412
-
413
- /**
414
- If jsonp is a string then the WhAjaj-internal response
415
- handling code ASSUMES that the response contains a JSONP-style
416
- construct and eval()s it after afterSend() but before onResponse().
417
- In this case, onResponse() will get a string value for the response
418
- instead of a response object parsed from JSON.
419
- */
420
- jsonp:undefined,
421
- /**
422
- Don't use yet. Planned future option.
423
- */
424
- propagateExceptions:false
425
- }
426
-};
427
-
428
-
429
-/**
430
- WhAjaj.Connector.prototype.callbacks defines callbacks analog
431
- to the onXXX callbacks defined in WhAjaj.Connector.options.ajax,
432
- with two notable differences:
433
-
434
- 1) these callbacks, if set, are called in addition to any
435
- request-specific callback. The intention is to allow a framework to set
436
- "framework-level" callbacks which should be called independently of the
437
- request-specific callbacks (without interfering with them, e.g.
438
- requiring special re-forwarding features).
439
-
440
- 2) The 'this' object in these callbacks is the Connector instance
441
- associated with the callback, whereas the "other" onXXX form has its
442
- "ajax options" object as its this.
443
-
444
- When this API says that an onXXX callback will be called for a request,
445
- both the request's onXXX (if set) and this one (if set) will be called.
446
-*/
447
-WhAjaj.Connector.prototype.callbacks = {};
448
-/**
449
- Instance-specific values for AJAJ-level properties (as opposed to
450
- application-level request properties). Options set here "override" those
451
- specified in WhAjaj.Connector.options.ajax and are "overridden" by
452
- options passed to sendRequest().
453
-*/
454
-WhAjaj.Connector.prototype.options = {};
455
-
456
-
457
-/**
458
- Tries to find the given key in any of the following, returning
459
- the first match found: opt, this.options, WhAjaj.Connector.options.ajax.
460
-
461
- Returns undefined if key is not found.
462
-*/
463
-WhAjaj.Connector.prototype.derivedOption = function(key,opt) {
464
- var v = opt ? opt[key] : undefined;
465
- if( undefined !== v ) return v;
466
- else v = this.options[key];
467
- if( undefined !== v ) return v;
468
- else v = WhAjaj.Connector.options.ajax[key];
469
- return v;
470
-};
471
-
472
-/**
473
- Returns a unique string on each call containing a generic
474
- reandom request identifier string. This is not used by the core
475
- API but can be used by client code to generate unique IDs for
476
- each request (if needed).
477
-
478
- The exact format is unspecified and may change in the future.
479
-
480
- Request IDs can be used by clients to "match up" responses to
481
- specific requests if needed. In practice, however, they are
482
- seldom, if ever, needed. When passing several concurrent
483
- requests through the same response callback, it might be useful
484
- for some clients to be able to distinguish, possibly re-routing
485
- them through other handlers based on the originating request type.
486
-
487
- If this.options.requestIdPrefix or
488
- WhAjaj.Connector.options.requestIdPrefix is set then that text
489
- is prefixed to the returned string.
490
-*/
491
-WhAjaj.Connector.prototype.generateRequestId = function()
492
-{
493
- if( undefined === arguments.callee.sequence )
494
- {
495
- arguments.callee.sequence = 0;
496
- }
497
- var pref = this.options.requestIdPrefix || WhAjaj.Connector.options.requestIdPrefix || '';
498
- return pref +
499
- WhAjaj.msTimestamp() +
500
- '/'+(Math.round( Math.random() * 100000000) )+
501
- ':'+(++arguments.callee.sequence);
502
-};
503
-
504
-/**
505
- Copies (SHALLOWLY) all properties in opt to this.options.
506
-*/
507
-WhAjaj.Connector.prototype.addOptions = function(opt) {
508
- var k, v;
509
- for( k in opt ) {
510
- if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
511
- this.options[k] = opt[k];
512
- }
513
- return this.options;
514
-};
515
-
516
-/**
517
- An internal helper object which holds several functions intended
518
- to simplify the creation of concrete communication channel
519
- implementations for WhAjaj.Connector.sendImpl(). These operations
520
- take care of some of the more error-prone parts of ensuring that
521
- onResponse(), onError(), etc. callbacks are called consistently
522
- using the same rules.
523
-*/
524
-WhAjaj.Connector.sendHelper = {
525
- /**
526
- opt is assumed to be a normalized set of
527
- WhAjaj.Connector.sendRequest() options. This function
528
- creates a url by concatenating opt.url and some form of
529
- opt.urlParam.
530
-
531
- If opt.urlParam is an object or string then it is appended
532
- to the url. An object is assumed to be a one-dimensional set
533
- of simple (urlencodable) key/value pairs, and not larger
534
- data structures. A string value is assumed to be a
535
- well-formed, urlencoded set of key/value pairs separated by
536
- '&' characters.
537
-
538
- The new/normalized URL is returned (opt is not modified). If
539
- opt.urlParam is not set then opt.url is returned (or an
540
- empty string if opt.url is itself a false value).
541
-
542
- TODO: if opt is-a Object and any key points to an array,
543
- build up a list of keys in the form "keyname[]". We could
544
- arguably encode sub-objects like "keyname[subkey]=...", but
545
- i don't know if that's conventions-compatible with other
546
- frameworks.
547
- */
548
- normalizeURL: function(opt) {
549
- var u = opt.url || '';
550
- if( opt.urlParam ) {
551
- var addQ = (u.indexOf('?') >= 0) ? false : true;
552
- var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
553
- var tail = '';
554
- if( WhAjaj.isObject(opt.urlParam) ) {
555
- var li = [], k;
556
- for( k in opt.urlParam) {
557
- li.push( k+'='+encodeURIComponent( opt.urlParam[k] ) );
558
- }
559
- tail = li.join('&');
560
- }
561
- else if( 'string' === typeof opt.urlParam ) {
562
- tail = opt.urlParam;
563
- }
564
- u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
565
- }
566
- return u;
567
- },
568
- /**
569
- Should be called by WhAjaj.Connector.sendImpl()
570
- implementations after a response has come back. This
571
- function takes care of most of ensuring that framework-level
572
- conventions involving WhAjaj.Connector.options.ajax
573
- properties are followed.
574
-
575
- The request argument must be the original request passed to
576
- the sendImpl() function. It may legally be null for GET requests.
577
-
578
- The opt object should be the normalized AJAX options used
579
- for the connection.
580
-
581
- The resp argument may be either a plain Object or a string
582
- (in which case it is assumed to be JSON).
583
-
584
- The 'this' object for this call MUST be a WhAjaj.Connector
585
- instance in order for callback processing to work properly.
586
-
587
- This function takes care of the following:
588
-
589
- - Calling opt.afterSend()
590
-
591
- - If resp is a string, de-JSON-izing it to an object.
592
-
593
- - Calling opt.onResponse()
594
-
595
- - Calling opt.onError() in several common (potential) error
596
- cases.
597
-
598
- - If resp is-a String and opt.jsonp then resp is assumed to be
599
- a JSONP-form construct and is eval()d BEFORE opt.onResponse()
600
- is called. It is arguable to eval() it first, but the logic
601
- integrates better with the non-jsonp handler.
602
-
603
- The sendImpl() should return immediately after calling this.
604
-
605
- The sendImpl() must call only one of onSendSuccess() or
606
- onSendError(). It must call one of them or it must implement
607
- its own response/error handling, which is not recommended
608
- because getting the documented semantics of the
609
- onError/onResponse/afterSend handling correct can be tedious.
610
- */
611
- onSendSuccess:function(request,resp,opt) {
612
- var cb = this.callbacks || {};
613
- if( WhAjaj.isFunction(cb.afterSend) ) {
614
- try {cb.afterSend( request, opt );}
615
- catch(e){}
616
- }
617
- if( WhAjaj.isFunction(opt.afterSend) ) {
618
- try {opt.afterSend( request, opt );}
619
- catch(e){}
620
- }
621
- function doErr(){
622
- if( WhAjaj.isFunction(cb.onError) ) {
623
- try {cb.onError( request, opt );}
624
- catch(e){}
625
- }
626
- if( WhAjaj.isFunction(opt.onError) ) {
627
- try {opt.onError( request, opt );}
628
- catch(e){}
629
- }
630
- }
631
- if( ! resp ) {
632
- opt.errorMessage = "Sending of request succeeded but returned no data!";
633
- doErr();
634
- return false;
635
- }
636
-
637
- if( 'string' === typeof resp ) {
638
- try {
639
- resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
640
- } catch(e) {
641
- opt.errorMessage = e.toString();
642
- doErr();
643
- return;
644
- }
645
- }
646
- try {
647
- if( WhAjaj.isFunction( cb.onResponse ) ) {
648
- cb.onResponse( resp, request, opt );
649
- }
650
- if( WhAjaj.isFunction( opt.onResponse ) ) {
651
- opt.onResponse( resp, request, opt );
652
- }
653
- return true;
654
- }
655
- catch(e) {
656
- opt.errorMessage = "Exception while handling inbound JSON response:\n"
657
- + e
658
- +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
659
- ;
660
- ;
661
- doErr();
662
- return false;
663
- }
664
- },
665
- /**
666
- Should be called by sendImpl() implementations after a response
667
- has failed to connect (e.g. could not resolve host or timeout
668
- reached). This function takes care of most of ensuring that
669
- framework-level conventions involving WhAjaj.Connector.options.ajax
670
- properties are followed.
671
-
672
- The request argument must be the original request passed to
673
- the sendImpl() function. It may legally be null for GET
674
- requests.
675
-
676
- The 'this' object for this call MUST be a WhAjaj.Connector
677
- instance in order for callback processing to work properly.
678
-
679
- The opt object should be the normalized AJAX options used
680
- for the connection. By convention, the caller of this
681
- function "should" set opt.errorMessage to contain a
682
- human-readable description of the error.
683
-
684
- The sendImpl() should return immediately after calling this. The
685
- return value from this function is unspecified.
686
- */
687
- onSendError: function(request,opt) {
688
- var cb = this.callbacks || {};
689
- if( WhAjaj.isFunction(cb.afterSend) ) {
690
- try {cb.afterSend( request, opt );}
691
- catch(e){}
692
- }
693
- if( WhAjaj.isFunction(opt.afterSend) ) {
694
- try {opt.afterSend( request, opt );}
695
- catch(e){}
696
- }
697
- if( WhAjaj.isFunction( cb.onError ) ) {
698
- try {cb.onError( request, opt );}
699
- catch(e) {/*ignore*/}
700
- }
701
- if( WhAjaj.isFunction( opt.onError ) ) {
702
- try {opt.onError( request, opt );}
703
- catch(e) {/*ignore*/}
704
- }
705
- }
706
-};
707
-
708
-/**
709
- WhAjaj.Connector.sendImpls holds several concrete
710
- implementations of WhAjaj.Connector.prototype.sendImpl(). To use
711
- a specific implementation by default assign
712
- WhAjaj.Connector.prototype.sendImpl to one of these functions.
713
-
714
- The functions defined here require that the 'this' object be-a
715
- WhAjaj.Connector instance.
716
-
717
- Historical notes:
718
-
719
- a) We once had an implementation based on Prototype, but that
720
- library just pisses me off (they change base-most types'
721
- prototypes, introducing side-effects in client code which
722
- doesn't even use Prototype). The Prototype version at the time
723
- had a serious toJSON() bug which caused empty arrays to
724
- serialize as the string "[]", which broke a bunch of my code.
725
- (That has been fixed in the mean time, but i don't use
726
- Prototype.)
727
-
728
- b) We once had an implementation for the dojo library,
729
-
730
- If/when the time comes to add Prototype/dojo support, we simply
731
- need to port:
732
-
733
- http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js
734
-
735
- (search that file for "dojo" and "Prototype") to this tree. That
736
- code is this code's generic grandfather and they are still very
737
- similar, so a port is trivial.
738
-
739
-*/
740
-WhAjaj.Connector.sendImpls = {
741
- /**
742
- This is a concrete implementation of
743
- WhAjaj.Connector.prototype.sendImpl() which uses the
744
- environment's native XMLHttpRequest class to send whiki
745
- requests and fetch the responses.
746
-
747
- The only argument must be a connection properties object, as
748
- constructed by WhAjaj.Connector.normalizeAjaxParameters().
749
-
750
- If window.firebug is set then window.firebug.watchXHR() is
751
- called to enable monitoring of the XMLHttpRequest object.
752
-
753
- This implementation honors the loginName and loginPassword
754
- connection parameters.
755
-
756
- Returns the XMLHttpRequest object.
757
-
758
- This implementation requires that the 'this' object be-a
759
- WhAjaj.Connector.
760
-
761
- This implementation uses setTimeout() to implement the
762
- timeout support, and thus the JS engine must provide that
763
- functionality.
764
- */
765
- XMLHttpRequest: function(request, args)
766
- {
767
- var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
768
- var xhr = new XMLHttpRequest();
769
- var startTime = (new Date()).getTime();
770
- var timeout = args.timeout || 10000/*arbitrary!*/;
771
- var hitTimeout = false;
772
- var done = false;
773
- var tmid /* setTimeout() ID */;
774
- var whself = this;
775
- function handleTimeout()
776
- {
777
- hitTimeout = true;
778
- if( ! done )
779
- {
780
- var now = (new Date()).getTime();
781
- try { xhr.abort(); } catch(e) {/*ignore*/}
782
- // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
783
- args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
784
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
785
- }
786
- return;
787
- }
788
- function onStateChange()
789
- { // reminder to self: apparently 'this' is-not-a XHR :/
790
- if( hitTimeout )
791
- { /* we're too late - the error was already triggered. */
792
- return;
793
- }
794
-
795
- if( 4 == xhr.readyState )
796
- {
797
- done = true;
798
- if( tmid )
799
- {
800
- clearTimeout( tmid );
801
- tmid = null;
802
- }
803
- if( (xhr.status >= 200) && (xhr.status < 300) )
804
- {
805
- WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
806
- return;
807
- }
808
- else
809
- {
810
- if( undefined === args.errorMessage )
811
- {
812
- args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
813
- +"["+args.url+"]: "
814
- +"Status text=["+xhr.statusText+"]"
815
- ;
816
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
817
- }
818
- else { /*maybe it was was set by the timeout handler. */ }
819
- return;
820
- }
821
- }
822
- };
823
-
824
- xhr.onreadystatechange = onStateChange;
825
- if( ('undefined'!==(typeof window)) && ('firebug' in window) && ('watchXHR' in window.firebug) )
826
- { /* plug in to firebug lite's XHR monitor... */
827
- window.firebug.watchXHR( xhr );
828
- }
829
- try
830
- {
831
- //alert( JSON.stringify( args ));
832
- function xhrOpen()
833
- {
834
- if( ('loginName' in args) && args.loginName )
835
- {
836
- xhr.open( args.method, args.url, args.asynchronous, args.loginName, args.loginPassword );
837
- }
838
- else
839
- {
840
- xhr.open( args.method, args.url, args.asynchronous );
841
- }
842
- }
843
- if( json && ('POST' === args.method.toUpperCase()) )
844
- {
845
- xhrOpen();
846
- xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
847
- // Google Chrome warns that it refuses to set these
848
- // "unsafe" headers (his words, not mine):
849
- // xhr.setRequestHeader("Content-length", json.length);
850
- // xhr.setRequestHeader("Connection", "close");
851
- xhr.send( json );
852
- }
853
- else /* assume GET */
854
- {
855
- xhrOpen();
856
- xhr.send(null);
857
- }
858
- tmid = setTimeout( handleTimeout, timeout );
859
- return xhr;
860
- }
861
- catch(e)
862
- {
863
- args.errorMessage = e.toString();
864
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
865
- return undefined;
866
- }
867
- }/*XMLHttpRequest()*/,
868
- /**
869
- This is a concrete implementation of
870
- WhAjaj.Connector.prototype.sendImpl() which uses the jQuery
871
- AJAX API to send requests and fetch the responses.
872
-
873
- The first argument may be either null/false, an Object
874
- containing toJSON-able data to post to the back-end, or such an
875
- object in JSON string form.
876
-
877
- The second argument must be a connection properties object, as
878
- constructed by WhAjaj.Connector.normalizeAjaxParameters().
879
-
880
- If window.firebug is set then window.firebug.watchXHR() is
881
- called to enable monitoring of the XMLHttpRequest object.
882
-
883
- This implementation honors the loginName and loginPassword
884
- connection parameters.
885
-
886
- Returns the XMLHttpRequest object.
887
-
888
- This implementation requires that the 'this' object be-a
889
- WhAjaj.Connector.
890
- */
891
- jQuery:function(request,args)
892
- {
893
- var data = request || undefined;
894
- var whself = this;
895
- if( data ) {
896
- if('string'!==typeof data) {
897
- try {
898
- data = JSON.stringify(data);
899
- }
900
- catch(e) {
901
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
902
- return;
903
- }
904
- }
905
- }
906
- var ajopt = {
907
- url: args.url,
908
- data: data,
909
- type: args.method,
910
- async: args.asynchronous,
911
- password: (undefined !== args.loginPassword) ? args.loginPassword : undefined,
912
- username: (undefined !== args.loginName) ? args.loginName : undefined,
913
- contentType: 'application/json; charset=utf-8',
914
- error: function(xhr, textStatus, errorThrown)
915
- {
916
- //this === the options for this ajax request
917
- args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
918
- +"Status text=["+textStatus+"]"
919
- +(errorThrown ? ("Error=["+errorThrown+"]") : "")
920
- ;
921
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
922
- },
923
- success: function(data)
924
- {
925
- WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
926
- },
927
- /* Set dataType=text instead of json to keep jQuery from doing our carefully
928
- written response handling for us.
929
- */
930
- dataType: 'text'
931
- };
932
- if( undefined !== args.timeout )
933
- {
934
- ajopt.timeout = args.timeout;
935
- }
936
- try
937
- {
938
- return jQuery.ajax(ajopt);
939
- }
940
- catch(e)
941
- {
942
- args.errorMessage = e.toString();
943
- WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
944
- return undefined;
945
- }
946
- }/*jQuery()*/,
947
- /**
948
- This is a concrete implementation of
949
- WhAjaj.Connector.prototype.sendImpl() which uses the rhino
950
- Java API to send requests and fetch the responses.
951
-
952
- Limitations vis-a-vis the interface:
953
-
954
- - timeouts are not supported.
955
-
956
- - asynchronous mode is not supported because implementing it
957
- requires the ability to kill a running thread (which is deprecated
958
- in the Java API).
959
-
960
- TODOs:
961
-
962
- - add socket timeouts.
963
-
964
- - support HTTP proxy.
965
-
966
- The Java APIs support this, it just hasn't been added here yet.
967
- */
968
- rhino:function(request,args)
969
- {
970
- var self = this;
971
- var data = request || undefined;
972
- if( data ) {
973
- if('string'!==typeof data) {
974
- try {
975
- data = JSON.stringify(data);
976
- }
977
- catch(e) {
978
- WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
979
- return;
980
- }
981
- }
982
- }
983
- var url;
984
- var con;
985
- var IO = new JavaImporter(java.io);
986
- var wr;
987
- var rd, ln, json = [];
988
- function setIncomingCookies(list){
989
- if(!list || !list.length) return;
990
- if( !self.cookies ) self.cookies = {};
991
- var k, v, i;
992
- for( i = 0; i < list.length; ++i ){
993
- v = list[i].split('=',2);
994
- k = decodeURIComponent(v[0])
995
- v = v[0] ? decodeURIComponent(v[0].split(';',2)[0]) : null;
996
- //print("RECEIVED COOKIE: "+k+"="+v);
997
- if(!v) {
998
- delete self.cookies[k];
999
- continue;
1000
- }else{
1001
- self.cookies[k] = v;
1002
- }
1003
- }
1004
- };
1005
- function setOutboundCookies(conn){
1006
- if(!self.cookies) return;
1007
- var k, v;
1008
- for( k in self.cookies ){
1009
- if(!self.cookies.hasOwnProperty(k)) continue /*kludge for broken JS libs*/;
1010
- v = self.cookies[k];
1011
- conn.addRequestProperty("Cookie", encodeURIComponent(k)+'='+encodeURIComponent(v));
1012
- //print("SENDING COOKIE: "+k+"="+v);
1013
- }
1014
- };
1015
- try{
1016
- url = new java.net.URL( args.url )
1017
- con = url.openConnection(/*FIXME: add proxy support!*/);
1018
- con.setRequestProperty("Accept-Charset","utf-8");
1019
- setOutboundCookies(con);
1020
- if(data){
1021
- con.setRequestProperty("Content-Type","application/json; charset=utf-8");
1022
- con.setDoOutput( true );
1023
- wr = new IO.OutputStreamWriter(con.getOutputStream())
1024
- wr.write(data);
1025
- wr.flush();
1026
- wr.close();
1027
- wr = null;
1028
- //print("POSTED: "+data);
1029
- }
1030
- rd = new IO.BufferedReader(new IO.InputStreamReader(con.getInputStream()));
1031
- //var skippedHeaders = false;
1032
- while ((line = rd.readLine()) !== null) {
1033
- //print("LINE: "+line);
1034
- //if(!line.length && !skippedHeaders){
1035
- // skippedHeaders = true;
1036
- // json = [];
1037
- // continue;
1038
- //}
1039
- json.push(line);
1040
- }
1041
- setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
1042
- }catch(e){
1043
- args.errorMessage = e.toString();
1044
- WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
1045
- return undefined;
1046
- }
1047
- try { if(wr) wr.close(); } catch(e) { /*ignore*/}
1048
- try { if(rd) rd.close(); } catch(e) { /*ignore*/}
1049
- json = json.join('');
1050
- //print("READ IN JSON: "+json);
1051
- WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
1052
- }/*rhino*/
1053
-};
1054
-
1055
-/**
1056
- An internal function which takes an object containing properties
1057
- for a WhAjaj.Connector network request. This function creates a new
1058
- object containing a superset of the properties from:
1059
-
1060
- a) opt
1061
- b) this.options
1062
- c) WhAjaj.Connector.options.ajax
1063
-
1064
- in that order, using the first one it finds.
1065
-
1066
- All non-function properties are _deeply_ copied via JSON cloning
1067
- in order to prevent accidental "cross-request pollenation" (been
1068
- there, done that). Functions cannot be cloned and are simply
1069
- copied by reference.
1070
-
1071
- This function throws if JSON-copying one of the options fails
1072
- (e.g. due to cyclic data structures).
1073
-
1074
- Reminder to self: this function does not "normalize" opt.urlParam
1075
- by encoding it into opt.url, mainly for historical reasons, but
1076
- also because that behaviour was specifically undesirable in this
1077
- code's genetic father.
1078
-*/
1079
-WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
1080
-{
1081
- var rc = {};
1082
- function merge(k,v)
1083
- {
1084
- if( rc.hasOwnProperty(k) ) return;
1085
- else if( WhAjaj.isFunction(v) ) {}
1086
- else if( WhAjaj.isObject(v) ) v = JSON.parse( JSON.stringify(v) );
1087
- rc[k]=v;
1088
- }
1089
- function cp(obj) {
1090
- if( ! WhAjaj.isObject(obj) ) return;
1091
- var k;
1092
- for( k in obj ) {
1093
- if( ! obj.hasOwnProperty(k) ) continue /* i will always hate the Prototype designers for this. */;
1094
- merge(k, obj[k]);
1095
- }
1096
- }
1097
- cp( opt );
1098
- cp( this.options );
1099
- cp( WhAjaj.Connector.options.ajax );
1100
- // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
1101
- return rc;
1102
-};
1103
-
1104
-/**
1105
- This is the generic interface for making calls to a back-end
1106
- JSON-producing request handler. It is a simple wrapper around
1107
- WhAjaj.Connector.prototype.sendImpl(), which just normalizes the
1108
- connection options for sendImpl() and makes sure that
1109
- opt.beforeSend() is (possibly) called.
1110
-
1111
- The request parameter must either be false/null/empty or a
1112
- fully-populated JSON-able request object (which will be sent as
1113
- unencoded application/json text), depending on the type of
1114
- request being made. It is never semantically legal (in this API)
1115
- for request to be a string/number/true/array value. As a rule,
1116
- only POST requests use the request data. GET requests should
1117
- encode their data in opt.url or opt.urlParam (see below).
1118
-
1119
- opt must contain the network-related parameters for the request.
1120
- Paramters _not_ set in opt are pulled from this.options or
1121
- WhAjaj.Connector.options.ajax (in that order, using the first
1122
- value it finds). Thus the set of connection-level options used
1123
- for the request are a superset of those various sources.
1124
-
1125
- The "normalized" (or "superimposed") opt object's URL may be
1126
- modified before the request is sent, as follows:
1127
-
1128
- if opt.urlParam is a string then it is assumed to be properly
1129
- URL-encoded parameters and is appended to the opt.url. If it is
1130
- an Object then it is assumed to be a one-dimensional set of
1131
- key/value pairs with simple values (numbers, strings, booleans,
1132
- null, and NOT objects/arrays). The keys/values are URL-encoded
1133
- and appended to the URL.
1134
-
1135
- The beforeSend() callback (see below) can modify the options
1136
- object before the request attempt is made.
1137
-
1138
- The callbacks in the normalized opt object will be triggered as
1139
- follows (if they are set to Function values):
1140
-
1141
- - beforeSend(request,opt) will be called before any network
1142
- processing starts. If beforeSend() throws then no other
1143
- callbacks are triggered and this function propagates the
1144
- exception. This function is passed normalized connection options
1145
- as its second parameter, and changes this function makes to that
1146
- object _will_ be used for the pending connection attempt.
1147
-
1148
- - onError(request,opt) will be called if a connection to the
1149
- back-end cannot be established. It will be passed the original
1150
- request object (which might be null, depending on the request
1151
- type) and the normalized options object. In the error case, the
1152
- opt object passed to onError() "should" have a property called
1153
- "errorMessage" which contains a description of the problem.
1154
-
1155
- - onError(request,opt) will also be called if connection
1156
- succeeds but the response is not JSON data.
1157
-
1158
- - onResponse(response,request) will be called if the response
1159
- returns JSON data. That data might hold an error response code -
1160
- clients need to check for that. It is passed the response object
1161
- (a plain object) and the original request object.
1162
-
1163
- - afterSend(request,opt) will be called directly after the
1164
- AJAX request is finished, before onError() or onResonse() are
1165
- called. Possible TODO: we explicitly do NOT pass the response to
1166
- this function in order to keep the line between the responsibilities
1167
- of the various callback clear (otherwise this could be used the same
1168
- as onResponse()). In practice it would sometimes be useful have the
1169
- response passed to this function, mainly for logging/debugging
1170
- purposes.
1171
-
1172
- The return value from this function is meaningless because
1173
- AJAX operations tend to take place asynchronously.
1174
-
1175
-*/
1176
-WhAjaj.Connector.prototype.sendRequest = function(request,opt)
1177
-{
1178
- if( !WhAjaj.isFunction(this.sendImpl) )
1179
- {
1180
- throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
1181
- }
1182
- var ex = false;
1183
- var av = Array.prototype.slice.apply( arguments, [0] );
1184
-
1185
- /**
1186
- FIXME: how to handle the error, vis-a-vis- the callbacks, if
1187
- normalizeAjaxParameters() throws? It can throw if
1188
- (de)JSON-izing fails.
1189
- */
1190
- var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
1191
- norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
1192
- if( ! request ) norm.method = 'GET';
1193
- var cb = this.callbacks || {};
1194
- if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
1195
- this.callbacks.beforeSend( request, norm );
1196
- }
1197
- if( WhAjaj.isFunction(norm.beforeSend) ){
1198
- norm.beforeSend( request, norm );
1199
- }
1200
- //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
1201
- try { this.sendImpl( request, norm ); }
1202
- catch(e) { ex = e; }
1203
- if(ex) throw ex;
1204
-};
1205
-
1206
-/**
1207
- sendImpl() holds a concrete back-end connection implementation. It
1208
- can be replaced with a custom implementation if one follows the rules
1209
- described throughout this API. See WhAjaj.Connector.sendImpls for
1210
- the concrete implementations included with this API.
1211
-*/
1212
-//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1213
-//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
1214
-//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1215
-
1216
-if( 'undefined' !== typeof jQuery ){
1217
- WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1218
-}
1219
-else {
1220
- WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1221
-}
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -1,1221 +0,0 @@
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
16 This framework is directly derived from code originally found in
17 http://code.google.com/p/jsonmessage, and later in
18 http://whiki.wanderinghorse.net, where it contained quite a bit
19 of application-specific logic. It was eventually (the 3rd time i
20 needed it) split off into its own library to simplify inclusion
21 into my many mini-projects.
22 */
23
24
25 /**
26 The WhAjaj function is primarily a namespace, and not intended
27 to called or instantiated via the 'new' operator.
28 */
29 function WhAjaj()
30 {
31 }
32
33 /** Returns a millisecond Unix Epoch timestamp. */
34 WhAjaj.msTimestamp = function()
35 {
36 return (new Date()).getTime();
37 };
38
39 /** Returns a Unix Epoch timestamp (in seconds) in integer format.
40
41 Reminder to self: (1.1 %1.2) evaluates to a floating-point value
42 in JS, and thus this implementation is less than optimal.
43 */
44 WhAjaj.unixTimestamp = function()
45 {
46 var ts = (new Date()).getTime();
47 return parseInt( ""+((ts / 1000) % ts) );
48 };
49
50 /**
51 Returns true if v is-a Array instance.
52 */
53 WhAjaj.isArray = function( v )
54 {
55 return (v &&
56 (v instanceof Array) ||
57 (Object.prototype.toString.call(v) === "[object Array]")
58 );
59 /* Reminders to self:
60 typeof [] == "object"
61 toString.call([]) == "[object Array]"
62 ([]).toString() == empty
63 */
64 };
65
66 /**
67 Returns true if v is-a Object instance.
68 */
69 WhAjaj.isObject = function( v )
70 {
71 return v &&
72 (v instanceof Object) &&
73 ('[object Object]' === Object.prototype.toString.apply(v) );
74 };
75
76 /**
77 Returns true if v is-a Function instance.
78 */
79 WhAjaj.isFunction = function(obj)
80 {
81 return obj
82 && (
83 (obj instanceof Function)
84 || ('function' === typeof obj)
85 || ("[object Function]" === Object.prototype.toString.call(obj))
86 )
87 ;
88 };
89
90 /**
91 Parses window.location.search-style string into an object
92 containing key/value pairs of URL arguments (already urldecoded).
93
94 If the str argument is not passed (arguments.length==0) then
95 window.location.search.substring(1) is used by default. If
96 neither str is passed in nor window exists then false is returned.
97
98 On success it returns an Object containing the key/value pairs
99 parsed from the string. Keys which have no value are treated
100 has having the boolean true value.
101
102 FIXME: for keys in the form "name[]", build an array of results,
103 like PHP does.
104
105 */
106 WhAjaj.processUrlArgs = function(str) {
107 if( 0 === arguments.length ) {
108 if( ('undefined' === typeof window) ||
109 !window.location ||
110 !window.location.search ) return false;
111 else str = (''+window.location.search).substring(1);
112 }
113 if( ! str ) return false;
114 str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
115 var args = {};
116 var sp = str.split(/&+/);
117 var rx = /^([^=]+)(=(.+))?/;
118 var i, m;
119 for( i in sp ) {
120 m = rx.exec( sp[i] );
121 if( ! m ) continue;
122 args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
123 }
124 return args;
125 };
126
127 /**
128 A simple wrapper around JSON.stringify(), using my own personal
129 preferred values for the 2nd and 3rd parameters. To globally
130 set its indentation level, assign WhAjaj.stringify.indent to
131 an integer value (0 for no intendation).
132
133 This function is intended only for human-readable output, not
134 generic over-the-wire JSON output (where JSON.stringify(val) will
135 produce smaller results).
136 */
137 WhAjaj.stringify = function(val) {
138 if( ! arguments.callee.indent ) arguments.callee.indent = 4;
139 return JSON.stringify(val,0,arguments.callee.indent);
140 };
141
142 /**
143 Each instance of this class holds state information for making
144 AJAJ requests to a back-end system. While clients may use one
145 "requester" object per connection attempt, for connections to the
146 same back-end, using an instance configured for that back-end
147 can simplify usage. This class is designed so that the actual
148 connection-related details (i.e. _how_ it connects to the
149 back-end) may be re-implemented to use a client's preferred
150 connection mechanism (e.g. jQuery).
151
152 The optional opt parameter may be an object with any (or all) of
153 the properties documented for WhAjaj.Connector.options.ajax.
154 Properties set here (or later via modification of the "options"
155 property of this object) will be used in calls to
156 WhAjaj.Connector.sendRequest(), and these override (normally) any
157 options set in WhAjaj.Connector.options.ajax. Note that
158 WhAjaj.Connector.sendRequest() _also_ takes an options object,
159 and ones passed there will override, for purposes of that one
160 request, any options passed in here or defined in
161 WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
162 and WhAjaj.Connector.prototype.sendRequest() for more details
163 about the precedence of options.
164
165 Sample usage:
166
167 @code
168 // Set up common connection-level options:
169 var cgi = new WhAjaj.Connector({
170 url: '/cgi-bin/my.cgi',
171 timeout:10000,
172 onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
173 onError(req,opt) {
174 alert(opt.errorMessage);
175 }
176 });
177 // Any of those options may optionally be set globally in
178 // WhAjaj.Connector.options.ajax (onError(), beforeSend(), and afterSend()
179 // are often easiest/most useful to set globally).
180
181 // Get list of pages...
182 cgi.sendRequest( null, {
183 onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
184 });
185 @endcode
186
187 For common request types, clients can add functions to this
188 object which act as wrappers for backend-specific functionality. As
189 a simple example:
190
191 @code
192 cgi.login = function(name,pw,ajajOpt) {
193 this.sendRequest(
194 {command:"json/login",
195 name:name,
196 password:pw
197 }, ajajOpt );
198 };
199 @endcode
200
201 TODOs:
202
203 - Caching of page-load requests, with a configurable lifetime.
204
205 - Use-cases like the above login() function are a tiny bit
206 problematic to implement when each request has a different URL
207 path (i know this from the whiki and fossil implementations).
208 This is partly a side-effect of design descisions made back in
209 the very first days of this code's life. i need to go through
210 and see where i can bend those conventions a bit (where it won't
211 break my other apps unduly).
212 */
213 WhAjaj.Connector = function(opt)
214 {
215 if(WhAjaj.isObject(opt)) this.options = opt;
216 //TODO?: this.$cache = {};
217 };
218
219 /**
220 The core options used by WhAjaj.Connector instances for performing
221 network operations. These options can (and some _should_)
222 be changed by a client application. They can also be changed
223 on specific instances of WhAjaj.Connector, but for most applications
224 it is simpler to set them here and not have to bother with configuring
225 each WhAjaj.Connector instance. Apps which use multiple back-ends at one time,
226 however, will need to customize each instance for a given back-end.
227 */
228 WhAjaj.Connector.options = {
229 /**
230 A (meaningless) prefix to apply to WhAjaj.Connector-generated
231 request IDs.
232 */
233 requestIdPrefix:'WhAjaj.Connector-',
234 /**
235 Default options for WhAjaj.Connector.sendRequest() connection
236 parameters. This object holds only connection-related
237 options and callbacks (all optional), and not options
238 related to the required JSON structure of any given request.
239 i.e. the page name used in a get-page request are not set
240 here but are specified as part of the request object.
241
242 These connection options are a "normalized form" of options
243 often found in various AJAX libraries like jQuery,
244 Prototype, dojo, etc. This approach allows us to swap out
245 the real connection-related parts by writing a simple proxy
246 which transforms our "normalized" form to the
247 backend-specific form. For examples, see the various
248 implementations stored in WhAjaj.Connector.sendImpls.
249
250 The following callback options are, in practice, almost
251 always set globally to some app-wide defaults:
252
253 - onError() to report errors using a common mechanism.
254 - beforeSend() to start a visual activity notification
255 - afterSend() to disable the visual activity notification
256
257 However, be aware that if any given WhAjaj.Connector instance is
258 given its own before/afterSend callback then those will
259 override these. Mixing shared/global and per-instance
260 callbacks can potentially lead to confusing results if, e.g.,
261 the beforeSend() and afterSend() functions have side-effects
262 but are not used with their proper before/after partner.
263
264 TODO: rename this to 'ajaj' (the name is historical). The
265 problem with renaming it is is that the word 'ajax' is
266 pretty prevelant in the source tree, so i can't globally
267 swap it out.
268 */
269 ajax: {
270 /**
271 URL of the back-end server/CGI.
272 */
273 url: '/some/path',
274
275 /**
276 Connection method. Some connection-related functions might
277 override any client-defined setting.
278
279 Must be one of 'GET' or 'POST'. For custom connection
280 implementation, it may optionally be some
281 implementation-specified value.
282
283 Normally the API can derive this value automatically - if the
284 request uses JSON data it is POSTed, else it is GETted.
285 */
286 method:'GET',
287
288 /**
289 A hint whether to run the operation asynchronously or
290 not. Not all concrete WhAjaj.Connector.sendImpl()
291 implementations can support this. Interestingly, at
292 least one popular AJAX toolkit does not document
293 supporting _synchronous_ AJAX operations. All common
294 browser-side implementations support async operation, but
295 non-browser implementations might not.
296 */
297 asynchronous:true,
298
299 /**
300 A HTTP authentication login name for the AJAX
301 connection. Not all concrete WhAjaj.Connector.sendImpl()
302 implementations can support this.
303 */
304 loginName:undefined,
305
306 /**
307 An HTTP authentication login password for the AJAJ
308 connection. Not all concrete WhAjaj.Connector.sendImpl()
309 implementations can support this.
310 */
311 loginPassword:undefined,
312
313 /**
314 A connection timeout, in milliseconds, for establishing
315 an AJAJ connection. Not all concrete
316 WhAjaj.Connector.sendImpl() implementations can support this.
317 */
318 timeout:10000,
319
320 /**
321 If an AJAJ request receives JSON data from the back-end,
322 that data is passed as a plain Object as the response
323 parameter (exception: in jsonp mode it is passed a
324 string (why???)). The initiating request object is
325 passed as the second parameter, but clients can normally
326 ignore it (only those which need a way to map specific
327 requests to responses will need it). The 3rd parameter
328 is the same as the 'this' object for the context of the
329 callback, but is provided because the instance-level
330 callbacks (set in (WhAjaj.Connector instance).callbacks,
331 require it in some cases (because their 'this' is
332 different!).
333
334 Note that the response might contain error information
335 which comes from the back-end. The difference between
336 this error info and the info passed to the onError()
337 callback is that this data indicates an
338 application-level error, whereas onError() is used to
339 report connection-level problems or when the backend
340 produces non-JSON data (which, when not in jsonp mode,
341 is unexpected and is as fatal to the request as a
342 connection error).
343 */
344 onResponse: function(response, request, opt){},
345
346 /**
347 If an AJAX request fails to establish a connection or it
348 receives non-JSON data from the back-end, this function
349 is called (e.g. timeout error or host name not
350 resolvable). It is passed the originating request and the
351 "normalized" connection parameters used for that
352 request. The connectOpt object "should" (or "might")
353 have an "errorMessage" property which describes the
354 nature of the problem.
355
356 Clients will almost always want to replace the default
357 implementation with something which integrates into
358 their application.
359 */
360 onError: function(request, connectOpt)
361 {
362 alert('AJAJ request failed:\n'
363 +'Connection information:\n'
364 +JSON.stringify(connectOpt,0,4)
365 );
366 },
367
368 /**
369 Called before each connection attempt is made. Clients
370 can use this to, e.g., enable a visual "network activity
371 notification" for the user. It is passed the original
372 request object and the normalized connection parameters
373 for the request. If this function changes opt, those
374 changes _are_ applied to the subsequent request. If this
375 function throws, neither the onError() nor afterSend()
376 callbacks are triggered and WhAjaj.Connector.sendImpl()
377 propagates the exception back to the caller.
378 */
379 beforeSend: function(request,opt){},
380
381 /**
382 Called after an AJAJ connection attempt completes,
383 regardless of success or failure. Passed the same
384 parameters as beforeSend() (see that function for
385 details).
386
387 Here's an example of setting up a visual notification on
388 ajax operations using jQuery (but it's also easy to do
389 without jQuery as well):
390
391 @code
392 function startAjaxNotif(req,opt) {
393 var me = arguments.callee;
394 var c = ++me.ajaxCount;
395 me.element.text( c + " pending AJAX operation(s)..." );
396 if( 1 == c ) me.element.stop().fadeIn();
397 }
398 startAjaxNotif.ajaxCount = 0.
399 startAjaxNotif.element = jQuery('#whikiAjaxNotification');
400
401 function endAjaxNotif() {
402 var c = --startAjaxNotif.ajaxCount;
403 startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
404 if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
405 }
406 @endcode
407
408 Set the beforeSend/afterSend properties to those
409 functions to enable the notifications by default.
410 */
411 afterSend: function(request,opt){},
412
413 /**
414 If jsonp is a string then the WhAjaj-internal response
415 handling code ASSUMES that the response contains a JSONP-style
416 construct and eval()s it after afterSend() but before onResponse().
417 In this case, onResponse() will get a string value for the response
418 instead of a response object parsed from JSON.
419 */
420 jsonp:undefined,
421 /**
422 Don't use yet. Planned future option.
423 */
424 propagateExceptions:false
425 }
426 };
427
428
429 /**
430 WhAjaj.Connector.prototype.callbacks defines callbacks analog
431 to the onXXX callbacks defined in WhAjaj.Connector.options.ajax,
432 with two notable differences:
433
434 1) these callbacks, if set, are called in addition to any
435 request-specific callback. The intention is to allow a framework to set
436 "framework-level" callbacks which should be called independently of the
437 request-specific callbacks (without interfering with them, e.g.
438 requiring special re-forwarding features).
439
440 2) The 'this' object in these callbacks is the Connector instance
441 associated with the callback, whereas the "other" onXXX form has its
442 "ajax options" object as its this.
443
444 When this API says that an onXXX callback will be called for a request,
445 both the request's onXXX (if set) and this one (if set) will be called.
446 */
447 WhAjaj.Connector.prototype.callbacks = {};
448 /**
449 Instance-specific values for AJAJ-level properties (as opposed to
450 application-level request properties). Options set here "override" those
451 specified in WhAjaj.Connector.options.ajax and are "overridden" by
452 options passed to sendRequest().
453 */
454 WhAjaj.Connector.prototype.options = {};
455
456
457 /**
458 Tries to find the given key in any of the following, returning
459 the first match found: opt, this.options, WhAjaj.Connector.options.ajax.
460
461 Returns undefined if key is not found.
462 */
463 WhAjaj.Connector.prototype.derivedOption = function(key,opt) {
464 var v = opt ? opt[key] : undefined;
465 if( undefined !== v ) return v;
466 else v = this.options[key];
467 if( undefined !== v ) return v;
468 else v = WhAjaj.Connector.options.ajax[key];
469 return v;
470 };
471
472 /**
473 Returns a unique string on each call containing a generic
474 reandom request identifier string. This is not used by the core
475 API but can be used by client code to generate unique IDs for
476 each request (if needed).
477
478 The exact format is unspecified and may change in the future.
479
480 Request IDs can be used by clients to "match up" responses to
481 specific requests if needed. In practice, however, they are
482 seldom, if ever, needed. When passing several concurrent
483 requests through the same response callback, it might be useful
484 for some clients to be able to distinguish, possibly re-routing
485 them through other handlers based on the originating request type.
486
487 If this.options.requestIdPrefix or
488 WhAjaj.Connector.options.requestIdPrefix is set then that text
489 is prefixed to the returned string.
490 */
491 WhAjaj.Connector.prototype.generateRequestId = function()
492 {
493 if( undefined === arguments.callee.sequence )
494 {
495 arguments.callee.sequence = 0;
496 }
497 var pref = this.options.requestIdPrefix || WhAjaj.Connector.options.requestIdPrefix || '';
498 return pref +
499 WhAjaj.msTimestamp() +
500 '/'+(Math.round( Math.random() * 100000000) )+
501 ':'+(++arguments.callee.sequence);
502 };
503
504 /**
505 Copies (SHALLOWLY) all properties in opt to this.options.
506 */
507 WhAjaj.Connector.prototype.addOptions = function(opt) {
508 var k, v;
509 for( k in opt ) {
510 if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
511 this.options[k] = opt[k];
512 }
513 return this.options;
514 };
515
516 /**
517 An internal helper object which holds several functions intended
518 to simplify the creation of concrete communication channel
519 implementations for WhAjaj.Connector.sendImpl(). These operations
520 take care of some of the more error-prone parts of ensuring that
521 onResponse(), onError(), etc. callbacks are called consistently
522 using the same rules.
523 */
524 WhAjaj.Connector.sendHelper = {
525 /**
526 opt is assumed to be a normalized set of
527 WhAjaj.Connector.sendRequest() options. This function
528 creates a url by concatenating opt.url and some form of
529 opt.urlParam.
530
531 If opt.urlParam is an object or string then it is appended
532 to the url. An object is assumed to be a one-dimensional set
533 of simple (urlencodable) key/value pairs, and not larger
534 data structures. A string value is assumed to be a
535 well-formed, urlencoded set of key/value pairs separated by
536 '&' characters.
537
538 The new/normalized URL is returned (opt is not modified). If
539 opt.urlParam is not set then opt.url is returned (or an
540 empty string if opt.url is itself a false value).
541
542 TODO: if opt is-a Object and any key points to an array,
543 build up a list of keys in the form "keyname[]". We could
544 arguably encode sub-objects like "keyname[subkey]=...", but
545 i don't know if that's conventions-compatible with other
546 frameworks.
547 */
548 normalizeURL: function(opt) {
549 var u = opt.url || '';
550 if( opt.urlParam ) {
551 var addQ = (u.indexOf('?') >= 0) ? false : true;
552 var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
553 var tail = '';
554 if( WhAjaj.isObject(opt.urlParam) ) {
555 var li = [], k;
556 for( k in opt.urlParam) {
557 li.push( k+'='+encodeURIComponent( opt.urlParam[k] ) );
558 }
559 tail = li.join('&');
560 }
561 else if( 'string' === typeof opt.urlParam ) {
562 tail = opt.urlParam;
563 }
564 u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
565 }
566 return u;
567 },
568 /**
569 Should be called by WhAjaj.Connector.sendImpl()
570 implementations after a response has come back. This
571 function takes care of most of ensuring that framework-level
572 conventions involving WhAjaj.Connector.options.ajax
573 properties are followed.
574
575 The request argument must be the original request passed to
576 the sendImpl() function. It may legally be null for GET requests.
577
578 The opt object should be the normalized AJAX options used
579 for the connection.
580
581 The resp argument may be either a plain Object or a string
582 (in which case it is assumed to be JSON).
583
584 The 'this' object for this call MUST be a WhAjaj.Connector
585 instance in order for callback processing to work properly.
586
587 This function takes care of the following:
588
589 - Calling opt.afterSend()
590
591 - If resp is a string, de-JSON-izing it to an object.
592
593 - Calling opt.onResponse()
594
595 - Calling opt.onError() in several common (potential) error
596 cases.
597
598 - If resp is-a String and opt.jsonp then resp is assumed to be
599 a JSONP-form construct and is eval()d BEFORE opt.onResponse()
600 is called. It is arguable to eval() it first, but the logic
601 integrates better with the non-jsonp handler.
602
603 The sendImpl() should return immediately after calling this.
604
605 The sendImpl() must call only one of onSendSuccess() or
606 onSendError(). It must call one of them or it must implement
607 its own response/error handling, which is not recommended
608 because getting the documented semantics of the
609 onError/onResponse/afterSend handling correct can be tedious.
610 */
611 onSendSuccess:function(request,resp,opt) {
612 var cb = this.callbacks || {};
613 if( WhAjaj.isFunction(cb.afterSend) ) {
614 try {cb.afterSend( request, opt );}
615 catch(e){}
616 }
617 if( WhAjaj.isFunction(opt.afterSend) ) {
618 try {opt.afterSend( request, opt );}
619 catch(e){}
620 }
621 function doErr(){
622 if( WhAjaj.isFunction(cb.onError) ) {
623 try {cb.onError( request, opt );}
624 catch(e){}
625 }
626 if( WhAjaj.isFunction(opt.onError) ) {
627 try {opt.onError( request, opt );}
628 catch(e){}
629 }
630 }
631 if( ! resp ) {
632 opt.errorMessage = "Sending of request succeeded but returned no data!";
633 doErr();
634 return false;
635 }
636
637 if( 'string' === typeof resp ) {
638 try {
639 resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
640 } catch(e) {
641 opt.errorMessage = e.toString();
642 doErr();
643 return;
644 }
645 }
646 try {
647 if( WhAjaj.isFunction( cb.onResponse ) ) {
648 cb.onResponse( resp, request, opt );
649 }
650 if( WhAjaj.isFunction( opt.onResponse ) ) {
651 opt.onResponse( resp, request, opt );
652 }
653 return true;
654 }
655 catch(e) {
656 opt.errorMessage = "Exception while handling inbound JSON response:\n"
657 + e
658 +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
659 ;
660 ;
661 doErr();
662 return false;
663 }
664 },
665 /**
666 Should be called by sendImpl() implementations after a response
667 has failed to connect (e.g. could not resolve host or timeout
668 reached). This function takes care of most of ensuring that
669 framework-level conventions involving WhAjaj.Connector.options.ajax
670 properties are followed.
671
672 The request argument must be the original request passed to
673 the sendImpl() function. It may legally be null for GET
674 requests.
675
676 The 'this' object for this call MUST be a WhAjaj.Connector
677 instance in order for callback processing to work properly.
678
679 The opt object should be the normalized AJAX options used
680 for the connection. By convention, the caller of this
681 function "should" set opt.errorMessage to contain a
682 human-readable description of the error.
683
684 The sendImpl() should return immediately after calling this. The
685 return value from this function is unspecified.
686 */
687 onSendError: function(request,opt) {
688 var cb = this.callbacks || {};
689 if( WhAjaj.isFunction(cb.afterSend) ) {
690 try {cb.afterSend( request, opt );}
691 catch(e){}
692 }
693 if( WhAjaj.isFunction(opt.afterSend) ) {
694 try {opt.afterSend( request, opt );}
695 catch(e){}
696 }
697 if( WhAjaj.isFunction( cb.onError ) ) {
698 try {cb.onError( request, opt );}
699 catch(e) {/*ignore*/}
700 }
701 if( WhAjaj.isFunction( opt.onError ) ) {
702 try {opt.onError( request, opt );}
703 catch(e) {/*ignore*/}
704 }
705 }
706 };
707
708 /**
709 WhAjaj.Connector.sendImpls holds several concrete
710 implementations of WhAjaj.Connector.prototype.sendImpl(). To use
711 a specific implementation by default assign
712 WhAjaj.Connector.prototype.sendImpl to one of these functions.
713
714 The functions defined here require that the 'this' object be-a
715 WhAjaj.Connector instance.
716
717 Historical notes:
718
719 a) We once had an implementation based on Prototype, but that
720 library just pisses me off (they change base-most types'
721 prototypes, introducing side-effects in client code which
722 doesn't even use Prototype). The Prototype version at the time
723 had a serious toJSON() bug which caused empty arrays to
724 serialize as the string "[]", which broke a bunch of my code.
725 (That has been fixed in the mean time, but i don't use
726 Prototype.)
727
728 b) We once had an implementation for the dojo library,
729
730 If/when the time comes to add Prototype/dojo support, we simply
731 need to port:
732
733 http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js
734
735 (search that file for "dojo" and "Prototype") to this tree. That
736 code is this code's generic grandfather and they are still very
737 similar, so a port is trivial.
738
739 */
740 WhAjaj.Connector.sendImpls = {
741 /**
742 This is a concrete implementation of
743 WhAjaj.Connector.prototype.sendImpl() which uses the
744 environment's native XMLHttpRequest class to send whiki
745 requests and fetch the responses.
746
747 The only argument must be a connection properties object, as
748 constructed by WhAjaj.Connector.normalizeAjaxParameters().
749
750 If window.firebug is set then window.firebug.watchXHR() is
751 called to enable monitoring of the XMLHttpRequest object.
752
753 This implementation honors the loginName and loginPassword
754 connection parameters.
755
756 Returns the XMLHttpRequest object.
757
758 This implementation requires that the 'this' object be-a
759 WhAjaj.Connector.
760
761 This implementation uses setTimeout() to implement the
762 timeout support, and thus the JS engine must provide that
763 functionality.
764 */
765 XMLHttpRequest: function(request, args)
766 {
767 var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
768 var xhr = new XMLHttpRequest();
769 var startTime = (new Date()).getTime();
770 var timeout = args.timeout || 10000/*arbitrary!*/;
771 var hitTimeout = false;
772 var done = false;
773 var tmid /* setTimeout() ID */;
774 var whself = this;
775 function handleTimeout()
776 {
777 hitTimeout = true;
778 if( ! done )
779 {
780 var now = (new Date()).getTime();
781 try { xhr.abort(); } catch(e) {/*ignore*/}
782 // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
783 args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
784 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
785 }
786 return;
787 }
788 function onStateChange()
789 { // reminder to self: apparently 'this' is-not-a XHR :/
790 if( hitTimeout )
791 { /* we're too late - the error was already triggered. */
792 return;
793 }
794
795 if( 4 == xhr.readyState )
796 {
797 done = true;
798 if( tmid )
799 {
800 clearTimeout( tmid );
801 tmid = null;
802 }
803 if( (xhr.status >= 200) && (xhr.status < 300) )
804 {
805 WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
806 return;
807 }
808 else
809 {
810 if( undefined === args.errorMessage )
811 {
812 args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
813 +"["+args.url+"]: "
814 +"Status text=["+xhr.statusText+"]"
815 ;
816 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
817 }
818 else { /*maybe it was was set by the timeout handler. */ }
819 return;
820 }
821 }
822 };
823
824 xhr.onreadystatechange = onStateChange;
825 if( ('undefined'!==(typeof window)) && ('firebug' in window) && ('watchXHR' in window.firebug) )
826 { /* plug in to firebug lite's XHR monitor... */
827 window.firebug.watchXHR( xhr );
828 }
829 try
830 {
831 //alert( JSON.stringify( args ));
832 function xhrOpen()
833 {
834 if( ('loginName' in args) && args.loginName )
835 {
836 xhr.open( args.method, args.url, args.asynchronous, args.loginName, args.loginPassword );
837 }
838 else
839 {
840 xhr.open( args.method, args.url, args.asynchronous );
841 }
842 }
843 if( json && ('POST' === args.method.toUpperCase()) )
844 {
845 xhrOpen();
846 xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
847 // Google Chrome warns that it refuses to set these
848 // "unsafe" headers (his words, not mine):
849 // xhr.setRequestHeader("Content-length", json.length);
850 // xhr.setRequestHeader("Connection", "close");
851 xhr.send( json );
852 }
853 else /* assume GET */
854 {
855 xhrOpen();
856 xhr.send(null);
857 }
858 tmid = setTimeout( handleTimeout, timeout );
859 return xhr;
860 }
861 catch(e)
862 {
863 args.errorMessage = e.toString();
864 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
865 return undefined;
866 }
867 }/*XMLHttpRequest()*/,
868 /**
869 This is a concrete implementation of
870 WhAjaj.Connector.prototype.sendImpl() which uses the jQuery
871 AJAX API to send requests and fetch the responses.
872
873 The first argument may be either null/false, an Object
874 containing toJSON-able data to post to the back-end, or such an
875 object in JSON string form.
876
877 The second argument must be a connection properties object, as
878 constructed by WhAjaj.Connector.normalizeAjaxParameters().
879
880 If window.firebug is set then window.firebug.watchXHR() is
881 called to enable monitoring of the XMLHttpRequest object.
882
883 This implementation honors the loginName and loginPassword
884 connection parameters.
885
886 Returns the XMLHttpRequest object.
887
888 This implementation requires that the 'this' object be-a
889 WhAjaj.Connector.
890 */
891 jQuery:function(request,args)
892 {
893 var data = request || undefined;
894 var whself = this;
895 if( data ) {
896 if('string'!==typeof data) {
897 try {
898 data = JSON.stringify(data);
899 }
900 catch(e) {
901 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
902 return;
903 }
904 }
905 }
906 var ajopt = {
907 url: args.url,
908 data: data,
909 type: args.method,
910 async: args.asynchronous,
911 password: (undefined !== args.loginPassword) ? args.loginPassword : undefined,
912 username: (undefined !== args.loginName) ? args.loginName : undefined,
913 contentType: 'application/json; charset=utf-8',
914 error: function(xhr, textStatus, errorThrown)
915 {
916 //this === the options for this ajax request
917 args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
918 +"Status text=["+textStatus+"]"
919 +(errorThrown ? ("Error=["+errorThrown+"]") : "")
920 ;
921 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
922 },
923 success: function(data)
924 {
925 WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
926 },
927 /* Set dataType=text instead of json to keep jQuery from doing our carefully
928 written response handling for us.
929 */
930 dataType: 'text'
931 };
932 if( undefined !== args.timeout )
933 {
934 ajopt.timeout = args.timeout;
935 }
936 try
937 {
938 return jQuery.ajax(ajopt);
939 }
940 catch(e)
941 {
942 args.errorMessage = e.toString();
943 WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
944 return undefined;
945 }
946 }/*jQuery()*/,
947 /**
948 This is a concrete implementation of
949 WhAjaj.Connector.prototype.sendImpl() which uses the rhino
950 Java API to send requests and fetch the responses.
951
952 Limitations vis-a-vis the interface:
953
954 - timeouts are not supported.
955
956 - asynchronous mode is not supported because implementing it
957 requires the ability to kill a running thread (which is deprecated
958 in the Java API).
959
960 TODOs:
961
962 - add socket timeouts.
963
964 - support HTTP proxy.
965
966 The Java APIs support this, it just hasn't been added here yet.
967 */
968 rhino:function(request,args)
969 {
970 var self = this;
971 var data = request || undefined;
972 if( data ) {
973 if('string'!==typeof data) {
974 try {
975 data = JSON.stringify(data);
976 }
977 catch(e) {
978 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
979 return;
980 }
981 }
982 }
983 var url;
984 var con;
985 var IO = new JavaImporter(java.io);
986 var wr;
987 var rd, ln, json = [];
988 function setIncomingCookies(list){
989 if(!list || !list.length) return;
990 if( !self.cookies ) self.cookies = {};
991 var k, v, i;
992 for( i = 0; i < list.length; ++i ){
993 v = list[i].split('=',2);
994 k = decodeURIComponent(v[0])
995 v = v[0] ? decodeURIComponent(v[0].split(';',2)[0]) : null;
996 //print("RECEIVED COOKIE: "+k+"="+v);
997 if(!v) {
998 delete self.cookies[k];
999 continue;
1000 }else{
1001 self.cookies[k] = v;
1002 }
1003 }
1004 };
1005 function setOutboundCookies(conn){
1006 if(!self.cookies) return;
1007 var k, v;
1008 for( k in self.cookies ){
1009 if(!self.cookies.hasOwnProperty(k)) continue /*kludge for broken JS libs*/;
1010 v = self.cookies[k];
1011 conn.addRequestProperty("Cookie", encodeURIComponent(k)+'='+encodeURIComponent(v));
1012 //print("SENDING COOKIE: "+k+"="+v);
1013 }
1014 };
1015 try{
1016 url = new java.net.URL( args.url )
1017 con = url.openConnection(/*FIXME: add proxy support!*/);
1018 con.setRequestProperty("Accept-Charset","utf-8");
1019 setOutboundCookies(con);
1020 if(data){
1021 con.setRequestProperty("Content-Type","application/json; charset=utf-8");
1022 con.setDoOutput( true );
1023 wr = new IO.OutputStreamWriter(con.getOutputStream())
1024 wr.write(data);
1025 wr.flush();
1026 wr.close();
1027 wr = null;
1028 //print("POSTED: "+data);
1029 }
1030 rd = new IO.BufferedReader(new IO.InputStreamReader(con.getInputStream()));
1031 //var skippedHeaders = false;
1032 while ((line = rd.readLine()) !== null) {
1033 //print("LINE: "+line);
1034 //if(!line.length && !skippedHeaders){
1035 // skippedHeaders = true;
1036 // json = [];
1037 // continue;
1038 //}
1039 json.push(line);
1040 }
1041 setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
1042 }catch(e){
1043 args.errorMessage = e.toString();
1044 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
1045 return undefined;
1046 }
1047 try { if(wr) wr.close(); } catch(e) { /*ignore*/}
1048 try { if(rd) rd.close(); } catch(e) { /*ignore*/}
1049 json = json.join('');
1050 //print("READ IN JSON: "+json);
1051 WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
1052 }/*rhino*/
1053 };
1054
1055 /**
1056 An internal function which takes an object containing properties
1057 for a WhAjaj.Connector network request. This function creates a new
1058 object containing a superset of the properties from:
1059
1060 a) opt
1061 b) this.options
1062 c) WhAjaj.Connector.options.ajax
1063
1064 in that order, using the first one it finds.
1065
1066 All non-function properties are _deeply_ copied via JSON cloning
1067 in order to prevent accidental "cross-request pollenation" (been
1068 there, done that). Functions cannot be cloned and are simply
1069 copied by reference.
1070
1071 This function throws if JSON-copying one of the options fails
1072 (e.g. due to cyclic data structures).
1073
1074 Reminder to self: this function does not "normalize" opt.urlParam
1075 by encoding it into opt.url, mainly for historical reasons, but
1076 also because that behaviour was specifically undesirable in this
1077 code's genetic father.
1078 */
1079 WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
1080 {
1081 var rc = {};
1082 function merge(k,v)
1083 {
1084 if( rc.hasOwnProperty(k) ) return;
1085 else if( WhAjaj.isFunction(v) ) {}
1086 else if( WhAjaj.isObject(v) ) v = JSON.parse( JSON.stringify(v) );
1087 rc[k]=v;
1088 }
1089 function cp(obj) {
1090 if( ! WhAjaj.isObject(obj) ) return;
1091 var k;
1092 for( k in obj ) {
1093 if( ! obj.hasOwnProperty(k) ) continue /* i will always hate the Prototype designers for this. */;
1094 merge(k, obj[k]);
1095 }
1096 }
1097 cp( opt );
1098 cp( this.options );
1099 cp( WhAjaj.Connector.options.ajax );
1100 // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
1101 return rc;
1102 };
1103
1104 /**
1105 This is the generic interface for making calls to a back-end
1106 JSON-producing request handler. It is a simple wrapper around
1107 WhAjaj.Connector.prototype.sendImpl(), which just normalizes the
1108 connection options for sendImpl() and makes sure that
1109 opt.beforeSend() is (possibly) called.
1110
1111 The request parameter must either be false/null/empty or a
1112 fully-populated JSON-able request object (which will be sent as
1113 unencoded application/json text), depending on the type of
1114 request being made. It is never semantically legal (in this API)
1115 for request to be a string/number/true/array value. As a rule,
1116 only POST requests use the request data. GET requests should
1117 encode their data in opt.url or opt.urlParam (see below).
1118
1119 opt must contain the network-related parameters for the request.
1120 Paramters _not_ set in opt are pulled from this.options or
1121 WhAjaj.Connector.options.ajax (in that order, using the first
1122 value it finds). Thus the set of connection-level options used
1123 for the request are a superset of those various sources.
1124
1125 The "normalized" (or "superimposed") opt object's URL may be
1126 modified before the request is sent, as follows:
1127
1128 if opt.urlParam is a string then it is assumed to be properly
1129 URL-encoded parameters and is appended to the opt.url. If it is
1130 an Object then it is assumed to be a one-dimensional set of
1131 key/value pairs with simple values (numbers, strings, booleans,
1132 null, and NOT objects/arrays). The keys/values are URL-encoded
1133 and appended to the URL.
1134
1135 The beforeSend() callback (see below) can modify the options
1136 object before the request attempt is made.
1137
1138 The callbacks in the normalized opt object will be triggered as
1139 follows (if they are set to Function values):
1140
1141 - beforeSend(request,opt) will be called before any network
1142 processing starts. If beforeSend() throws then no other
1143 callbacks are triggered and this function propagates the
1144 exception. This function is passed normalized connection options
1145 as its second parameter, and changes this function makes to that
1146 object _will_ be used for the pending connection attempt.
1147
1148 - onError(request,opt) will be called if a connection to the
1149 back-end cannot be established. It will be passed the original
1150 request object (which might be null, depending on the request
1151 type) and the normalized options object. In the error case, the
1152 opt object passed to onError() "should" have a property called
1153 "errorMessage" which contains a description of the problem.
1154
1155 - onError(request,opt) will also be called if connection
1156 succeeds but the response is not JSON data.
1157
1158 - onResponse(response,request) will be called if the response
1159 returns JSON data. That data might hold an error response code -
1160 clients need to check for that. It is passed the response object
1161 (a plain object) and the original request object.
1162
1163 - afterSend(request,opt) will be called directly after the
1164 AJAX request is finished, before onError() or onResonse() are
1165 called. Possible TODO: we explicitly do NOT pass the response to
1166 this function in order to keep the line between the responsibilities
1167 of the various callback clear (otherwise this could be used the same
1168 as onResponse()). In practice it would sometimes be useful have the
1169 response passed to this function, mainly for logging/debugging
1170 purposes.
1171
1172 The return value from this function is meaningless because
1173 AJAX operations tend to take place asynchronously.
1174
1175 */
1176 WhAjaj.Connector.prototype.sendRequest = function(request,opt)
1177 {
1178 if( !WhAjaj.isFunction(this.sendImpl) )
1179 {
1180 throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
1181 }
1182 var ex = false;
1183 var av = Array.prototype.slice.apply( arguments, [0] );
1184
1185 /**
1186 FIXME: how to handle the error, vis-a-vis- the callbacks, if
1187 normalizeAjaxParameters() throws? It can throw if
1188 (de)JSON-izing fails.
1189 */
1190 var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
1191 norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
1192 if( ! request ) norm.method = 'GET';
1193 var cb = this.callbacks || {};
1194 if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
1195 this.callbacks.beforeSend( request, norm );
1196 }
1197 if( WhAjaj.isFunction(norm.beforeSend) ){
1198 norm.beforeSend( request, norm );
1199 }
1200 //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
1201 try { this.sendImpl( request, norm ); }
1202 catch(e) { ex = e; }
1203 if(ex) throw ex;
1204 };
1205
1206 /**
1207 sendImpl() holds a concrete back-end connection implementation. It
1208 can be replaced with a custom implementation if one follows the rules
1209 described throughout this API. See WhAjaj.Connector.sendImpls for
1210 the concrete implementations included with this API.
1211 */
1212 //WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1213 //WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
1214 //WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1215
1216 if( 'undefined' !== typeof jQuery ){
1217 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
1218 }
1219 else {
1220 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
1221 }
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -1,1221 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D ajax/wiki-editor.html
-379
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -1,381 +0,0 @@
1
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
-
4
-<head>
5
- <title>Fossil/JSON Wiki Editor Prototype</title>
6
- <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
8
- <script type="text/javascript" src="js/whajaj.js"></script>
9
- <script type="text/javascript" src="js/fossil-ajaj.js"></script>
10
-
11
-<style type='text/css'>
12
-th {
13
- text-align: left;
14
- background-color: #ececec;
15
-}
16
-
17
-.dangerWillRobinson {
18
- background-color: yellow;
19
-}
20
-
21
-.wikiPageLink {
22
- text-decoration: underline;
23
-}
24
-</style>
25
-
26
-<script type='text/javascript'>
27
-WhAjaj.Connector.options.ajax.url =
28
-/*
29
- Change this to your CGI/server root path:
30
-*/
31
- //'http://fjson/cgi-bin/fossil.cgi'
32
- //'/repos/fossil-sgb/json.cgi'
33
- '/cgi-bin/fossil-json.cgi'
34
- ;
35
-var TheApp = {
36
- response:null,
37
- sessionID:null,
38
- jqe:{}/*jqe==jQuery Elements*/,
39
- ajaxCount:0,
40
- cgi: new FossilAjaj(),
41
- pages:{}
42
-};
43
-
44
-
45
-TheApp.startAjaxNotif = function()
46
-{
47
- ++this.ajaxCount;
48
- TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
49
- this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
50
- if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
51
-};
52
-
53
-TheApp.endAjaxNotif = function()
54
-{
55
- --this.ajaxCount;
56
- this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
57
- if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
58
-};
59
-
60
-TheApp.responseContainsError = function(resp) {
61
- if( !resp || resp.resultCode ) {
62
- //alert("Error response:\n"+JSON.stringify(resp,0,4));
63
- TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
64
- //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
65
- //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
66
- TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
67
- return true;
68
- }
69
- return false;
70
-};
71
-
72
-
73
-TheApp.sendRequest = function() {
74
- var path = this.jqe.textPath.val();
75
- var self = this;
76
- var data = this.jqe.pageListArea.val();
77
- var doPost = (data && data.length);
78
- var req;
79
- if( doPost ) try {
80
- req = JSON.parse(data);
81
- }
82
- catch(e) {
83
- TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
84
- return;
85
- }
86
- if( req ) {
87
- req.requestId = this.cgi.generateRequestId();
88
- }
89
- var self = this;
90
- var opt = {
91
- url: WhAjaj.Connector.options.ajax.url + path,
92
- method: doPost ? 'POST' : 'GET'
93
- };
94
- this.cgi.sendRequest( req, opt );
95
-};
96
-jQuery.fn.animateHighlight = function(highlightColor, duration) {
97
- var highlightBg = highlightColor || "#FFFF9C";
98
- var animateMs = duration || 1500;
99
- var originalBg = this.css("backgroundColor");
100
- this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
101
-};
102
-jQuery.fn.flash = function( color, duration )
103
-{
104
- var current = this.css( 'color' );
105
- this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
106
- this.animate( { color: current }, duration / 2 );
107
-};
108
-
109
-jQuery(document).ready(function(){
110
- var ids = [
111
- 'btnSend',
112
- 'ajaxNotification',
113
- 'currentAuthToken',
114
- 'responseContainer',
115
- 'spanPageName',
116
- 'pageListArea',
117
- 'taPageContent',
118
- 'taResponse',
119
- 'textPath', // list of HTML element IDs we use often.
120
- 'timer'
121
- ];
122
- var i, k;
123
- for( i = 0; i < ids.length; ++i ) {
124
- k = ids[i];
125
- TheApp.jqe[k] = jQuery('#'+k);
126
- }
127
- TheApp.jqe.textPath.
128
- keyup(function(event){
129
- if(event.keyCode == 13){
130
- TheApp.sendRequest();
131
- }
132
- });
133
- TheApp.timer = {
134
- _tstart:0,_tend:0,duration:0,
135
- start:function(){
136
- this._tstart = (new Date()).getTime();
137
- },
138
- end:function(){
139
- this._tend = (new Date()).getTime();
140
- return this.duration = this._tend - this._tstart;
141
- }
142
- };
143
- var ajcb = TheApp.cgi.ajaj.callbacks;
144
- ajcb.beforeSend = TheApp.beforeSend = function(req,opt) {
145
- TheApp.timer.start();
146
- var val =
147
- req ?
148
- (('string'===typeof req) ? req : WhAjaj.stringify(req))
149
- : '';
150
- TheApp.jqe.taResponse.val('');
151
- TheApp.startAjaxNotif();
152
- };
153
- ajcb.afterSend = TheApp.afterSend = function(req,opt) {
154
- TheApp.timer.end();
155
- TheApp.endAjaxNotif();
156
- TheApp.jqe.timer.text( "(Round-trip time: "+TheApp.timer.duration+'ms)' );
157
- };
158
- ajcb.onResponse = TheApp.onResponse = function(resp,req) {
159
- var val;
160
- try {
161
- val = WhAjaj.stringify(resp);
162
- }
163
- catch(e) {
164
- val = WhAjaj.stringify(e)
165
- }
166
- if(resp.resultCode){
167
- alert("Response contains error info:\n"+val);
168
- }
169
- TheApp.jqe.taResponse.val( val );
170
- };
171
- ajcb.onError = function(req,opt) {
172
- TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
173
- };
174
-
175
- TheApp.jqe.taPageContent.blur(function(){
176
- var p = TheApp.currentPage;
177
- if(! p ) return;
178
- p.content = TheApp.jqe.taPageContent.val();
179
- });
180
-
181
- TheApp.cgi.onLogin = function(){
182
- TheApp.jqe.taResponse.val( "Logged in: "+WhAjaj.stringify(this.auth));
183
- TheApp.jqe.currentAuthToken.text("Logged in: "+WhAjaj.stringify(this.auth));
184
- };
185
- TheApp.cgi.onLogout = function(){
186
- TheApp.jqe.taResponse.val( "Logged out!" );
187
- TheApp.jqe.currentAuthToken.text("");
188
- };
189
-
190
- TheApp.showPage = function(name){
191
- function doShow(page){
192
- TheApp.currentPage = page;
193
- TheApp.jqe.spanPageName.text('('+page.name+')');
194
- TheApp.jqe.taPageContent.val(page.content);
195
- }
196
- var p = ('object' === typeof name) ? name : TheApp.pages[name];
197
- if(('object' === typeof p) && p.content) {
198
- doShow(p);
199
- return;
200
- }
201
- TheApp.cgi.sendCommand('/json/wiki/get',{
202
- name:name
203
- },{
204
- onResponse:function(resp,req){
205
- TheApp.onResponse(resp,req);
206
- if(resp.resultCode) return;
207
- var p = resp.payload;
208
- doShow( TheApp.pages[p.name] = p );
209
- }
210
- });
211
- };
212
- TheApp.refreshPageListView = function(){
213
- var list = (function(){
214
- var k, v, li = [];
215
- for( k in TheApp.pages ){
216
- if(!TheApp.pages.hasOwnProperty(k)) continue;
217
- li.push(k);
218
- }
219
- return li;
220
- })();
221
- var i, p, a, tgt = TheApp.jqe.pageListArea;
222
- tgt.text('');
223
- function makeLink(name){
224
- var link = jQuery('<span></span>');
225
- link.text(name);
226
- link.addClass('wikiPageLink');
227
- link.click(function(e){
228
- TheApp.showPage(name);
229
- e.preventDefault();
230
- return false;
231
- });
232
- return link;
233
- }
234
- list.sort();
235
- for( i = 0; i < list.length; ++i ){
236
- tgt.append(makeLink(list[i]));
237
- tgt.append('<br/>');
238
- }
239
- };
240
-
241
- TheApp.loadPageList = function(){
242
- TheApp.cgi.sendCommand('/json/wiki/list',null,{
243
- onResponse:function(resp,req){
244
- TheApp.onResponse(resp,req);
245
- if(resp.resultCode) return;
246
- var i, v, p, ar = resp.payload;
247
- for( i = 0; i < ar.length; ++i ){
248
- v = ar[i];
249
- p = TheApp.pages[v];
250
- if( !p ) TheApp.pages[v] = {name:v};
251
- }
252
- TheApp.refreshPageListView();
253
- }
254
- });
255
- return false /*for click handlers*/;
256
- }
257
-
258
- TheApp.savePage = function(p){
259
- p = p || TheApp.currentPage;
260
- if( 'object' !== typeof p ){
261
- p = TheApp.pages[p];
262
- }
263
- if('object' !== typeof p){
264
- alert("savePage() argument is not a page object or known page name.");
265
- }
266
- TheApp.pages[p.name] = p;
267
- p.content = TheApp.jqe.taPageContent.val();
268
- var req = {
269
- name:p.name,
270
- content:p.content
271
- };
272
- if(! confirm("Really save wiki page ["+p.name+"]?") ) return;
273
- TheApp.cgi.sendCommand('/json/wiki/'+(p.isNew?'create':'save'),req,{
274
- onResponse:function(resp,req){
275
- TheApp.onResponse(resp,req);
276
- if(resp.resultCode) return;
277
- delete p.isNew;
278
- p.timestamp = resp.payload.timestamp;
279
- }
280
- });
281
-
282
- };
283
-
284
- TheApp.createNewPage = function(){
285
- var name = prompt("New page name?");
286
- if(!name) return;
287
- var p = {
288
- name:name,
289
- content:"New, empty page.",
290
- isNew:true
291
- };
292
- TheApp.pages[name] = p;
293
- TheApp.refreshPageListView();
294
- TheApp.showPage(p);
295
-/*
296
- if(! confirm("Really create new wiki page ["+name+"]?") ) return;
297
- TheApp.cgi.sendCommand('/json/wiki/create',req,{
298
- onResponse:function(resp,req){
299
- TheApp.onResponse(resp,req);
300
- if(resp.resultCode) return;
301
- TheApp.pages[p.name] = p;
302
- TheApp.refreshPageListView();
303
- }
304
- });
305
-*/
306
- };
307
-
308
- TheApp.cgi.whoami();
309
-
310
-});
311
-
312
-</script>
313
-
314
-</head>
315
-
316
-<body>
317
-<span id='ajaxNotification'></span>
318
-<h1>PROTOTYPE JSON-based Fossil Wiki Editor</h1>
319
-
320
-See also: <a href='index.html'>main test page</a>.
321
-
322
-<br>
323
-<b>Login:</b>
324
-<br/>
325
-<input type='button' value='Anon. Login' onclick='TheApp.cgi.login()' />
326
-or:
327
-name:<input type='text' id='textUser' value='json-demo' size='12'/>
328
-pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
329
-<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
330
-<input type='button' value='logout' onclick='TheApp.cgi.logout()' />
331
-
332
-<br/>
333
-<span id='currentAuthToken' style='font-family:monospaced'></span>
334
-
335
-<hr/>
336
-<strong>Quick-posts:</strong><br/>
337
-<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
338
-<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
339
-<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
340
-<input type='button' value='wiki/list' onclick='TheApp.loadPageList()' />
341
-<!--
342
-<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' />
--->
343
-
344
-<!--
345
-<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
346
-<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
347
-<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
348
-<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
--->
349
-<hr/>
350
-
351
-<table>
352
- <tr>
353
- <th>Page List</th>
354
- <th>Content <span id='spanPageName'></span></th>
355
- </tr>
356
- <tr>
357
- <td width='25%' valign='top'>
358
- <input type='button' value='Create new...' onclick='TheApp.createNewPage()' /><br/>
359
- <div id='pageListArea'></div>
360
- </td>
361
- <td width='75%' valign='top'>
362
- <input type='button' value='Save' onclick='TheApp.savePage()' /><br/>
363
- <textarea id='taPageContent' rows='20' cols='60'></textarea>
364
- </td>
365
- </tr>
366
- <tr>
367
- <th colspan='2'>Response <span id='timer'></span></th>
368
- </tr>
369
- <tr>
370
- <td colspan='2' id='responseContainer'>
371
- <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
372
- </td>
373
- </tr>
374
-</table>
375
-<div></div>
376
-<div></div>
377
-<div></div>
378
-
379
-</body></html>
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -1,381 +0,0 @@
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
4 <head>
5 <title>Fossil/JSON Wiki Editor Prototype</title>
6 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
7 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
8 <script type="text/javascript" src="js/whajaj.js"></script>
9 <script type="text/javascript" src="js/fossil-ajaj.js"></script>
10
11 <style type='text/css'>
12 th {
13 text-align: left;
14 background-color: #ececec;
15 }
16
17 .dangerWillRobinson {
18 background-color: yellow;
19 }
20
21 .wikiPageLink {
22 text-decoration: underline;
23 }
24 </style>
25
26 <script type='text/javascript'>
27 WhAjaj.Connector.options.ajax.url =
28 /*
29 Change this to your CGI/server root path:
30 */
31 //'http://fjson/cgi-bin/fossil.cgi'
32 //'/repos/fossil-sgb/json.cgi'
33 '/cgi-bin/fossil-json.cgi'
34 ;
35 var TheApp = {
36 response:null,
37 sessionID:null,
38 jqe:{}/*jqe==jQuery Elements*/,
39 ajaxCount:0,
40 cgi: new FossilAjaj(),
41 pages:{}
42 };
43
44
45 TheApp.startAjaxNotif = function()
46 {
47 ++this.ajaxCount;
48 TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
49 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
50 if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
51 };
52
53 TheApp.endAjaxNotif = function()
54 {
55 --this.ajaxCount;
56 this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
57 if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
58 };
59
60 TheApp.responseContainsError = function(resp) {
61 if( !resp || resp.resultCode ) {
62 //alert("Error response:\n"+JSON.stringify(resp,0,4));
63 TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
64 //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
65 //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
66 TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
67 return true;
68 }
69 return false;
70 };
71
72
73 TheApp.sendRequest = function() {
74 var path = this.jqe.textPath.val();
75 var self = this;
76 var data = this.jqe.pageListArea.val();
77 var doPost = (data && data.length);
78 var req;
79 if( doPost ) try {
80 req = JSON.parse(data);
81 }
82 catch(e) {
83 TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
84 return;
85 }
86 if( req ) {
87 req.requestId = this.cgi.generateRequestId();
88 }
89 var self = this;
90 var opt = {
91 url: WhAjaj.Connector.options.ajax.url + path,
92 method: doPost ? 'POST' : 'GET'
93 };
94 this.cgi.sendRequest( req, opt );
95 };
96 jQuery.fn.animateHighlight = function(highlightColor, duration) {
97 var highlightBg = highlightColor || "#FFFF9C";
98 var animateMs = duration || 1500;
99 var originalBg = this.css("backgroundColor");
100 this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
101 };
102 jQuery.fn.flash = function( color, duration )
103 {
104 var current = this.css( 'color' );
105 this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
106 this.animate( { color: current }, duration / 2 );
107 };
108
109 jQuery(document).ready(function(){
110 var ids = [
111 'btnSend',
112 'ajaxNotification',
113 'currentAuthToken',
114 'responseContainer',
115 'spanPageName',
116 'pageListArea',
117 'taPageContent',
118 'taResponse',
119 'textPath', // list of HTML element IDs we use often.
120 'timer'
121 ];
122 var i, k;
123 for( i = 0; i < ids.length; ++i ) {
124 k = ids[i];
125 TheApp.jqe[k] = jQuery('#'+k);
126 }
127 TheApp.jqe.textPath.
128 keyup(function(event){
129 if(event.keyCode == 13){
130 TheApp.sendRequest();
131 }
132 });
133 TheApp.timer = {
134 _tstart:0,_tend:0,duration:0,
135 start:function(){
136 this._tstart = (new Date()).getTime();
137 },
138 end:function(){
139 this._tend = (new Date()).getTime();
140 return this.duration = this._tend - this._tstart;
141 }
142 };
143 var ajcb = TheApp.cgi.ajaj.callbacks;
144 ajcb.beforeSend = TheApp.beforeSend = function(req,opt) {
145 TheApp.timer.start();
146 var val =
147 req ?
148 (('string'===typeof req) ? req : WhAjaj.stringify(req))
149 : '';
150 TheApp.jqe.taResponse.val('');
151 TheApp.startAjaxNotif();
152 };
153 ajcb.afterSend = TheApp.afterSend = function(req,opt) {
154 TheApp.timer.end();
155 TheApp.endAjaxNotif();
156 TheApp.jqe.timer.text( "(Round-trip time: "+TheApp.timer.duration+'ms)' );
157 };
158 ajcb.onResponse = TheApp.onResponse = function(resp,req) {
159 var val;
160 try {
161 val = WhAjaj.stringify(resp);
162 }
163 catch(e) {
164 val = WhAjaj.stringify(e)
165 }
166 if(resp.resultCode){
167 alert("Response contains error info:\n"+val);
168 }
169 TheApp.jqe.taResponse.val( val );
170 };
171 ajcb.onError = function(req,opt) {
172 TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
173 };
174
175 TheApp.jqe.taPageContent.blur(function(){
176 var p = TheApp.currentPage;
177 if(! p ) return;
178 p.content = TheApp.jqe.taPageContent.val();
179 });
180
181 TheApp.cgi.onLogin = function(){
182 TheApp.jqe.taResponse.val( "Logged in: "+WhAjaj.stringify(this.auth));
183 TheApp.jqe.currentAuthToken.text("Logged in: "+WhAjaj.stringify(this.auth));
184 };
185 TheApp.cgi.onLogout = function(){
186 TheApp.jqe.taResponse.val( "Logged out!" );
187 TheApp.jqe.currentAuthToken.text("");
188 };
189
190 TheApp.showPage = function(name){
191 function doShow(page){
192 TheApp.currentPage = page;
193 TheApp.jqe.spanPageName.text('('+page.name+')');
194 TheApp.jqe.taPageContent.val(page.content);
195 }
196 var p = ('object' === typeof name) ? name : TheApp.pages[name];
197 if(('object' === typeof p) && p.content) {
198 doShow(p);
199 return;
200 }
201 TheApp.cgi.sendCommand('/json/wiki/get',{
202 name:name
203 },{
204 onResponse:function(resp,req){
205 TheApp.onResponse(resp,req);
206 if(resp.resultCode) return;
207 var p = resp.payload;
208 doShow( TheApp.pages[p.name] = p );
209 }
210 });
211 };
212 TheApp.refreshPageListView = function(){
213 var list = (function(){
214 var k, v, li = [];
215 for( k in TheApp.pages ){
216 if(!TheApp.pages.hasOwnProperty(k)) continue;
217 li.push(k);
218 }
219 return li;
220 })();
221 var i, p, a, tgt = TheApp.jqe.pageListArea;
222 tgt.text('');
223 function makeLink(name){
224 var link = jQuery('<span></span>');
225 link.text(name);
226 link.addClass('wikiPageLink');
227 link.click(function(e){
228 TheApp.showPage(name);
229 e.preventDefault();
230 return false;
231 });
232 return link;
233 }
234 list.sort();
235 for( i = 0; i < list.length; ++i ){
236 tgt.append(makeLink(list[i]));
237 tgt.append('<br/>');
238 }
239 };
240
241 TheApp.loadPageList = function(){
242 TheApp.cgi.sendCommand('/json/wiki/list',null,{
243 onResponse:function(resp,req){
244 TheApp.onResponse(resp,req);
245 if(resp.resultCode) return;
246 var i, v, p, ar = resp.payload;
247 for( i = 0; i < ar.length; ++i ){
248 v = ar[i];
249 p = TheApp.pages[v];
250 if( !p ) TheApp.pages[v] = {name:v};
251 }
252 TheApp.refreshPageListView();
253 }
254 });
255 return false /*for click handlers*/;
256 }
257
258 TheApp.savePage = function(p){
259 p = p || TheApp.currentPage;
260 if( 'object' !== typeof p ){
261 p = TheApp.pages[p];
262 }
263 if('object' !== typeof p){
264 alert("savePage() argument is not a page object or known page name.");
265 }
266 TheApp.pages[p.name] = p;
267 p.content = TheApp.jqe.taPageContent.val();
268 var req = {
269 name:p.name,
270 content:p.content
271 };
272 if(! confirm("Really save wiki page ["+p.name+"]?") ) return;
273 TheApp.cgi.sendCommand('/json/wiki/'+(p.isNew?'create':'save'),req,{
274 onResponse:function(resp,req){
275 TheApp.onResponse(resp,req);
276 if(resp.resultCode) return;
277 delete p.isNew;
278 p.timestamp = resp.payload.timestamp;
279 }
280 });
281
282 };
283
284 TheApp.createNewPage = function(){
285 var name = prompt("New page name?");
286 if(!name) return;
287 var p = {
288 name:name,
289 content:"New, empty page.",
290 isNew:true
291 };
292 TheApp.pages[name] = p;
293 TheApp.refreshPageListView();
294 TheApp.showPage(p);
295 /*
296 if(! confirm("Really create new wiki page ["+name+"]?") ) return;
297 TheApp.cgi.sendCommand('/json/wiki/create',req,{
298 onResponse:function(resp,req){
299 TheApp.onResponse(resp,req);
300 if(resp.resultCode) return;
301 TheApp.pages[p.name] = p;
302 TheApp.refreshPageListView();
303 }
304 });
305 */
306 };
307
308 TheApp.cgi.whoami();
309
310 });
311
312 </script>
313
314 </head>
315
316 <body>
317 <span id='ajaxNotification'></span>
318 <h1>PROTOTYPE JSON-based Fossil Wiki Editor</h1>
319
320 See also: <a href='index.html'>main test page</a>.
321
322 <br>
323 <b>Login:</b>
324 <br/>
325 <input type='button' value='Anon. Login' onclick='TheApp.cgi.login()' />
326 or:
327 name:<input type='text' id='textUser' value='json-demo' size='12'/>
328 pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
329 <input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
330 <input type='button' value='logout' onclick='TheApp.cgi.logout()' />
331
332 <br/>
333 <span id='currentAuthToken' style='font-family:monospaced'></span>
334
335 <hr/>
336 <strong>Quick-posts:</strong><br/>
337 <input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
338 <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
339 <input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
340 <input type='button' value='wiki/list' onclick='TheApp.loadPageList()' />
341 <!--
342 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' />
--->
343
344 <!--
345 <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
346 <input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
347 <input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
348 <input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
--->
349 <hr/>
350
351 <table>
352 <tr>
353 <th>Page List</th>
354 <th>Content <span id='spanPageName'></span></th>
355 </tr>
356 <tr>
357 <td width='25%' valign='top'>
358 <input type='button' value='Create new...' onclick='TheApp.createNewPage()' /><br/>
359 <div id='pageListArea'></div>
360 </td>
361 <td width='75%' valign='top'>
362 <input type='button' value='Save' onclick='TheApp.savePage()' /><br/>
363 <textarea id='taPageContent' rows='20' cols='60'></textarea>
364 </td>
365 </tr>
366 <tr>
367 <th colspan='2'>Response <span id='timer'></span></th>
368 </tr>
369 <tr>
370 <td colspan='2' id='responseContainer'>
371 <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
372 </td>
373 </tr>
374 </table>
375 <div></div>
376 <div></div>
377 <div></div>
378
379 </body></html>
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -1,381 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--->
 
 
 
 
 
 
--->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
D art/encode1.tex
-2
--- a/art/encode1.tex
+++ b/art/encode1.tex
@@ -1,2 +0,0 @@
1
-\LARGE A = (\sum_{i=0}^{NHASH-1} z_i) \bmod 2^{16}
2
-
--- a/art/encode1.tex
+++ b/art/encode1.tex
@@ -1,2 +0,0 @@
1 \LARGE A = (\sum_{i=0}^{NHASH-1} z_i) \bmod 2^{16}
2
--- a/art/encode1.tex
+++ b/art/encode1.tex
@@ -1,2 +0,0 @@
 
 
D art/encode2.tex
-1
--- a/art/encode2.tex
+++ b/art/encode2.tex
@@ -1 +0,0 @@
1
-\LARGE B = (\sum_{i=0}^{NHASH-1} (NHASH-i)z_i) \bmod 2^{16}
--- a/art/encode2.tex
+++ b/art/encode2.tex
@@ -1 +0,0 @@
1 \LARGE B = (\sum_{i=0}^{NHASH-1} (NHASH-i)z_i) \bmod 2^{16}
--- a/art/encode2.tex
+++ b/art/encode2.tex
@@ -1 +0,0 @@
 
D art/encode3.tex
-1
--- a/art/encode3.tex
+++ b/art/encode3.tex
@@ -1 +0,0 @@
1
-\LARGE V = 2^{16}B + A
--- a/art/encode3.tex
+++ b/art/encode3.tex
@@ -1 +0,0 @@
1 \LARGE V = 2^{16}B + A
--- a/art/encode3.tex
+++ b/art/encode3.tex
@@ -1 +0,0 @@
 
D art/encode4.tex
-1
--- a/art/encode4.tex
+++ b/art/encode4.tex
@@ -1 +0,0 @@
1
-\LARGE z_0
--- a/art/encode4.tex
+++ b/art/encode4.tex
@@ -1 +0,0 @@
1 \LARGE z_0
--- a/art/encode4.tex
+++ b/art/encode4.tex
@@ -1 +0,0 @@
 
D art/encode5.tex
-1
--- a/art/encode5.tex
+++ b/art/encode5.tex
@@ -1 +0,0 @@
1
-\LARGE z_{new}
--- a/art/encode5.tex
+++ b/art/encode5.tex
@@ -1 +0,0 @@
1 \LARGE z_{new}
--- a/art/encode5.tex
+++ b/art/encode5.tex
@@ -1 +0,0 @@
 
D art/encode6.tex
-1
--- a/art/encode6.tex
+++ b/art/encode6.tex
@@ -1 +0,0 @@
1
-\LARGE A_{new} = (A - z_0 + z_{new}) \bmod 2^{16}
--- a/art/encode6.tex
+++ b/art/encode6.tex
@@ -1 +0,0 @@
1 \LARGE A_{new} = (A - z_0 + z_{new}) \bmod 2^{16}
--- a/art/encode6.tex
+++ b/art/encode6.tex
@@ -1 +0,0 @@
 
D art/encode7.tex
-1
--- a/art/encode7.tex
+++ b/art/encode7.tex
@@ -1 +0,0 @@
1
-\LARGE B_{new} = (B - z_0 NHASH + A_{new}) \bmod 2^{16}
--- a/art/encode7.tex
+++ b/art/encode7.tex
@@ -1 +0,0 @@
1 \LARGE B_{new} = (B - z_0 NHASH + A_{new}) \bmod 2^{16}
--- a/art/encode7.tex
+++ b/art/encode7.tex
@@ -1 +0,0 @@
 
D art/encode8.tex
-1
--- a/art/encode8.tex
+++ b/art/encode8.tex
@@ -1 +0,0 @@
1
-\LARGE V_{new} = 2^{16}B_{new} + A_{new}
--- a/art/encode8.tex
+++ b/art/encode8.tex
@@ -1 +0,0 @@
1 \LARGE V_{new} = 2^{16}B_{new} + A_{new}
--- a/art/encode8.tex
+++ b/art/encode8.tex
@@ -1 +0,0 @@
 
D art/encode9.tex
-1
--- a/art/encode9.tex
+++ b/art/encode9.tex
@@ -1 +0,0 @@
1
-\LARGE A_{new}
--- a/art/encode9.tex
+++ b/art/encode9.tex
@@ -1 +0,0 @@
1 \LARGE A_{new}
--- a/art/encode9.tex
+++ b/art/encode9.tex
@@ -1 +0,0 @@
 

Keyboard Shortcuts

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