Fossil SCM
Added fossil.fetch() responseHeaders option to pass on one or more response headers to the onload() callback.
Commit
bbb738d07dfbecb9f3f5923765fb6c1cf66fb301bf36ec4bfc3ba69782a7b46c
Parent
c145140d3ee52d2…
1 file changed
+54
-17
+54
-17
| --- src/fossil.fetch.js | ||
| +++ src/fossil.fetch.js | ||
| @@ -40,17 +40,30 @@ | ||
| 40 | 40 | |
| 41 | 41 | - responseType: optional string. One of ("text", "arraybuffer", |
| 42 | 42 | "blob", or "document") (as specified by XHR2). Default = "text". |
| 43 | 43 | As an extension, it supports "json", which tells it that the |
| 44 | 44 | response is expected to be text and that it should be JSON.parse()d |
| 45 | - before passing it on to the onload() callback. | |
| 45 | + before passing it on to the onload() callback. If parsing of such | |
| 46 | + an object fails, the onload callback is not called, and the | |
| 47 | + onerror() callback is passed the exception from the parsing error. | |
| 46 | 48 | |
| 47 | 49 | - urlParams: string|object. If a string, it is assumed to be a |
| 48 | 50 | URI-encoded list of params in the form "key1=val1&key2=val2...", |
| 49 | 51 | with NO leading '?'. If it is an object, all of its properties get |
| 50 | 52 | converted to that form. Either way, the parameters get appended to |
| 51 | 53 | the URL before submitting the request. |
| 54 | + | |
| 55 | + - responseHeaders: If true, the onload() callback is passed an | |
| 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 | + | |
| 52 | 65 | |
| 53 | 66 | When an options object does not provide onload() or onerror() |
| 54 | 67 | handlers of its own, this function falls back to |
| 55 | 68 | fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The |
| 56 | 69 | default implementations route the data through the dev console and |
| @@ -61,11 +74,11 @@ | ||
| 61 | 74 | Returns this object, noting that the XHR request is asynchronous, |
| 62 | 75 | and still in transit (or has yet to be sent) when that happens. |
| 63 | 76 | */ |
| 64 | 77 | window.fossil.fetch = function f(uri,opt){ |
| 65 | 78 | const F = fossil; |
| 66 | - if(!f.onerror){ | |
| 79 | + if(!f.onerror){/* "static" functions... */ | |
| 67 | 80 | f.onerror = function(e/*event or exception*/){ |
| 68 | 81 | console.error("Ajax error:",e); |
| 69 | 82 | if(e instanceof Error){ |
| 70 | 83 | F.error('Exception:',e); |
| 71 | 84 | } |
| @@ -83,11 +96,23 @@ | ||
| 83 | 96 | F.error(txt) |
| 84 | 97 | } |
| 85 | 98 | } |
| 86 | 99 | }; |
| 87 | 100 | f.onload = (r)=>console.debug('ajax response:',r); |
| 88 | - } | |
| 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) { | |
| 106 | + const parts = line.split(': '); | |
| 107 | + const header = parts.shift(); | |
| 108 | + const value = parts.join(': '); | |
| 109 | + rc[header.toLowerCase()] = value; | |
| 110 | + }); | |
| 111 | + return rc; | |
| 112 | + }; | |
| 113 | + }/*static init*/ | |
| 89 | 114 | if('/'===uri[0]) uri = uri.substr(1); |
| 90 | 115 | if(!opt) opt = {}; |
| 91 | 116 | else if('function'===typeof opt) opt={onload:opt}; |
| 92 | 117 | if(!opt.onload) opt.onload = f.onload; |
| 93 | 118 | if(!opt.onerror) opt.onerror = f.onerror; |
| @@ -118,23 +143,35 @@ | ||
| 118 | 143 | jsonResponse = true; |
| 119 | 144 | x.responseType = 'text'; |
| 120 | 145 | }else{ |
| 121 | 146 | x.responseType = opt.responseType||'text'; |
| 122 | 147 | } |
| 123 | - if(opt.onload){ | |
| 124 | - x.onload = function(e){ | |
| 125 | - if(200!==this.status){ | |
| 126 | - if(opt.onerror) opt.onerror(e); | |
| 127 | - return; | |
| 128 | - } | |
| 129 | - try{ | |
| 130 | - opt.onload((jsonResponse && this.response) | |
| 131 | - ? JSON.parse(this.response) : this.response); | |
| 132 | - }catch(e){ | |
| 133 | - if(opt.onerror) opt.onerror(e); | |
| 134 | - } | |
| 135 | - } | |
| 136 | - } | |
| 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){ | |
| 156 | + head = f.parseResponseHeaders(this.getAllResponseHeaders()); | |
| 157 | + }else if('string'===typeof orh){ | |
| 158 | + head = this.getResponseHeader(orh); | |
| 159 | + }else if(orh instanceof Array){ | |
| 160 | + head = {}; | |
| 161 | + orh.forEach((s)=>{ | |
| 162 | + if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s); | |
| 163 | + }); | |
| 164 | + } | |
| 165 | + try{ | |
| 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 | + }; | |
| 137 | 174 | if(undefined!==payload) x.send(payload); |
| 138 | 175 | else x.send(); |
| 139 | 176 | return this; |
| 140 | 177 | }; |
| 141 | 178 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -40,17 +40,30 @@ | |
| 40 | |
| 41 | - responseType: optional string. One of ("text", "arraybuffer", |
| 42 | "blob", or "document") (as specified by XHR2). Default = "text". |
| 43 | As an extension, it supports "json", which tells it that the |
| 44 | response is expected to be text and that it should be JSON.parse()d |
| 45 | before passing it on to the onload() callback. |
| 46 | |
| 47 | - urlParams: string|object. If a string, it is assumed to be a |
| 48 | URI-encoded list of params in the form "key1=val1&key2=val2...", |
| 49 | with NO leading '?'. If it is an object, all of its properties get |
| 50 | converted to that form. Either way, the parameters get appended to |
| 51 | the URL before submitting the request. |
| 52 | |
| 53 | When an options object does not provide onload() or onerror() |
| 54 | handlers of its own, this function falls back to |
| 55 | fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The |
| 56 | default implementations route the data through the dev console and |
| @@ -61,11 +74,11 @@ | |
| 61 | Returns this object, noting that the XHR request is asynchronous, |
| 62 | and still in transit (or has yet to be sent) when that happens. |
| 63 | */ |
| 64 | window.fossil.fetch = function f(uri,opt){ |
| 65 | const F = fossil; |
| 66 | if(!f.onerror){ |
| 67 | f.onerror = function(e/*event or exception*/){ |
| 68 | console.error("Ajax error:",e); |
| 69 | if(e instanceof Error){ |
| 70 | F.error('Exception:',e); |
| 71 | } |
| @@ -83,11 +96,23 @@ | |
| 83 | F.error(txt) |
| 84 | } |
| 85 | } |
| 86 | }; |
| 87 | f.onload = (r)=>console.debug('ajax response:',r); |
| 88 | } |
| 89 | if('/'===uri[0]) uri = uri.substr(1); |
| 90 | if(!opt) opt = {}; |
| 91 | else if('function'===typeof opt) opt={onload:opt}; |
| 92 | if(!opt.onload) opt.onload = f.onload; |
| 93 | if(!opt.onerror) opt.onerror = f.onerror; |
| @@ -118,23 +143,35 @@ | |
| 118 | jsonResponse = true; |
| 119 | x.responseType = 'text'; |
| 120 | }else{ |
| 121 | x.responseType = opt.responseType||'text'; |
| 122 | } |
| 123 | if(opt.onload){ |
| 124 | x.onload = function(e){ |
| 125 | if(200!==this.status){ |
| 126 | if(opt.onerror) opt.onerror(e); |
| 127 | return; |
| 128 | } |
| 129 | try{ |
| 130 | opt.onload((jsonResponse && this.response) |
| 131 | ? JSON.parse(this.response) : this.response); |
| 132 | }catch(e){ |
| 133 | if(opt.onerror) opt.onerror(e); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | if(undefined!==payload) x.send(payload); |
| 138 | else x.send(); |
| 139 | return this; |
| 140 | }; |
| 141 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -40,17 +40,30 @@ | |
| 40 | |
| 41 | - responseType: optional string. One of ("text", "arraybuffer", |
| 42 | "blob", or "document") (as specified by XHR2). Default = "text". |
| 43 | As an extension, it supports "json", which tells it that the |
| 44 | response is expected to be text and that it should be JSON.parse()d |
| 45 | before passing it on to the onload() callback. If parsing of such |
| 46 | an object fails, the onload callback is not called, and the |
| 47 | onerror() callback is passed the exception from the parsing error. |
| 48 | |
| 49 | - urlParams: string|object. If a string, it is assumed to be a |
| 50 | URI-encoded list of params in the form "key1=val1&key2=val2...", |
| 51 | with NO leading '?'. If it is an object, all of its properties get |
| 52 | converted to that form. Either way, the parameters get appended to |
| 53 | the URL before submitting the request. |
| 54 | |
| 55 | - responseHeaders: If true, the onload() callback is passed an |
| 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 |
| @@ -61,11 +74,11 @@ | |
| 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 | } |
| @@ -83,11 +96,23 @@ | |
| 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) { |
| 106 | const parts = line.split(': '); |
| 107 | const header = parts.shift(); |
| 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; |
| @@ -118,23 +143,35 @@ | |
| 143 | jsonResponse = true; |
| 144 | x.responseType = 'text'; |
| 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){ |
| 156 | head = f.parseResponseHeaders(this.getAllResponseHeaders()); |
| 157 | }else if('string'===typeof orh){ |
| 158 | head = this.getResponseHeader(orh); |
| 159 | }else if(orh instanceof Array){ |
| 160 | head = {}; |
| 161 | orh.forEach((s)=>{ |
| 162 | if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s); |
| 163 | }); |
| 164 | } |
| 165 | try{ |
| 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 |