xhr-shaper
Advanced tools
Comparing version 1.0.0 to 1.1.0
26
index.js
@@ -5,26 +5,16 @@ 'use strict'; | ||
var objectMirrors = require('./src/object-mirrors'); | ||
var XHRShaper = require('./src/shaper'); | ||
var XMLHttpRequestShim = require('./src/shim'); | ||
// Export all our stuff via module if we are one | ||
if (typeof module !== 'undefined') { | ||
module.exports = { | ||
BaseXHR: BaseXHR, | ||
XMLHttpRequest: XMLHttpRequestShim, | ||
useGlobal: useGlobal, | ||
objectMirrors: objectMirrors | ||
}; | ||
} | ||
function useGlobal() { | ||
// Shim window/global XHR | ||
var global = window || global; | ||
if (typeof global !== 'undefined') { | ||
// Overload native window constructor | ||
global.XMLHttpRequest = XMLHttpRequestShim; | ||
} | ||
// Overload native window constructor | ||
global.XMLHttpRequest = XMLHttpRequestShim; | ||
} | ||
module.exports = { | ||
BaseXHR: BaseXHR, | ||
XMLHttpRequest: XMLHttpRequestShim, | ||
useGlobal: useGlobal, | ||
objectMirrors: objectMirrors | ||
}; |
{ | ||
"name": "xhr-shaper", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Shapes your XHR requests to a max emulated bandwidth and latency, randomizes frequency of progress events", | ||
@@ -10,3 +10,4 @@ "main": "index.js", | ||
"build": "./node_modules/.bin/webpack --entry ./index --output-file dist/build.js --output-library XHRShaper", | ||
"dev": "./node_modules/.bin/webpack-dev-server --entry ./index --output-file ./index.js --output-library XHRShaper" | ||
"dev": "./node_modules/.bin/webpack-dev-server --entry ./index --output-file ./index.js --output-library XHRShaper", | ||
"lint": "./node_modules/.bin/eslint ." | ||
}, | ||
@@ -27,2 +28,4 @@ "keywords": [ | ||
"devDependencies": { | ||
"eslint": "^3.1.1", | ||
"eslint-config-streamroot": "^1.0.3-beta", | ||
"webpack": "1.13.1", | ||
@@ -29,0 +32,0 @@ "webpack-dev-server": "1.14.1" |
@@ -5,38 +5,7 @@ 'use strict'; | ||
var BaseXHR = function(xhrImpl, onreadystatechange, onprogress, onloadend) { | ||
var BaseXHR = function(xhrImpl) { | ||
var instance = {}; | ||
var xhr = xhrImpl || {}; | ||
var instance = {}; | ||
var xhr = xhrImpl || {}; | ||
var _onreadystatechange = onreadystatechange || function() {}; | ||
var _onprogress = onprogress || function() {}; | ||
var _onloadend = onloadend || function() {}; | ||
Object.defineProperty(instance, "onreadystatechange", { | ||
get: function() { | ||
return xhr.onreadystatechange; | ||
}, | ||
set: function(handler) { | ||
_onreadystatechange(handler); | ||
} | ||
}); | ||
Object.defineProperty(instance, "onprogress", { | ||
get: function() { | ||
return xhr.onprogress; | ||
}, | ||
set: function(handler) { | ||
_onprogress(handler); | ||
} | ||
}); | ||
Object.defineProperty(instance, "onloadend", { | ||
get: function() { | ||
return xhr.onloadend; | ||
}, | ||
set: function(handler) { | ||
_onloadend(handler); | ||
} | ||
}); | ||
objectMirrors.mirrorRwProp(instance, xhr, "responseType"); | ||
@@ -63,2 +32,16 @@ objectMirrors.mirrorRwProp(instance, xhr, "timeout"); | ||
// EventTarget iface | ||
objectMirrors.mirrorFunc(instance, xhr, "addRemoveEventListener"); | ||
objectMirrors.mirrorFunc(instance, xhr, "removeRemoveEventListener"); | ||
// All events as in https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest | ||
objectMirrors.mirrorRwProp(instance, xhr, "onload"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onloadstart"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onloadend"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onabort"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onerror"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onprogress"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "ontimeout"); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onreadystatechange"); | ||
return instance; | ||
@@ -65,0 +48,0 @@ }; |
@@ -6,26 +6,47 @@ 'use strict'; | ||
function checkProperty(source, prop) { | ||
if (typeof source[prop] === 'undefined') { | ||
throw new Error(ERROR_SOURCE_PROPERTY_NOT_IMPLEMENTED); | ||
} | ||
if (typeof source[prop] === 'undefined') { | ||
throw new Error(ERROR_SOURCE_PROPERTY_NOT_IMPLEMENTED + ': ' + prop); | ||
} | ||
} | ||
function mirrorReadOnlyProp(target, source, prop) { | ||
function readProperty(target, source, prop, getterHook) { | ||
if (getterHook) { | ||
var result = getterHook(target, source, prop); | ||
if (result.override) { | ||
return result.value; | ||
} | ||
} | ||
checkProperty(source, prop); | ||
return source[prop]; | ||
} | ||
function writeProperty(val, target, source, prop, setterHook) { | ||
if (setterHook) { | ||
var result = setterHook(val, target, source, prop); | ||
if (result.override) { | ||
return; | ||
} | ||
} | ||
checkProperty(source, prop); | ||
source[prop] = val; | ||
} | ||
function mirrorReadOnlyProp(target, source, prop, getterHook) { | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
checkProperty(source, prop); | ||
return source[prop]; | ||
} | ||
return readProperty(target, source, prop, getterHook); | ||
}, | ||
configurable: true | ||
}); | ||
} | ||
function mirrorRwProp(target, source, prop) { | ||
function mirrorRwProp(target, source, prop, setterHook, getterHook) { | ||
Object.defineProperty(target, prop, { | ||
get: function() { | ||
checkProperty(source, prop); | ||
return source[prop]; | ||
return readProperty(target, source, prop, getterHook); | ||
}, | ||
set: function(val) { | ||
checkProperty(source, prop); | ||
source[prop] = val; | ||
} | ||
return writeProperty(val, target, source, prop, setterHook); | ||
}, | ||
configurable: true | ||
}); | ||
@@ -35,19 +56,12 @@ } | ||
function mirrorFunc(target, source, func) { | ||
if (typeof source[func] === 'undefined'){ | ||
target[func] = function() { | ||
throw new Error(ERROR_SOURCE_FUNCTION_NOT_IMPLEMENTED); | ||
} | ||
return; | ||
} | ||
target[func] = function() { | ||
checkProperty(source, func); | ||
source[func].apply(source, arguments); | ||
} | ||
checkProperty(source, func); | ||
return source[func].apply(source, arguments); | ||
}; | ||
} | ||
module.exports = { | ||
mirrorFunc: mirrorFunc, | ||
mirrorRwProp: mirrorRwProp, | ||
mirrorReadOnlyProp: mirrorReadOnlyProp | ||
mirrorFunc: mirrorFunc, | ||
mirrorRwProp: mirrorRwProp, | ||
mirrorReadOnlyProp: mirrorReadOnlyProp | ||
}; |
@@ -11,3 +11,3 @@ var XHRShaper = function() { | ||
}, | ||
set: function(val) { _maxBandwidth = val } | ||
set: function(val) { _maxBandwidth = val; } | ||
}); | ||
@@ -18,3 +18,3 @@ Object.defineProperty(this, "minLatency", { | ||
}, | ||
set: function(val) { _minLatency = val } | ||
set: function(val) { _minLatency = val; } | ||
}); | ||
@@ -25,3 +25,3 @@ Object.defineProperty(this, "minProgressEvents", { | ||
}, | ||
set: function(val) { _minProgressEvents = val } | ||
set: function(val) { _minProgressEvents = val; } | ||
}); | ||
@@ -32,3 +32,3 @@ Object.defineProperty(this, "randomness", { | ||
}, | ||
set: function(val) { _randomness = val } | ||
set: function(val) { _randomness = val; } | ||
}); | ||
@@ -35,0 +35,0 @@ }; |
105
src/shim.js
@@ -5,2 +5,3 @@ // This will shim the XHR object in your window and add some custom functionnality on top in the Shaper object | ||
var XHRShaper = require('./shaper'); | ||
var objectMirrors = require('./object-mirrors'); | ||
@@ -39,48 +40,48 @@ var WindowXHR = window.XMLHttpRequest; | ||
switch (xhr.readyState) { | ||
case 0: // UNSENT | ||
triggerStateChange(event); | ||
break; | ||
case 1: // OPENED | ||
openedTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 2: // HEADERS_RECEIVE | ||
headersTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 3: // LOADING | ||
loadingTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 4: // DONE | ||
var delay1 = 0, delay2 = 0; | ||
doneTs = Date.now(); | ||
var latency = doneTs - openedTs; | ||
if (latency < shaper.minLatency) { | ||
delay1 = shaper.minLatency - latency; | ||
} | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay2 = (currentBitrateKpbs / shaper.maxBandwidth) * latency - latency; | ||
} | ||
if (delay1 || delay2) { | ||
setTimeout(function() { | ||
case 0: // UNSENT | ||
triggerStateChange(event); | ||
break; | ||
case 1: // OPENED | ||
openedTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 2: // HEADERS_RECEIVE | ||
headersTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 3: // LOADING | ||
loadingTs = Date.now(); | ||
triggerStateChange(event); | ||
break; | ||
case 4: // DONE | ||
var delay1 = 0, delay2 = 0; | ||
doneTs = Date.now(); | ||
var latency = doneTs - openedTs; | ||
if (latency < shaper.minLatency) { | ||
delay1 = shaper.minLatency - latency; | ||
} | ||
if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
delay2 = (currentBitrateKpbs / shaper.maxBandwidth) * latency - latency; | ||
} | ||
if (delay1 || delay2) { | ||
setTimeout(function() { | ||
if (loaded === total && !lastProgressEvent) { | ||
clearTimeout(progressTimer); | ||
_onprogress(progressEvents[progressEvents.length-1]); | ||
} | ||
if (loaded === total && !lastProgressEvent) { | ||
clearTimeout(progressTimer); | ||
_onprogress(progressEvents[progressEvents.length - 1]); | ||
} | ||
triggerStateChange(event); | ||
triggerStateChange(event); | ||
if (loadEndEvent && _onloadend) { | ||
_onloadend(loadEndEvent); | ||
} | ||
if (loadEndEvent && _onloadend) { | ||
_onloadend(loadEndEvent); | ||
} | ||
}, Math.max(delay1, delay2)); | ||
break; | ||
} | ||
}, Math.max(delay1, delay2)); | ||
break; | ||
} | ||
triggerStateChange(event); | ||
triggerStateChange(event); | ||
break; | ||
break; | ||
} | ||
@@ -111,3 +112,3 @@ | ||
//console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps'); | ||
// console.log('current bitrate: ' + Math.round(currentBitrateKpbs) + ' kbps'); | ||
@@ -117,3 +118,3 @@ if (currentBitrateKpbs > shaper.maxBandwidth) { | ||
progressEvents.push(event); | ||
//console.log('delaying progress event by ' + Math.round(delay) + ' ms'); | ||
// console.log('delaying progress event by ' + Math.round(delay) + ' ms'); | ||
progressTimer = setTimeout(function() { | ||
@@ -128,9 +129,17 @@ triggerProgress(event); | ||
var instance = new BaseXHR(xhr, function(handler) { | ||
_onreadystatechange = handler; | ||
}, function(handler) { | ||
_onprogress = handler; | ||
}, function(handler) { | ||
_onloadend = handler; | ||
var instance = new BaseXHR(xhr); | ||
// we need to override these with a custom setter hook | ||
objectMirrors.mirrorRwProp(instance, xhr, "onreadystatechange", function(val) { | ||
_onreadystatechange = val; | ||
return {override: true}; | ||
}); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onprogress", function(val) { | ||
_onprogress = val; | ||
return {override: true}; | ||
}); | ||
objectMirrors.mirrorRwProp(instance, xhr, "onloadend", function(val) { | ||
_onloadend = val; | ||
return {override: true}; | ||
}); | ||
@@ -137,0 +146,0 @@ instance.shaper = shaper; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
16183
11
0
4