Fossil SCM

Fixed a potential corner-case bug which would have broken fossil.fetch() if a user had defined their own fossil.fetch.onerror() impl before calling fossil.fetch() the first time.

stephan 2020-05-12 16:26 fileedit-ajaxify
Commit 695dde004c4e6f533cf5bd07cd989c3500bf5da5557c2dc883b31c32d954d3bd
1 file changed +22 -15
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -17,25 +17,29 @@
1717
the equivalent of "%R/" prepended to it.
1818
1919
The optionsObject may be an onload callback or an object with any
2020
of these properties:
2121
22
- - onload: callback(responseData) (default = output response to
23
- the console).
22
+ - onload: callback(responseData) (default = output response to the
23
+ console). In the context of the callback, the options object is
24
+ "this", noting that this call may have amended the options object
25
+ with state other than what the caller provided.
2426
2527
- onerror: callback(XHR onload event | exception)
2628
(default = event or exception to the console).
2729
2830
- method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!
2931
3032
- payload: anything acceptable by XHR2.send(ARG) (DOMString,
3133
Document, FormData, Blob, File, ArrayBuffer), or a plain object or
3234
array, either of which gets JSON.stringify()'d. If payload is set
33
- then the method is automatically set to 'POST'. If an object/array
34
- is converted to JSON, the contentType option is automatically set
35
- to 'application/json'. By default XHR2 will set the content type
36
- based on the payload type.
35
+ then the method is automatically set to 'POST'. By default XHR2
36
+ will set the content type based on the payload type. If an
37
+ object/array is converted to JSON, the contentType option is
38
+ automatically set to 'application/json', and if JSON.stringify() of
39
+ that value fails then the exception is propagated to this
40
+ function's caller.
3741
3842
- contentType: Optional request content type when POSTing. Ignored
3943
if the method is not 'POST'.
4044
4145
- responseType: optional string. One of ("text", "arraybuffer",
@@ -56,14 +60,13 @@
5660
additional argument: a map of all of the response headers. If it's
5761
a string value, the 2nd argument passed to onload() is instead the
5862
value of that single header. If it's an array, it's treated as a
5963
list of headers to return, and the 2nd argument is a map of those
6064
header values. When a map is passed on, all of its keys are
61
- lower-cased. When a single header is requested and that header is
62
- set multiple times, they are (per the XHR docs) concatenated
63
- together with ", " between them.
64
-
65
+ lower-cased. When a given header is requested and that header is
66
+ set multiple times, their values are (per the XHR docs)
67
+ concatenated together with ", " between them.
6568
6669
When an options object does not provide onload() or onerror()
6770
handlers of its own, this function falls back to
6871
fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
6972
default implementations route the data through the dev console and
@@ -74,11 +77,14 @@
7477
Returns this object, noting that the XHR request is asynchronous,
7578
and still in transit (or has yet to be sent) when that happens.
7679
*/
7780
window.fossil.fetch = function f(uri,opt){
7881
const F = fossil;
79
- if(!f.onerror){/* "static" functions... */
82
+ if(!f.onload){
83
+ f.onload = (r)=>console.debug('ajax response:',r);
84
+ }
85
+ if(!f.onerror){
8086
f.onerror = function(e/*event or exception*/){
8187
console.error("Ajax error:",e);
8288
if(e instanceof Error){
8389
F.error('Exception:',e);
8490
}
@@ -95,11 +101,12 @@
95101
}catch(e){/* Try harder */
96102
F.error(txt)
97103
}
98104
}
99105
};
100
- f.onload = (r)=>console.debug('ajax response:',r);
106
+ }/*f.onerror()*/
107
+ if(!f.parseResponseHeaders){
101108
f.parseResponseHeaders = function(h){
102109
const rc = {};
103110
if(!h) return rc;
104111
const ar = h.trim().split(/[\r\n]+/);
105112
ar.forEach(function(line) {
@@ -108,11 +115,11 @@
108115
const value = parts.join(': ');
109116
rc[header.toLowerCase()] = value;
110117
});
111118
return rc;
112119
};
113
- }/*static init*/
120
+ }
114121
if('/'===uri[0]) uri = uri.substr(1);
115122
if(!opt) opt = {};
116123
else if('function'===typeof opt) opt={onload:opt};
117124
if(!opt.onload) opt.onload = f.onload;
118125
if(!opt.onerror) opt.onerror = f.onerror;
@@ -145,11 +152,11 @@
145152
}else{
146153
x.responseType = opt.responseType||'text';
147154
}
148155
x.onload = function(e){
149156
if(200!==this.status){
150
- if(opt.onerror) opt.onerror(e);
157
+ opt.onerror(e);
151158
return;
152159
}
153160
const orh = opt.responseHeaders;
154161
let head;
155162
if(true===orh){
@@ -166,12 +173,12 @@
166173
const args = [(jsonResponse && this.response)
167174
? JSON.parse(this.response) : this.response];
168175
if(head) args.push(head);
169176
opt.onload.apply(opt, args);
170177
}catch(e){
171
- if(opt.onerror) opt.onerror(e);
178
+ opt.onerror(e);
172179
}
173180
};
174181
if(undefined!==payload) x.send(payload);
175182
else x.send();
176183
return this;
177184
};
178185
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -17,25 +17,29 @@
17 the equivalent of "%R/" prepended to it.
18
19 The optionsObject may be an onload callback or an object with any
20 of these properties:
21
22 - onload: callback(responseData) (default = output response to
23 the console).
 
 
24
25 - onerror: callback(XHR onload event | exception)
26 (default = event or exception to the console).
27
28 - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!
29
30 - payload: anything acceptable by XHR2.send(ARG) (DOMString,
31 Document, FormData, Blob, File, ArrayBuffer), or a plain object or
32 array, either of which gets JSON.stringify()'d. If payload is set
33 then the method is automatically set to 'POST'. If an object/array
34 is converted to JSON, the contentType option is automatically set
35 to 'application/json'. By default XHR2 will set the content type
36 based on the payload type.
 
 
37
38 - contentType: Optional request content type when POSTing. Ignored
39 if the method is not 'POST'.
40
41 - responseType: optional string. One of ("text", "arraybuffer",
@@ -56,14 +60,13 @@
56 additional argument: a map of all of the response headers. If it's
57 a string value, the 2nd argument passed to onload() is instead the
58 value of that single header. If it's an array, it's treated as a
59 list of headers to return, and the 2nd argument is a map of those
60 header values. When a map is passed on, all of its keys are
61 lower-cased. When a single header is requested and that header is
62 set multiple times, they are (per the XHR docs) concatenated
63 together with ", " between them.
64
65
66 When an options object does not provide onload() or onerror()
67 handlers of its own, this function falls back to
68 fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
69 default implementations route the data through the dev console and
@@ -74,11 +77,14 @@
74 Returns this object, noting that the XHR request is asynchronous,
75 and still in transit (or has yet to be sent) when that happens.
76 */
77 window.fossil.fetch = function f(uri,opt){
78 const F = fossil;
79 if(!f.onerror){/* "static" functions... */
 
 
 
80 f.onerror = function(e/*event or exception*/){
81 console.error("Ajax error:",e);
82 if(e instanceof Error){
83 F.error('Exception:',e);
84 }
@@ -95,11 +101,12 @@
95 }catch(e){/* Try harder */
96 F.error(txt)
97 }
98 }
99 };
100 f.onload = (r)=>console.debug('ajax response:',r);
 
101 f.parseResponseHeaders = function(h){
102 const rc = {};
103 if(!h) return rc;
104 const ar = h.trim().split(/[\r\n]+/);
105 ar.forEach(function(line) {
@@ -108,11 +115,11 @@
108 const value = parts.join(': ');
109 rc[header.toLowerCase()] = value;
110 });
111 return rc;
112 };
113 }/*static init*/
114 if('/'===uri[0]) uri = uri.substr(1);
115 if(!opt) opt = {};
116 else if('function'===typeof opt) opt={onload:opt};
117 if(!opt.onload) opt.onload = f.onload;
118 if(!opt.onerror) opt.onerror = f.onerror;
@@ -145,11 +152,11 @@
145 }else{
146 x.responseType = opt.responseType||'text';
147 }
148 x.onload = function(e){
149 if(200!==this.status){
150 if(opt.onerror) opt.onerror(e);
151 return;
152 }
153 const orh = opt.responseHeaders;
154 let head;
155 if(true===orh){
@@ -166,12 +173,12 @@
166 const args = [(jsonResponse && this.response)
167 ? JSON.parse(this.response) : this.response];
168 if(head) args.push(head);
169 opt.onload.apply(opt, args);
170 }catch(e){
171 if(opt.onerror) opt.onerror(e);
172 }
173 };
174 if(undefined!==payload) x.send(payload);
175 else x.send();
176 return this;
177 };
178
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -17,25 +17,29 @@
17 the equivalent of "%R/" prepended to it.
18
19 The optionsObject may be an onload callback or an object with any
20 of these properties:
21
22 - onload: callback(responseData) (default = output response to the
23 console). In the context of the callback, the options object is
24 "this", noting that this call may have amended the options object
25 with state other than what the caller provided.
26
27 - onerror: callback(XHR onload event | exception)
28 (default = event or exception to the console).
29
30 - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!
31
32 - payload: anything acceptable by XHR2.send(ARG) (DOMString,
33 Document, FormData, Blob, File, ArrayBuffer), or a plain object or
34 array, either of which gets JSON.stringify()'d. If payload is set
35 then the method is automatically set to 'POST'. By default XHR2
36 will set the content type based on the payload type. If an
37 object/array is converted to JSON, the contentType option is
38 automatically set to 'application/json', and if JSON.stringify() of
39 that value fails then the exception is propagated to this
40 function's caller.
41
42 - contentType: Optional request content type when POSTing. Ignored
43 if the method is not 'POST'.
44
45 - responseType: optional string. One of ("text", "arraybuffer",
@@ -56,14 +60,13 @@
60 additional argument: a map of all of the response headers. If it's
61 a string value, the 2nd argument passed to onload() is instead the
62 value of that single header. If it's an array, it's treated as a
63 list of headers to return, and the 2nd argument is a map of those
64 header values. When a map is passed on, all of its keys are
65 lower-cased. When a given header is requested and that header is
66 set multiple times, their values are (per the XHR docs)
67 concatenated together with ", " between them.
 
68
69 When an options object does not provide onload() or onerror()
70 handlers of its own, this function falls back to
71 fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
72 default implementations route the data through the dev console and
@@ -74,11 +77,14 @@
77 Returns this object, noting that the XHR request is asynchronous,
78 and still in transit (or has yet to be sent) when that happens.
79 */
80 window.fossil.fetch = function f(uri,opt){
81 const F = fossil;
82 if(!f.onload){
83 f.onload = (r)=>console.debug('ajax response:',r);
84 }
85 if(!f.onerror){
86 f.onerror = function(e/*event or exception*/){
87 console.error("Ajax error:",e);
88 if(e instanceof Error){
89 F.error('Exception:',e);
90 }
@@ -95,11 +101,12 @@
101 }catch(e){/* Try harder */
102 F.error(txt)
103 }
104 }
105 };
106 }/*f.onerror()*/
107 if(!f.parseResponseHeaders){
108 f.parseResponseHeaders = function(h){
109 const rc = {};
110 if(!h) return rc;
111 const ar = h.trim().split(/[\r\n]+/);
112 ar.forEach(function(line) {
@@ -108,11 +115,11 @@
115 const value = parts.join(': ');
116 rc[header.toLowerCase()] = value;
117 });
118 return rc;
119 };
120 }
121 if('/'===uri[0]) uri = uri.substr(1);
122 if(!opt) opt = {};
123 else if('function'===typeof opt) opt={onload:opt};
124 if(!opt.onload) opt.onload = f.onload;
125 if(!opt.onerror) opt.onerror = f.onerror;
@@ -145,11 +152,11 @@
152 }else{
153 x.responseType = opt.responseType||'text';
154 }
155 x.onload = function(e){
156 if(200!==this.status){
157 opt.onerror(e);
158 return;
159 }
160 const orh = opt.responseHeaders;
161 let head;
162 if(true===orh){
@@ -166,12 +173,12 @@
173 const args = [(jsonResponse && this.response)
174 ? JSON.parse(this.response) : this.response];
175 if(head) args.push(head);
176 opt.onload.apply(opt, args);
177 }catch(e){
178 opt.onerror(e);
179 }
180 };
181 if(undefined!==payload) x.send(payload);
182 else x.send();
183 return this;
184 };
185

Keyboard Shortcuts

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