diff --git a/.zuul.yml b/.zuul.yml index 978fb0a..7b45294 100644 --- a/.zuul.yml +++ b/.zuul.yml @@ -16,4 +16,5 @@ browsers: version: 4.0..latest server: ./test/server/index.js scripts: - - "/polyfill.js" \ No newline at end of file + - "/ie8-polyfill.js" + - "/test-polyfill.js" \ No newline at end of file diff --git a/README.md b/README.md index 23da71c..9ec4846 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,13 @@ All browsers newer than IE8 support binary responses. All of the above browsers support true streaming or pseudo-streaming support that for binary data as well except for IE10. Old (presto-based) Opera also does not support binary streaming either. +### IE8 note: +As of version 2.0.0, IE8 support requires the user to supply polyfills for +`Object.keys`, `Array.prototype.forEach`, and `Array.prototype.indexOf`. Example +implementations are provided in [ie8-polyfill.js](ie8-polyfill.js); alternately, +you may want to consider using [es5-shim](https://github.com/es-shims/es5-shim). +All browsers with full ES5 support shouldn't require any polyfills. + ## How do you use it? The intent is to have the same api as the client part of the diff --git a/ie8-polyfill.js b/ie8-polyfill.js new file mode 100644 index 0000000..adea0fa --- /dev/null +++ b/ie8-polyfill.js @@ -0,0 +1,168 @@ +// These polyfills taken from MDN (developer.mozilla.org) + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys +if (!Object.keys) { + Object.keys = (function() { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], prop, i; + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); +} + +// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.io/#x15.4.4.18 +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function(callback, thisArg) { + + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling ToObject passing the |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If IsCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let k be 0 + k = 0; + + // 7. Repeat, while k < len + while (k < len) { + + var kValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + + // i. Let kValue be the result of calling the Get internal method of O with argument Pk. + kValue = O[k]; + + // ii. Call the Call internal method of callback with T as the this value and + // argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // d. Increase k by 1. + k++; + } + // 8. return undefined + }; +} + +// Production steps of ECMA-262, Edition 5, 15.4.4.14 +// Reference: http://es5.github.io/#x15.4.4.14 +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement, fromIndex) { + + var k; + + // 1. Let O be the result of calling ToObject passing + // the this value as the argument. + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get + // internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If len is 0, return -1. + if (len === 0) { + return -1; + } + + // 5. If argument fromIndex was passed let n be + // ToInteger(fromIndex); else let n be 0. + var n = +fromIndex || 0; + + if (Math.abs(n) === Infinity) { + n = 0; + } + + // 6. If n >= len, return -1. + if (n >= len) { + return -1; + } + + // 7. If n >= 0, then Let k be n. + // 8. Else, n<0, Let k be len - abs(n). + // If k is less than 0, then let k be 0. + k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + + // 9. Repeat, while k < len + while (k < len) { + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the + // HasProperty internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + // i. Let elementK be the result of calling the Get + // internal method of O with the argument ToString(k). + // ii. Let same be the result of applying the + // Strict Equality Comparison Algorithm to + // searchElement and elementK. + // iii. If same is true, return k. + if (k in O && O[k] === searchElement) { + return k; + } + k++; + } + return -1; + }; +} \ No newline at end of file diff --git a/lib/request.js b/lib/request.js index 504f974..9bb3c4b 100644 --- a/lib/request.js +++ b/lib/request.js @@ -1,9 +1,6 @@ // var Base64 = require('Base64') var capability = require('./capability') -var foreach = require('foreach') -var indexOf = require('indexof') var inherits = require('inherits') -var keys = require('object-keys') var response = require('./response') var stream = require('stream') @@ -35,7 +32,7 @@ var ClientRequest = module.exports = function (opts) { self._headers = {} if (opts.auth) self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64')) - foreach(keys(opts.headers), function (name) { + Object.keys(opts.headers).forEach(function (name) { self.setHeader(name, opts.headers[name]) }) @@ -68,7 +65,7 @@ ClientRequest.prototype.setHeader = function (name, value) { // This check is not necessary, but it prevents warnings from browsers about setting unsafe // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but // http-browserify did it, so I will too. - if (indexOf(unsafeHeaders, lowerName) !== -1) + if (unsafeHeaders.indexOf(lowerName) !== -1) return self._headers[lowerName] = { @@ -110,7 +107,7 @@ ClientRequest.prototype._onFinish = function () { } if (self._mode === 'fetch') { - var headers = keys(headersObj).map(function (name) { + var headers = Object.keys(headersObj).map(function (name) { return [headersObj[name].name, headersObj[name].value] }) @@ -147,7 +144,7 @@ ClientRequest.prototype._onFinish = function () { if (self._mode === 'text' && 'overrideMimeType' in xhr) xhr.overrideMimeType('text/plain; charset=x-user-defined') - foreach(keys(headersObj), function (name) { + Object.keys(headersObj).forEach(function (name) { xhr.setRequestHeader(headersObj[name].name, headersObj[name].value) }) diff --git a/lib/response.js b/lib/response.js index 378291a..5483d91 100644 --- a/lib/response.js +++ b/lib/response.js @@ -1,5 +1,4 @@ var capability = require('./capability') -var foreach = require('foreach') var inherits = require('inherits') var stream = require('stream') @@ -64,7 +63,7 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) { self.statusCode = xhr.status self.statusMessage = xhr.statusText var headers = xhr.getAllResponseHeaders().split(/\r?\n/) - foreach(headers, function (header) { + headers.forEach(function (header) { var matches = header.match(/^([^:]+):\s*(.*)/) if (matches) { var key = matches[1].toLowerCase() diff --git a/package.json b/package.json index e21a4db..9c5adfe 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,7 @@ ], "dependencies": { "builtin-status-codes": "^1.0.0", - "foreach": "^2.0.5", - "indexof": "0.0.1", "inherits": "^2.0.1", - "object-keys": "^1.0.4", "xtend": "^4.0.0" }, "devDependencies": { diff --git a/test/browser/headers.js b/test/browser/headers.js index 02e4ae8..99f8bf8 100644 --- a/test/browser/headers.js +++ b/test/browser/headers.js @@ -1,20 +1,10 @@ var Buffer = require('buffer').Buffer var fs = require('fs') -var keys = require('object-keys') var test = require('tape') var UAParser = require('ua-parser-js') var http = require('../..') -function indexOf (arr, item) { - var len = arr.length - for (var i = 0; i < len; i++) { - if (arr[i] === item) - return i - } - return -1 -} - test('headers', function (t) { http.get({ path: '/testHeaders?Response-Header=bar&Response-Header-2=BAR2', @@ -29,10 +19,10 @@ test('headers', function (t) { if (lowerKey.indexOf('test-') === 0) rawHeaders.push(lowerKey, res.rawHeaders[i + 1]) } - var header1Pos = indexOf(rawHeaders, 'test-response-header') + var header1Pos = rawHeaders.indexOf('test-response-header') t.ok(header1Pos >= 0, 'raw response header 1 present') t.equal(rawHeaders[header1Pos + 1], 'bar', 'raw response header value 1') - var header2Pos = indexOf(rawHeaders, 'test-response-header-2') + var header2Pos = rawHeaders.indexOf('test-response-header-2') t.ok(header2Pos >= 0, 'raw response header 2 present') t.equal(rawHeaders[header2Pos + 1], 'BAR2', 'raw response header value 2') t.equal(rawHeaders.length, 4, 'correct number of raw headers') @@ -46,7 +36,7 @@ test('headers', function (t) { var body = JSON.parse(Buffer.concat(buffers).toString()) t.equal(body['test-request-header'], 'foo', 'request header 1') t.equal(body['test-request-header-2'], 'FOO2', 'request header 2') - t.equal(keys(body).length, 2, 'correct number of request headers') + t.equal(Object.keys(body).length, 2, 'correct number of request headers') t.end() }) diff --git a/test/server/static/ie8-polyfill.js b/test/server/static/ie8-polyfill.js new file mode 120000 index 0000000..96ad5d9 --- /dev/null +++ b/test/server/static/ie8-polyfill.js @@ -0,0 +1 @@ +../../../ie8-polyfill.js \ No newline at end of file diff --git a/test/server/static/polyfill.js b/test/server/static/test-polyfill.js similarity index 77% rename from test/server/static/polyfill.js rename to test/server/static/test-polyfill.js index da6fbe0..f6a1a9d 100644 --- a/test/server/static/polyfill.js +++ b/test/server/static/test-polyfill.js @@ -1,4 +1,3 @@ -// TODO: send a PR to url to remove .trim() so this isn't necessary if (!String.prototype.trim) { (function() { // Make sure we trim BOM and NBSP