OLD | NEW |
(Empty) | |
| 1 // https://github.com/github/fetch |
| 2 // |
| 3 // Copyright (c) 2014-2016 GitHub, Inc. |
| 4 // |
| 5 // Permission is hereby granted, free of charge, to any person obtaining |
| 6 // a copy of this software and associated documentation files (the |
| 7 // "Software"), to deal in the Software without restriction, including |
| 8 // without limitation the rights to use, copy, modify, merge, publish, |
| 9 // distribute, sublicense, and/or sell copies of the Software, and to |
| 10 // permit persons to whom the Software is furnished to do so, subject to |
| 11 // the following conditions: |
| 12 // |
| 13 // The above copyright notice and this permission notice shall be |
| 14 // included in all copies or substantial portions of the Software. |
| 15 // |
| 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 23 |
| 24 (function(self) { |
| 25 'use strict'; |
| 26 |
| 27 if (self.fetch) { |
| 28 return |
| 29 } |
| 30 |
| 31 var support = { |
| 32 searchParams: 'URLSearchParams' in self, |
| 33 iterable: 'Symbol' in self && 'iterator' in Symbol, |
| 34 blob: 'FileReader' in self && 'Blob' in self && (function() { |
| 35 try { |
| 36 new Blob() |
| 37 return true |
| 38 } catch(e) { |
| 39 return false |
| 40 } |
| 41 })(), |
| 42 formData: 'FormData' in self, |
| 43 arrayBuffer: 'ArrayBuffer' in self |
| 44 } |
| 45 |
| 46 function normalizeName(name) { |
| 47 if (typeof name !== 'string') { |
| 48 name = String(name) |
| 49 } |
| 50 if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { |
| 51 throw new TypeError('Invalid character in header field name') |
| 52 } |
| 53 return name.toLowerCase() |
| 54 } |
| 55 |
| 56 function normalizeValue(value) { |
| 57 if (typeof value !== 'string') { |
| 58 value = String(value) |
| 59 } |
| 60 return value |
| 61 } |
| 62 |
| 63 // Build a destructive iterator for the value list |
| 64 function iteratorFor(items) { |
| 65 var iterator = { |
| 66 next: function() { |
| 67 var value = items.shift() |
| 68 return {done: value === undefined, value: value} |
| 69 } |
| 70 } |
| 71 |
| 72 if (support.iterable) { |
| 73 iterator[Symbol.iterator] = function() { |
| 74 return iterator |
| 75 } |
| 76 } |
| 77 |
| 78 return iterator |
| 79 } |
| 80 |
| 81 function Headers(headers) { |
| 82 this.map = {} |
| 83 |
| 84 if (headers instanceof Headers) { |
| 85 headers.forEach(function(value, name) { |
| 86 this.append(name, value) |
| 87 }, this) |
| 88 |
| 89 } else if (headers) { |
| 90 Object.getOwnPropertyNames(headers).forEach(function(name) { |
| 91 this.append(name, headers[name]) |
| 92 }, this) |
| 93 } |
| 94 } |
| 95 |
| 96 Headers.prototype.append = function(name, value) { |
| 97 name = normalizeName(name) |
| 98 value = normalizeValue(value) |
| 99 var list = this.map[name] |
| 100 if (!list) { |
| 101 list = [] |
| 102 this.map[name] = list |
| 103 } |
| 104 list.push(value) |
| 105 } |
| 106 |
| 107 Headers.prototype['delete'] = function(name) { |
| 108 delete this.map[normalizeName(name)] |
| 109 } |
| 110 |
| 111 Headers.prototype.get = function(name) { |
| 112 var values = this.map[normalizeName(name)] |
| 113 return values ? values[0] : null |
| 114 } |
| 115 |
| 116 Headers.prototype.getAll = function(name) { |
| 117 return this.map[normalizeName(name)] || [] |
| 118 } |
| 119 |
| 120 Headers.prototype.has = function(name) { |
| 121 return this.map.hasOwnProperty(normalizeName(name)) |
| 122 } |
| 123 |
| 124 Headers.prototype.set = function(name, value) { |
| 125 this.map[normalizeName(name)] = [normalizeValue(value)] |
| 126 } |
| 127 |
| 128 Headers.prototype.forEach = function(callback, thisArg) { |
| 129 Object.getOwnPropertyNames(this.map).forEach(function(name) { |
| 130 this.map[name].forEach(function(value) { |
| 131 callback.call(thisArg, value, name, this) |
| 132 }, this) |
| 133 }, this) |
| 134 } |
| 135 |
| 136 Headers.prototype.keys = function() { |
| 137 var items = [] |
| 138 this.forEach(function(value, name) { items.push(name) }) |
| 139 return iteratorFor(items) |
| 140 } |
| 141 |
| 142 Headers.prototype.values = function() { |
| 143 var items = [] |
| 144 this.forEach(function(value) { items.push(value) }) |
| 145 return iteratorFor(items) |
| 146 } |
| 147 |
| 148 Headers.prototype.entries = function() { |
| 149 var items = [] |
| 150 this.forEach(function(value, name) { items.push([name, value]) }) |
| 151 return iteratorFor(items) |
| 152 } |
| 153 |
| 154 if (support.iterable) { |
| 155 Headers.prototype[Symbol.iterator] = Headers.prototype.entries |
| 156 } |
| 157 |
| 158 function consumed(body) { |
| 159 if (body.bodyUsed) { |
| 160 return Promise.reject(new TypeError('Already read')) |
| 161 } |
| 162 body.bodyUsed = true |
| 163 } |
| 164 |
| 165 function fileReaderReady(reader) { |
| 166 return new Promise(function(resolve, reject) { |
| 167 reader.onload = function() { |
| 168 resolve(reader.result) |
| 169 } |
| 170 reader.onerror = function() { |
| 171 reject(reader.error) |
| 172 } |
| 173 }) |
| 174 } |
| 175 |
| 176 function readBlobAsArrayBuffer(blob) { |
| 177 var reader = new FileReader() |
| 178 reader.readAsArrayBuffer(blob) |
| 179 return fileReaderReady(reader) |
| 180 } |
| 181 |
| 182 function readBlobAsText(blob) { |
| 183 var reader = new FileReader() |
| 184 reader.readAsText(blob) |
| 185 return fileReaderReady(reader) |
| 186 } |
| 187 |
| 188 function Body() { |
| 189 this.bodyUsed = false |
| 190 |
| 191 this._initBody = function(body) { |
| 192 this._bodyInit = body |
| 193 if (typeof body === 'string') { |
| 194 this._bodyText = body |
| 195 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { |
| 196 this._bodyBlob = body |
| 197 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { |
| 198 this._bodyFormData = body |
| 199 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf
(body)) { |
| 200 this._bodyText = body.toString() |
| 201 } else if (!body) { |
| 202 this._bodyText = '' |
| 203 } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body
)) { |
| 204 // Only support ArrayBuffers for POST method. |
| 205 // Receiving ArrayBuffers happens via Blobs, instead. |
| 206 } else { |
| 207 throw new Error('unsupported BodyInit type') |
| 208 } |
| 209 |
| 210 if (!this.headers.get('content-type')) { |
| 211 if (typeof body === 'string') { |
| 212 this.headers.set('content-type', 'text/plain;charset=UTF-8') |
| 213 } else if (this._bodyBlob && this._bodyBlob.type) { |
| 214 this.headers.set('content-type', this._bodyBlob.type) |
| 215 } else if (support.searchParams && URLSearchParams.prototype.isPrototype
Of(body)) { |
| 216 this.headers.set('content-type', 'application/x-www-form-urlencoded;ch
arset=UTF-8') |
| 217 } |
| 218 } |
| 219 } |
| 220 |
| 221 if (support.blob) { |
| 222 this.blob = function() { |
| 223 var rejected = consumed(this) |
| 224 if (rejected) { |
| 225 return rejected |
| 226 } |
| 227 |
| 228 if (this._bodyBlob) { |
| 229 return Promise.resolve(this._bodyBlob) |
| 230 } else if (this._bodyFormData) { |
| 231 throw new Error('could not read FormData body as blob') |
| 232 } else { |
| 233 return Promise.resolve(new Blob([this._bodyText])) |
| 234 } |
| 235 } |
| 236 |
| 237 this.arrayBuffer = function() { |
| 238 return this.blob().then(readBlobAsArrayBuffer) |
| 239 } |
| 240 |
| 241 this.text = function() { |
| 242 var rejected = consumed(this) |
| 243 if (rejected) { |
| 244 return rejected |
| 245 } |
| 246 |
| 247 if (this._bodyBlob) { |
| 248 return readBlobAsText(this._bodyBlob) |
| 249 } else if (this._bodyFormData) { |
| 250 throw new Error('could not read FormData body as text') |
| 251 } else { |
| 252 return Promise.resolve(this._bodyText) |
| 253 } |
| 254 } |
| 255 } else { |
| 256 this.text = function() { |
| 257 var rejected = consumed(this) |
| 258 return rejected ? rejected : Promise.resolve(this._bodyText) |
| 259 } |
| 260 } |
| 261 |
| 262 if (support.formData) { |
| 263 this.formData = function() { |
| 264 return this.text().then(decode) |
| 265 } |
| 266 } |
| 267 |
| 268 this.json = function() { |
| 269 return this.text().then(JSON.parse) |
| 270 } |
| 271 |
| 272 return this |
| 273 } |
| 274 |
| 275 // HTTP methods whose capitalization should be normalized |
| 276 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] |
| 277 |
| 278 function normalizeMethod(method) { |
| 279 var upcased = method.toUpperCase() |
| 280 return (methods.indexOf(upcased) > -1) ? upcased : method |
| 281 } |
| 282 |
| 283 function Request(input, options) { |
| 284 options = options || {} |
| 285 var body = options.body |
| 286 if (Request.prototype.isPrototypeOf(input)) { |
| 287 if (input.bodyUsed) { |
| 288 throw new TypeError('Already read') |
| 289 } |
| 290 this.url = input.url |
| 291 this.credentials = input.credentials |
| 292 if (!options.headers) { |
| 293 this.headers = new Headers(input.headers) |
| 294 } |
| 295 this.method = input.method |
| 296 this.mode = input.mode |
| 297 if (!body) { |
| 298 body = input._bodyInit |
| 299 input.bodyUsed = true |
| 300 } |
| 301 } else { |
| 302 this.url = input |
| 303 } |
| 304 |
| 305 this.credentials = options.credentials || this.credentials || 'omit' |
| 306 if (options.headers || !this.headers) { |
| 307 this.headers = new Headers(options.headers) |
| 308 } |
| 309 this.method = normalizeMethod(options.method || this.method || 'GET') |
| 310 this.mode = options.mode || this.mode || null |
| 311 this.referrer = null |
| 312 |
| 313 if ((this.method === 'GET' || this.method === 'HEAD') && body) { |
| 314 throw new TypeError('Body not allowed for GET or HEAD requests') |
| 315 } |
| 316 this._initBody(body) |
| 317 } |
| 318 |
| 319 Request.prototype.clone = function() { |
| 320 return new Request(this) |
| 321 } |
| 322 |
| 323 function decode(body) { |
| 324 var form = new FormData() |
| 325 body.trim().split('&').forEach(function(bytes) { |
| 326 if (bytes) { |
| 327 var split = bytes.split('=') |
| 328 var name = split.shift().replace(/\+/g, ' ') |
| 329 var value = split.join('=').replace(/\+/g, ' ') |
| 330 form.append(decodeURIComponent(name), decodeURIComponent(value)) |
| 331 } |
| 332 }) |
| 333 return form |
| 334 } |
| 335 |
| 336 function headers(xhr) { |
| 337 var head = new Headers() |
| 338 var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n') |
| 339 pairs.forEach(function(header) { |
| 340 var split = header.trim().split(':') |
| 341 var key = split.shift().trim() |
| 342 var value = split.join(':').trim() |
| 343 head.append(key, value) |
| 344 }) |
| 345 return head |
| 346 } |
| 347 |
| 348 Body.call(Request.prototype) |
| 349 |
| 350 function Response(bodyInit, options) { |
| 351 if (!options) { |
| 352 options = {} |
| 353 } |
| 354 |
| 355 this.type = 'default' |
| 356 this.status = options.status |
| 357 this.ok = this.status >= 200 && this.status < 300 |
| 358 this.statusText = options.statusText |
| 359 this.headers = options.headers instanceof Headers ? options.headers : new He
aders(options.headers) |
| 360 this.url = options.url || '' |
| 361 this._initBody(bodyInit) |
| 362 } |
| 363 |
| 364 Body.call(Response.prototype) |
| 365 |
| 366 Response.prototype.clone = function() { |
| 367 return new Response(this._bodyInit, { |
| 368 status: this.status, |
| 369 statusText: this.statusText, |
| 370 headers: new Headers(this.headers), |
| 371 url: this.url |
| 372 }) |
| 373 } |
| 374 |
| 375 Response.error = function() { |
| 376 var response = new Response(null, {status: 0, statusText: ''}) |
| 377 response.type = 'error' |
| 378 return response |
| 379 } |
| 380 |
| 381 var redirectStatuses = [301, 302, 303, 307, 308] |
| 382 |
| 383 Response.redirect = function(url, status) { |
| 384 if (redirectStatuses.indexOf(status) === -1) { |
| 385 throw new RangeError('Invalid status code') |
| 386 } |
| 387 |
| 388 return new Response(null, {status: status, headers: {location: url}}) |
| 389 } |
| 390 |
| 391 self.Headers = Headers |
| 392 self.Request = Request |
| 393 self.Response = Response |
| 394 |
| 395 self.fetch = function(input, init) { |
| 396 return new Promise(function(resolve, reject) { |
| 397 var request |
| 398 if (Request.prototype.isPrototypeOf(input) && !init) { |
| 399 request = input |
| 400 } else { |
| 401 request = new Request(input, init) |
| 402 } |
| 403 |
| 404 var xhr = new XMLHttpRequest() |
| 405 |
| 406 function responseURL() { |
| 407 if ('responseURL' in xhr) { |
| 408 return xhr.responseURL |
| 409 } |
| 410 |
| 411 // Avoid security warnings on getResponseHeader when not allowed by CORS |
| 412 if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { |
| 413 return xhr.getResponseHeader('X-Request-URL') |
| 414 } |
| 415 |
| 416 return |
| 417 } |
| 418 |
| 419 xhr.onload = function() { |
| 420 var options = { |
| 421 status: xhr.status, |
| 422 statusText: xhr.statusText, |
| 423 headers: headers(xhr), |
| 424 url: responseURL() |
| 425 } |
| 426 var body = 'response' in xhr ? xhr.response : xhr.responseText |
| 427 resolve(new Response(body, options)) |
| 428 } |
| 429 |
| 430 xhr.onerror = function() { |
| 431 reject(new TypeError('Network request failed')) |
| 432 } |
| 433 |
| 434 xhr.ontimeout = function() { |
| 435 reject(new TypeError('Network request failed')) |
| 436 } |
| 437 |
| 438 xhr.open(request.method, request.url, true) |
| 439 |
| 440 if (request.credentials === 'include') { |
| 441 xhr.withCredentials = true |
| 442 } |
| 443 |
| 444 if ('responseType' in xhr && support.blob) { |
| 445 xhr.responseType = 'blob' |
| 446 } |
| 447 |
| 448 request.headers.forEach(function(value, name) { |
| 449 xhr.setRequestHeader(name, value) |
| 450 }) |
| 451 |
| 452 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyIn
it) |
| 453 }) |
| 454 } |
| 455 self.fetch.polyfill = true |
| 456 })(typeof self !== 'undefined' ? self : this); |
OLD | NEW |