diff --git a/deps/fetch.js b/deps/fetch.js index 10ec9c6f..aa28bad6 100644 --- a/deps/fetch.js +++ b/deps/fetch.js @@ -1,418 +1,466 @@ -(function() { - 'use strict'; +define(function(require) { + 'use strict'; - // if __disableNativeFetch is set to true, the it will always polyfill fetch - // with Ajax. - if (!self.__disableNativeFetch && self.fetch) { - return - } + function vendorAccess() { + var platform = require('util').platform + if (platform.isQQ()) { + return false + } + return true + } - function normalizeName(name) { - if (typeof name !== 'string') { - name = String(name) + if (self.fetch && vendorAccess()) { + return } - if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { - throw new TypeError('Invalid character in header field name') + + var support = { + searchParams: 'URLSearchParams' in self, + iterable: 'Symbol' in self && 'iterator' in Symbol, + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob() + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self, + arrayBuffer: 'ArrayBuffer' in self } - return name.toLowerCase() - } - function normalizeValue(value) { - if (typeof value !== 'string') { - value = String(value) + if (support.arrayBuffer) { + var viewClasses = [ + '[object Int8Array]', + '[object Uint8Array]', + '[object Uint8ClampedArray]', + '[object Int16Array]', + '[object Uint16Array]', + '[object Int32Array]', + '[object Uint32Array]', + '[object Float32Array]', + '[object Float64Array]' + ] + + var isDataView = function(obj) { + return obj && DataView.prototype.isPrototypeOf(obj) + } + + var isArrayBufferView = ArrayBuffer.isView || function(obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } - return value } - function Headers(headers) { - this.map = {} + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name) + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value) + } + return value + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function() { + var value = items.shift() + return {done: value === undefined, value: value} + } + } - if (headers instanceof Headers) { - headers.forEach(function(value, name) { - this.append(name, value) - }, this) + if (support.iterable) { + iterator[Symbol.iterator] = function() { + return iterator + } + } - } else if (headers) { - Object.getOwnPropertyNames(headers).forEach(function(name) { - this.append(name, headers[name]) - }, this) + return iterator } - } - Headers.prototype.append = function(name, value) { - name = normalizeName(name) - value = normalizeValue(value) - var list = this.map[name] - if (!list) { - list = [] - this.map[name] = list + function Headers(headers) { + this.map = {} + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value) + }, this) + + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]) + }, this) + } } - list.push(value) - } - Headers.prototype['delete'] = function(name) { - delete this.map[normalizeName(name)] - } + Headers.prototype.append = function(name, value) { + name = normalizeName(name) + value = normalizeValue(value) + var oldValue = this.map[name] + this.map[name] = oldValue ? oldValue+','+value : value + } - Headers.prototype.get = function(name) { - var values = this.map[normalizeName(name)] - return values ? values[0] : null - } + Headers.prototype['delete'] = function(name) { + delete this.map[normalizeName(name)] + } - Headers.prototype.getAll = function(name) { - return this.map[normalizeName(name)] || [] - } + Headers.prototype.get = function(name) { + name = normalizeName(name) + return this.has(name) ? this.map[name] : null + } - Headers.prototype.has = function(name) { - return this.map.hasOwnProperty(normalizeName(name)) - } + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)) + } - Headers.prototype.set = function(name, value) { - this.map[normalizeName(name)] = [normalizeValue(value)] - } + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = normalizeValue(value) + } - Headers.prototype.forEach = function(callback, thisArg) { - Object.getOwnPropertyNames(this.map).forEach(function(name) { - this.map[name].forEach(function(value) { - callback.call(thisArg, value, name, this) - }, this) - }, this) - } + Headers.prototype.forEach = function(callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this) + } + } + } - function consumed(body) { - if (body.bodyUsed) { - return Promise.reject(new TypeError('Already read')) + Headers.prototype.keys = function() { + var items = [] + this.forEach(function(value, name) { items.push(name) }) + return iteratorFor(items) } - body.bodyUsed = true - } - function fileReaderReady(reader) { - return new Promise(function(resolve, reject) { - reader.onload = function() { - resolve(reader.result) - } - reader.onerror = function() { - reject(reader.error) - } - }) - } + Headers.prototype.values = function() { + var items = [] + this.forEach(function(value) { items.push(value) }) + return iteratorFor(items) + } - function readBlobAsArrayBuffer(blob) { - var reader = new FileReader() - reader.readAsArrayBuffer(blob) - return fileReaderReady(reader) - } + Headers.prototype.entries = function() { + var items = [] + this.forEach(function(value, name) { items.push([name, value]) }) + return iteratorFor(items) + } - function readBlobAsText(blob, options) { - var reader = new FileReader() - var contentType = options.headers.map['content-type'] ? options.headers.map['content-type'].toString() : '' - var regex = /charset\=[0-9a-zA-Z\-\_]*;?/ - var _charset = blob.type.match(regex) || contentType.match(regex) - var args = [blob] + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries + } - if(_charset) { - args.push(_charset[0].replace(/^charset\=/, '').replace(/;$/, '')) + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')) + } + body.bodyUsed = true } - reader.readAsText.apply(reader, args) - return fileReaderReady(reader) - } + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result) + } + reader.onerror = function() { + reject(reader.error) + } + }) + } - var support = { - blob: 'FileReader' in self && 'Blob' in self && (function() { - try { - new Blob(); - return true - } catch(e) { - return false - } - })(), - formData: 'FormData' in self, - arrayBuffer: 'ArrayBuffer' in self - } + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsArrayBuffer(blob) + return promise + } - function Body() { - this.bodyUsed = false - - - this._initBody = function(body, options) { - this._bodyInit = body - if (typeof body === 'string') { - this._bodyText = body - } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { - this._bodyBlob = body - this._options = options - } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { - this._bodyFormData = body - } else if (!body) { - this._bodyText = '' - } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { - // Only support ArrayBuffers for POST method. - // Receiving ArrayBuffers happens via Blobs, instead. - } else { - throw new Error('unsupported BodyInit type') - } - } - - if (support.blob) { - this.blob = function() { - var rejected = consumed(this) - if (rejected) { - return rejected + function readBlobAsText(blob) { + var reader = new FileReader() + var promise = fileReaderReady(reader) + reader.readAsText(blob) + return promise + } + + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf) + var chars = new Array(view.length) + + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]) } + return chars.join('') + } - if (this._bodyBlob) { - return Promise.resolve(this._bodyBlob) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as blob') + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0) } else { - return Promise.resolve(new Blob([this._bodyText])) + var view = new Uint8Array(buf.byteLength) + view.set(new Uint8Array(buf)) + return view.buffer } - } + } - this.arrayBuffer = function() { - return this.blob().then(readBlobAsArrayBuffer) - } + function Body() { + this.bodyUsed = false + + this._initBody = function(body) { + this._bodyInit = body + if (!body) { + this._bodyText = '' + } else if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString() + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer) + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]) + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body) + } else { + throw new Error('unsupported BodyInit type') + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8') + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type) + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') + } + } + } - this.text = function() { + if (support.blob) { + this.blob = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } + } + + this.arrayBuffer = function() { + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer) + } else { + return this.blob().then(readBlobAsArrayBuffer) + } + } + } + + this.text = function() { var rejected = consumed(this) if (rejected) { - return rejected + return rejected } if (this._bodyBlob) { - return readBlobAsText(this._bodyBlob, this._options) + return readBlobAsText(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { - throw new Error('could not read FormData body as text') + throw new Error('could not read FormData body as text') } else { - return Promise.resolve(this._bodyText) + return Promise.resolve(this._bodyText) } - } - } else { - this.text = function() { - var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(this._bodyText) - } } if (support.formData) { - this.formData = function() { - return this.text().then(decode) - } + this.formData = function() { + return this.text().then(decode) + } } this.json = function() { - return this.text().then(JSON.parse) + return this.text().then(JSON.parse) } return this - } - - // HTTP methods whose capitalization should be normalized - var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] - - function normalizeMethod(method) { - var upcased = method.toUpperCase() - return (methods.indexOf(upcased) > -1) ? upcased : method - } - - function Request(input, options) { - options = options || {} - var body = options.body - if (Request.prototype.isPrototypeOf(input)) { - if (input.bodyUsed) { - throw new TypeError('Already read') - } - this.url = input.url - this.credentials = input.credentials - if (!options.headers) { - this.headers = new Headers(input.headers) - } - this.method = input.method - this.mode = input.mode - if (!body) { - body = input._bodyInit - input.bodyUsed = true - } - } else { - this.url = input - } - - this.credentials = options.credentials || this.credentials || 'omit' - if (options.headers || !this.headers) { - this.headers = new Headers(options.headers) - } - this.method = normalizeMethod(options.method || this.method || 'GET') - this.mode = options.mode || this.mode || null - this.referrer = null - - if ((this.method === 'GET' || this.method === 'HEAD') && body) { - throw new TypeError('Body not allowed for GET or HEAD requests') - } - this._initBody(body, options) - } - - Request.prototype.clone = function() { - return new Request(this) - } - - function decode(body) { - var form = new FormData() - body.trim().split('&').forEach(function(bytes) { - if (bytes) { - var split = bytes.split('=') - var name = split.shift().replace(/\+/g, ' ') - var value = split.join('=').replace(/\+/g, ' ') - form.append(decodeURIComponent(name), decodeURIComponent(value)) - } - }) - return form - } - - function headers(xhr) { - var head = new Headers() - var pairs = xhr.getAllResponseHeaders().trim().split('\n') - pairs.forEach(function(header) { - var split = header.trim().split(':') - var key = split.shift().trim() - var value = split.join(':').trim() - head.append(key, value) - }) - return head - } - - Body.call(Request.prototype) - - function Response(bodyInit, options) { - if (!options) { - options = {} } - this._initBody(bodyInit, options) - this.type = 'default' - this.status = options.status - this.ok = this.status >= 200 && this.status < 300 - this.statusText = options.statusText - this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) - this.url = options.url || '' - } - - Body.call(Response.prototype) + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] - Response.prototype.clone = function() { - return new Response(this._bodyInit, { - status: this.status, - statusText: this.statusText, - headers: new Headers(this.headers), - url: this.url - }) - } + function normalizeMethod(method) { + var upcased = method.toUpperCase() + return (methods.indexOf(upcased) > -1) ? upcased : method + } - Response.error = function() { - var response = new Response(null, {status: 0, statusText: ''}) - response.type = 'error' - return response - } + function Request(input, options) { + options = options || {} + var body = options.body - var redirectStatuses = [301, 302, 303, 307, 308] + if (typeof input === 'string') { + this.url = input + } else { + if (input.bodyUsed) { + throw new TypeError('Already read') + } + this.url = input.url + this.credentials = input.credentials + if (!options.headers) { + this.headers = new Headers(input.headers) + } + this.method = input.method + this.mode = input.mode + if (!body && input._bodyInit != null) { + body = input._bodyInit + input.bodyUsed = true + } + } - Response.redirect = function(url, status) { - if (redirectStatuses.indexOf(status) === -1) { - throw new RangeError('Invalid status code') + this.credentials = options.credentials || this.credentials || 'omit' + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers) + } + this.method = normalizeMethod(options.method || this.method || 'GET') + this.mode = options.mode || this.mode || null + this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(body) } - return new Response(null, {status: status, headers: {location: url}}) - } + Request.prototype.clone = function() { + return new Request(this, { body: this._bodyInit }) + } - self.Headers = Headers; - self.Request = Request; - self.Response = Response; + function decode(body) { + var form = new FormData() + body.trim().split('&').forEach(function(bytes) { + if (bytes) { + var split = bytes.split('=') + var name = split.shift().replace(/\+/g, ' ') + var value = split.join('=').replace(/\+/g, ' ') + form.append(decodeURIComponent(name), decodeURIComponent(value)) + } + }) + return form + } - self.fetch = function(input, init) { - return new Promise(function(resolve, reject) { - var request - if (Request.prototype.isPrototypeOf(input) && !init) { - request = input - } else { - request = new Request(input, init) - } + function parseHeaders(rawHeaders) { + var headers = new Headers() + rawHeaders.split('\r\n').forEach(function(line) { + var parts = line.split(':') + var key = parts.shift().trim() + if (key) { + var value = parts.join(':').trim() + headers.append(key, value) + } + }) + return headers + } - var xhr = new XMLHttpRequest() + Body.call(Request.prototype) - function responseURL() { - if ('responseURL' in xhr) { - return xhr.responseURL + function Response(bodyInit, options) { + if (!options) { + options = {} } - // Avoid security warnings on getResponseHeader when not allowed by CORS - if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { - return xhr.getResponseHeader('X-Request-URL') - } + this.type = 'default' + this.status = 'status' in options ? options.status : 200 + this.ok = this.status >= 200 && this.status < 300 + this.statusText = 'statusText' in options ? options.statusText : 'OK' + this.headers = new Headers(options.headers) + this.url = options.url || '' + this._initBody(bodyInit) + } - return; - } + Body.call(Response.prototype) - var __onLoadHandled = false; + Response.prototype.clone = function() { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }) + } - function onload() { - if (xhr.readyState !== 4) { - return - } - var status = (xhr.status === 1223) ? 204 : xhr.status - if (status < 100 || status > 599) { - if (__onLoadHandled) { return; } else { __onLoadHandled = true; } - reject(new TypeError('Network request failed')) - return - } - var options = { - status: status, - statusText: xhr.statusText, - headers: headers(xhr), - url: responseURL() - } - var body = 'response' in xhr ? xhr.response : xhr.responseText; - - if (__onLoadHandled) { return; } else { __onLoadHandled = true; } - resolve(new Response(body, options)) - } - xhr.onreadystatechange = onload; - xhr.onload = onload; - xhr.onerror = function() { - if (__onLoadHandled) { return; } else { __onLoadHandled = true; } - reject(new TypeError('Network request failed')) - } - - xhr.open(request.method, request.url, true) - - // `withCredentials` should be setted after calling `.open` in IE10 - // http://stackoverflow.com/a/19667959/1219343 - try { - if (request.credentials === 'include') { - if ('withCredentials' in xhr) { - xhr.withCredentials = true; - } else { - console && console.warn && console.warn('withCredentials is not supported, you can ignore this warning'); - } - } - } catch (e) { - console && console.warn && console.warn('set withCredentials error:' + e); - } + Response.error = function() { + var response = new Response(null, {status: 0, statusText: ''}) + response.type = 'error' + return response + } - if ('responseType' in xhr && support.blob) { - xhr.responseType = 'blob' - } + var redirectStatuses = [301, 302, 303, 307, 308] - request.headers.forEach(function(value, name) { - xhr.setRequestHeader(name, value) - }) + Response.redirect = function(url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code') + } - xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) - }) - } - self.fetch.polyfill = true + return new Response(null, {status: status, headers: {location: url}}) + } - // Support CommonJS - if (typeof module !== 'undefined' && module.exports) { - module.exports = self.fetch; - } -})(); + self.Headers = Headers + self.Request = Request + self.Response = Response + + self.fetch = function(input, init) { + return new Promise(function(resolve, reject) { + var request = new Request(input, init) + var xhr = new XMLHttpRequest() + + xhr.onload = function() { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || '') + } + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') + var body = 'response' in xhr ? xhr.response : xhr.responseText + resolve(new Response(body, options)) + } + + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + + xhr.ontimeout = function() { + reject(new TypeError('Network request failed')) + } + + xhr.open(request.method, request.url, true) + + if (request.credentials === 'include') { + xhr.withCredentials = true + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob' + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value) + }) + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) + }) + } + self.fetch.polyfill = true +}); \ No newline at end of file diff --git a/src/mip.js b/src/mip.js index b1c00a16..9eae0c20 100755 --- a/src/mip.js +++ b/src/mip.js @@ -2,6 +2,7 @@ define(function (require) { require('zepto'); require('naboo'); require('fetch-jsonp'); + require('fetch'); require('./utils/fn'); require('./utils/gesture/gesture-recognizer'); diff --git a/tools/build.js b/tools/build.js index 900e7c0b..65f620c1 100644 --- a/tools/build.js +++ b/tools/build.js @@ -30,9 +30,10 @@ var requireConfig = { paths: { 'zepto' : '../deps/zepto', 'naboo' : '../deps/naboo', - 'fetch-jsonp' : '../deps/fetch-jsonp' + 'fetch-jsonp' : '../deps/fetch-jsonp', + 'fetch' : '../deps/fetch', } - + // , // packages: [ // { @@ -50,7 +51,8 @@ var amdCompiler = new AMDCompiler({ 'src/**/*.js', 'deps/naboo.js', 'deps/zepto.js', - 'deps/fetch-jsonp.js' + 'deps/fetch-jsonp.js', + 'deps/fetch.js' ] }); @@ -61,7 +63,8 @@ var testAmdCompiler = new AMDCompiler({ 'test': '../test', 'zepto' : '../deps/zepto', 'naboo' : '../deps/naboo', - 'fetch-jsonp' : '../deps/fetch-jsonp' + 'fetch-jsonp' : '../deps/fetch-jsonp', + 'fetch' : '../deps/fetch', } }, files: [ @@ -69,6 +72,7 @@ var testAmdCompiler = new AMDCompiler({ 'deps/naboo.js', 'deps/zepto.js', 'deps/fetch-jsonp.js', + 'deps/fetch.js', 'test/**/*.js' ] }); @@ -93,7 +97,6 @@ var mainCombiner = new Combiner({ 'src/mip.js': [ 'src/prefix.js', 'deps/promise.js', - 'deps/fetch.js', 'deps/document-register-element.max.js', 'deps/esl.js', 'src/mip.js'