Advertisement
Guest User

https://fast.com/app-8f1bee.js

a guest
May 30th, 2019
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ! function() {
  2.     var require = function(file, cwd) {
  3.         var resolved = require.resolve(file, cwd || "/"),
  4.             mod = require.modules[resolved];
  5.         if (!mod) throw new Error("Failed to resolve module " + file + ", tried " + resolved);
  6.         var cached = require.cache[resolved],
  7.             res = cached ? cached.exports : mod();
  8.         return res
  9.     };
  10.     require.paths = [], require.modules = {}, require.cache = {}, require.extensions = [".js", ".coffee", ".json"], require._core = {
  11.             assert: !0,
  12.             events: !0,
  13.             fs: !0,
  14.             path: !0,
  15.             vm: !0
  16.         }, require.resolve = function() {
  17.             return function(x, cwd) {
  18.                 function loadAsFileSync(x) {
  19.                     if (x = path.normalize(x), require.modules[x]) return x;
  20.                     for (var i = 0; i < require.extensions.length; i++) {
  21.                         var ext = require.extensions[i];
  22.                         if (require.modules[x + ext]) return x + ext
  23.                     }
  24.                 }
  25.  
  26.                 function loadAsDirectorySync(x) {
  27.                     x = x.replace(/\/+$/, "");
  28.                     var pkgfile = path.normalize(x + "/package.json");
  29.                     if (require.modules[pkgfile]) {
  30.                         var pkg = require.modules[pkgfile](),
  31.                             b = pkg.browserify;
  32.                         if ("object" == typeof b && b.main) {
  33.                             var m = loadAsFileSync(path.resolve(x, b.main));
  34.                             if (m) return m
  35.                         } else if ("string" == typeof b) {
  36.                             var m = loadAsFileSync(path.resolve(x, b));
  37.                             if (m) return m
  38.                         } else if (pkg.main) {
  39.                             var m = loadAsFileSync(path.resolve(x, pkg.main));
  40.                             if (m) return m
  41.                         }
  42.                     }
  43.                     return loadAsFileSync(x + "/index")
  44.                 }
  45.  
  46.                 function loadNodeModulesSync(x, start) {
  47.                     for (var dirs = nodeModulesPathsSync(start), i = 0; i < dirs.length; i++) {
  48.                         var dir = dirs[i],
  49.                             m = loadAsFileSync(dir + "/" + x);
  50.                         if (m) return m;
  51.                         var n = loadAsDirectorySync(dir + "/" + x);
  52.                         if (n) return n
  53.                     }
  54.                     var m = loadAsFileSync(x);
  55.                     return m ? m : void 0
  56.                 }
  57.  
  58.                 function nodeModulesPathsSync(start) {
  59.                     var parts;
  60.                     parts = "/" === start ? [""] : path.normalize(start).split("/");
  61.                     for (var dirs = [], i = parts.length - 1; i >= 0; i--)
  62.                         if ("node_modules" !== parts[i]) {
  63.                             var dir = parts.slice(0, i + 1).join("/") + "/node_modules";
  64.                             dirs.push(dir)
  65.                         } return dirs
  66.                 }
  67.                 if (cwd || (cwd = "/"), require._core[x]) return x;
  68.                 var path = require.modules.path();
  69.                 cwd = path.resolve("/", cwd);
  70.                 var y = cwd || "/";
  71.                 if (x.match(/^(?:\.\.?\/|\/)/)) {
  72.                     var m = loadAsFileSync(path.resolve(y, x)) || loadAsDirectorySync(path.resolve(y, x));
  73.                     if (m) return m
  74.                 }
  75.                 var n = loadNodeModulesSync(x, y);
  76.                 if (n) return n;
  77.                 throw new Error("Cannot find module '" + x + "'")
  78.             }
  79.         }(), require.alias = function(from, to) {
  80.             var path = require.modules.path(),
  81.                 res = null;
  82.             try {
  83.                 res = require.resolve(from + "/package.json", "/")
  84.             } catch (err) {
  85.                 res = require.resolve(from, "/")
  86.             }
  87.             for (var basedir = path.dirname(res), keys = (Object.keys || function(obj) {
  88.                     var res = [];
  89.                     for (var key in obj) res.push(key);
  90.                     return res
  91.                 })(require.modules), i = 0; i < keys.length; i++) {
  92.                 var key = keys[i];
  93.                 if (key.slice(0, basedir.length + 1) === basedir + "/") {
  94.                     var f = key.slice(basedir.length);
  95.                     require.modules[to + f] = require.modules[basedir + f]
  96.                 } else key === basedir && (require.modules[to] = require.modules[basedir])
  97.             }
  98.         },
  99.         function() {
  100.             var process = {},
  101.                 global = "undefined" != typeof window ? window : {},
  102.                 definedProcess = !1;
  103.             require.define = function(filename, fn) {
  104.                 !definedProcess && require.modules.__browserify_process && (process = require.modules.__browserify_process(), definedProcess = !0);
  105.                 var dirname = require._core[filename] ? "" : require.modules.path().dirname(filename),
  106.                     require_ = function(file) {
  107.                         var requiredModule = require(file, dirname),
  108.                             cached = require.cache[require.resolve(file, dirname)];
  109.                         return cached && null === cached.parent && (cached.parent = module_), requiredModule
  110.                     };
  111.                 require_.resolve = function(name) {
  112.                     return require.resolve(name, dirname)
  113.                 }, require_.modules = require.modules, require_.define = require.define, require_.cache = require.cache;
  114.                 var module_ = {
  115.                     id: filename,
  116.                     filename: filename,
  117.                     exports: {},
  118.                     loaded: !1,
  119.                     parent: null
  120.                 };
  121.                 require.modules[filename] = function() {
  122.                     return require.cache[filename] = module_, fn.call(module_.exports, require_, module_, module_.exports, dirname, filename, process, global), module_.loaded = !0, module_.exports
  123.                 }
  124.             }
  125.         }(), require.define("path", function(require, module, exports, __dirname, __filename, process) {
  126.             function filter(xs, fn) {
  127.                 for (var res = [], i = 0; i < xs.length; i++) fn(xs[i], i, xs) && res.push(xs[i]);
  128.                 return res
  129.             }
  130.  
  131.             function normalizeArray(parts, allowAboveRoot) {
  132.                 for (var up = 0, i = parts.length; i >= 0; i--) {
  133.                     var last = parts[i];
  134.                     "." == last ? parts.splice(i, 1) : ".." === last ? (parts.splice(i, 1), up++) : up && (parts.splice(i, 1), up--)
  135.                 }
  136.                 if (allowAboveRoot)
  137.                     for (; up--; up) parts.unshift("..");
  138.                 return parts
  139.             }
  140.             var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
  141.             exports.resolve = function() {
  142.                 for (var resolvedPath = "", resolvedAbsolute = !1, i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
  143.                     var path = i >= 0 ? arguments[i] : process.cwd();
  144.                     "string" == typeof path && path && (resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = "/" === path.charAt(0))
  145.                 }
  146.                 return resolvedPath = normalizeArray(filter(resolvedPath.split("/"), function(p) {
  147.                     return !!p
  148.                 }), !resolvedAbsolute).join("/"), (resolvedAbsolute ? "/" : "") + resolvedPath || "."
  149.             }, exports.normalize = function(path) {
  150.                 var isAbsolute = "/" === path.charAt(0),
  151.                     trailingSlash = "/" === path.slice(-1);
  152.                 return path = normalizeArray(filter(path.split("/"), function(p) {
  153.                     return !!p
  154.                 }), !isAbsolute).join("/"), path || isAbsolute || (path = "."), path && trailingSlash && (path += "/"), (isAbsolute ? "/" : "") + path
  155.             }, exports.join = function() {
  156.                 var paths = Array.prototype.slice.call(arguments, 0);
  157.                 return exports.normalize(filter(paths, function(p) {
  158.                     return p && "string" == typeof p
  159.                 }).join("/"))
  160.             }, exports.dirname = function(path) {
  161.                 var dir = splitPathRe.exec(path)[1] || "",
  162.                     isWindows = !1;
  163.                 return dir ? 1 === dir.length || isWindows && dir.length <= 3 && ":" === dir.charAt(1) ? dir : dir.substring(0, dir.length - 1) : "."
  164.             }, exports.basename = function(path, ext) {
  165.                 var f = splitPathRe.exec(path)[2] || "";
  166.                 return ext && f.substr(-1 * ext.length) === ext && (f = f.substr(0, f.length - ext.length)), f
  167.             }, exports.extname = function(path) {
  168.                 return splitPathRe.exec(path)[3] || ""
  169.             }
  170.         }), require.define("__browserify_process", function(require, module, exports, __dirname, __filename, process) {
  171.             var process = module.exports = {};
  172.             process.nextTick = function() {
  173.                     var canSetImmediate = "undefined" != typeof window && window.setImmediate,
  174.                         canPost = "undefined" != typeof window && window.postMessage && window.addEventListener;
  175.                     if (canSetImmediate) return function(f) {
  176.                         return window.setImmediate(f)
  177.                     };
  178.                     if (canPost) {
  179.                         var queue = [];
  180.                         return window.addEventListener("message", function(ev) {
  181.                                 if (ev.source === window && "browserify-tick" === ev.data && (ev.stopPropagation(), queue.length > 0)) {
  182.                                     var fn = queue.shift();
  183.                                     fn()
  184.                                 }
  185.                             }, !0),
  186.                             function(fn) {
  187.                                 queue.push(fn), window.postMessage("browserify-tick", "*")
  188.                             }
  189.                     }
  190.                     return function(fn) {
  191.                         setTimeout(fn, 0)
  192.                     }
  193.                 }(), process.title = "browser", process.browser = !0, process.env = {}, process.argv = [], process.binding = function(name) {
  194.                     if ("evals" === name) return require("vm");
  195.                     throw new Error("No such module. (Possibly not yet loaded)")
  196.                 },
  197.                 function() {
  198.                     var path, cwd = "/";
  199.                     process.cwd = function() {
  200.                         return cwd
  201.                     }, process.chdir = function(dir) {
  202.                         path || (path = require("path")), cwd = path.resolve(dir, cwd)
  203.                     }
  204.                 }()
  205.         }), require.define("/app/aggregator/stableMovingAverage.js", function(require, module) {
  206.             module.exports = function(windowSize, snapshotResetThreshold) {
  207.                 function reset() {
  208.                     startInd = 0, curSpeed = 0, lastLen = 0, bytes = 0, times = 0, fixedStartInd = !1
  209.                 }
  210.  
  211.                 function movingAvg(snapshots) {
  212.                     snapshots.length < snapshotResetThreshold && reset(), lastLen = snapshots.length;
  213.                     var snapshotInd, speed;
  214.                     if (!fixedStartInd) {
  215.                         var sumBytes = 0,
  216.                             sumTime = 0,
  217.                             start = snapshots.length - 1,
  218.                             end = Math.max(0, snapshots.length - windowSize);
  219.                         for (snapshotInd = start; snapshotInd >= end; --snapshotInd) snapshot = snapshots[snapshotInd], sumBytes += snapshot.bytes, sumTime += snapshot.time;
  220.                         speed = sumTime > 0 ? sumBytes / sumTime : 0, speed >= curSpeed ? (startInd = start + 1, curSpeed = speed, bytes = sumBytes, times = sumTime) : fixedStartInd = !0
  221.                     }
  222.                     for (snapshotInd = startInd; snapshotInd < snapshots.length; ++snapshotInd) snapshot = snapshots[snapshotInd], bytes += snapshot.bytes, times += snapshot.time;
  223.                     return startInd = snapshots.length, times > 0 ? 1e3 * bytes * 8 / times : 0
  224.                 }
  225.                 var startInd, curSpeed, fixedStartInd, lastLen, bytes, times;
  226.                 return snapshotResetThreshold = snapshotResetThreshold || 5, reset(), movingAvg
  227.             }
  228.         }), require.define("/app/stopper/stableDeltaMeasurementsStopper.js", function(require, module) {
  229.             module.exports = function(config) {
  230.                 function lastWindowMaxInd(measurements, windowSize) {
  231.                     var measurement, curMaxSpeed = 0,
  232.                         curMaxInd = 0,
  233.                         measurementInd = 0,
  234.                         numMeasurements = measurements.length,
  235.                         firstMeasurementInd = Math.max(0, numMeasurements - windowSize);
  236.                     for (measurementInd = numMeasurements - 1; measurementInd >= firstMeasurementInd; --measurementInd) measurement = measurements[measurementInd], measurement.speed >= curMaxSpeed && (curMaxSpeed = measurement.speed, curMaxInd = measurementInd);
  237.                     return curMaxInd
  238.                 }
  239.  
  240.                 function maxDelta(value, measurements) {
  241.                     var measurementInd, delta, maxDelta = 0;
  242.                     for (measurementInd = 0; measurementInd < measurements.length; ++measurementInd) delta = 100 * Math.abs(measurements[measurementInd].speed - value) / value, delta > maxDelta && (maxDelta = delta);
  243.                     return maxDelta
  244.                 }
  245.  
  246.                 function isCompleted(metrics) {
  247.                     var maxInd, delta, testTime = void 0 !== metrics.testTime ? metrics.testTime : timer() - startTime,
  248.                         measurements = metrics.progressMeasurements,
  249.                         afterCompleteDuration = metrics.afterCompleteDuration,
  250.                         numMeasurements = measurements.length,
  251.                         curSpeed = metrics.speed;
  252.                     return void 0 === afterCompleteDuration ? testTime >= maxDuration ? !0 : minStableMeasurements > numMeasurements ? !1 : (maxInd = lastWindowMaxInd(measurements, Math.ceil(minStableMeasurements / 2)), numMeasurements - maxInd < Math.ceil(minStableMeasurements / 2) ? !1 : (maxInd = Math.max(0, numMeasurements - minStableMeasurements), minStableMeasurements > numMeasurements - maxInd ? !1 : (delta = maxDelta(curSpeed, measurements.slice(numMeasurements - minStableMeasurements, numMeasurements)), delta > stabilityDelta ? !1 : testTime >= minDuration))) : testTime >= afterCompleteDuration
  253.                 }
  254.                 var minDuration, maxDuration, stabilityDelta, minStableMeasurements, timer, startTime;
  255.                 return minDuration = config.minDuration || 5, maxDuration = config.maxDuration || 60, stabilityDelta = config.stabilityDelta || 5, minStableMeasurements = config.minStableMeasurements || 5, timer = config.timer || require("../timer").time, startTime = timer(), isCompleted
  256.             }
  257.         }), require.define("/app/timer.js", function(require, module) {
  258.             module.exports = function() {
  259.                 var timer;
  260.                 return this.window && window.performance && (timer = window.performance.now || window.performance.webkitNow, timer && (timer = timer.bind(window.performance))), timer || (timer = function() {
  261.                     return (new Date).getTime()
  262.                 }), {
  263.                     time: timer
  264.                 }
  265.             }()
  266.         }), require.define("/app/tester.js", function(require, module) {
  267.             var tester = function() {
  268.                 var MAX_PAYLOAD_BYTES = 26214400,
  269.                     MIN_REQUEST_SIZE = 1024,
  270.                     STABLE_MEASUREMENTS_THRESHOLD = 10,
  271.                     TEST_TYPE = {
  272.                         DOWNLOAD_TEST: "download",
  273.                         UPLOAD_TEST: "upload",
  274.                         LATENCY_TEST: "latency"
  275.                     },
  276.                     utils = require("./utils"),
  277.                     errors = require("./error"),
  278.                     events = require("./event"),
  279.                     testConfig = require("./config"),
  280.                     factory = (require("circular-buffer"), function(requester, config) {
  281.                         function validateEvent(eventName, callback) {
  282.                             if (void 0 === events.schema[eventName]) throw new errors.TesterEventError("no event with name " + eventName);
  283.                             if (!utils.isFunction(callback)) throw new errors.TesterEventError("function callback is expected for event " + eventName)
  284.                         }
  285.  
  286.                         function isActive() {
  287.                             return testIsRunning
  288.                         }
  289.  
  290.                         function stop() {
  291.                             var reqId;
  292.                             testIsRunning = !1, urlGetter.stop(), reportTimeout && clearTimeout(reportTimeout), retryTimeout && clearTimeout(retryTimeout);
  293.                             for (reqId in requesters) requesters[reqId].stop(), pingers[reqId].stop()
  294.                         }
  295.  
  296.                         function reset() {
  297.                             isActive() && stop(), startTime = null, downloadStartTime = null, testAttempt = 1, workerMeasurements = {}, latencyMeasurements = {}, requesters = {}, pingers = {}, progressMeasurements = [], requestTimeMs = config.startRangeRequestTimeMs, activeWorkersCount = 0, snapshot.reset(), window && window.performance && window.performance.clearResourceTimings && window.performance.clearResourceTimings()
  298.                         }
  299.  
  300.                         function computeLoadedLatency() {
  301.                             var reqId, value, latencies = [],
  302.                                 count = 0;
  303.                             for (reqId in latencyMeasurements) latencyMeasurements[reqId].length > 4 && (value = utils.percentile(latencyMeasurements[reqId], .75), latencies.push(value)), count += latencyMeasurements[reqId].length;
  304.                             if (0 === latencies.length)
  305.                                 for (reqId in latencyMeasurements) value = utils.percentile(latencyMeasurements[reqId], .75), latencies.push(value);
  306.                             return {
  307.                                 value: Math.min.apply(null, latencies),
  308.                                 count: count
  309.                             }
  310.                         }
  311.  
  312.                         function computeUnloadedLatency() {
  313.                             var reqId, value, latencies = [],
  314.                                 count = 0;
  315.                             for (reqId in latencyMeasurements) value = utils.percentile(latencyMeasurements[reqId], .1), latencies.push(value), count += latencyMeasurements[reqId].length;
  316.                             return {
  317.                                 value: Math.min.apply(null, latencies),
  318.                                 count: count
  319.                             }
  320.                         }
  321.  
  322.                         function reportEndEvents(data) {
  323.                             var eventData, result = data.result,
  324.                                 resultValue = -1;
  325.                             eventData = {
  326.                                 start: startTime,
  327.                                 downloadStart: downloadStartTime,
  328.                                 attempts: testAttempt,
  329.                                 numberOfTests: stats[currentTestType].tests,
  330.                                 bytes: stats[currentTestType].bytes[stats[currentTestType].bytes.length - 1]
  331.                             }, "success" === result ? (eventData.stable = data.stable, currentTestType === TEST_TYPE.LATENCY_TEST ? (resultValue = data.value, eventData.count = data.count, eventData.value = resultValue) : (resultValue = data.speed, eventData.speed = resultValue, eventData.latency = computeLoadedLatency()), event(events.events.ATTEMPT_SUCCESS, eventData), event(events.events.SUCCESS, eventData), eventData.result = result) : "stop" === result ? (eventData.reason = data.reason || "stopped", event(events.events.STOP, eventData), eventData.result = result) : (eventData.error = data.error, eventData.attempts = testAttempt - 1, event(events.events.ATTEMPT_FAIL, eventData), event(events.events.FAIL, eventData), eventData.result = result), event(events.events.ATTEMPT_END, eventData), event(events.events.END, eventData), stats[currentTestType].results.push(resultValue)
  332.                         }
  333.  
  334.                         function startTestWorker(workerId, url, attempt) {
  335.                             function sendLatencyRequest(onLatencyComplete) {
  336.                                 var rangeUrl = url.replace("/range/", "/range/0-0");
  337.                                 event(events.events.LATENCY_START, {
  338.                                     oca: oca
  339.                                 }), pinger.upload(rangeUrl, 0, utils.dummy, onLatencyComplete)
  340.                             }
  341.  
  342.                             function sendDownloadRequest(onDownloadProgress, onDownloadComplete) {
  343.                                 var rangeUrl;
  344.                                 3 > testAttempt || config.maxBytesInFlight - 1 < activeWorkersCount * MAX_PAYLOAD_BYTES || !req.supportsProgress() ? rangeUrl = url.replace("/range/", "/range/0-" + requestSize) : (rangeUrl = url.replace("/range/", ""), requestSize = MAX_PAYLOAD_BYTES), event(events.events.CONNECTION_START, {
  345.                                     oca: oca,
  346.                                     range: requestSize,
  347.                                     requestUrl: rangeUrl
  348.                                 });
  349.                                 try {
  350.                                     req.start(rangeUrl, onDownloadProgress, onDownloadComplete, !1, url)
  351.                                 } catch (e) {
  352.                                     onDownloadComplete({
  353.                                         success: !1,
  354.                                         response: {
  355.                                             type: "Requester error",
  356.                                             message: e
  357.                                         }
  358.                                     })
  359.                                 }
  360.                             }
  361.  
  362.                             function sendUploadRequest(onUploadProgress, onUploadComplete) {
  363.                                 var rangeUrl;
  364.                                 3 > testAttempt || config.maxBytesInFlight - 1 < activeWorkersCount * MAX_PAYLOAD_BYTES || !req.supportsProgress() ? rangeUrl = url.replace("/range/", "/range/0-" + requestSize) : (rangeUrl = url.replace("/range/", ""), requestSize = MAX_PAYLOAD_BYTES), event(events.events.CONNECTION_START, {
  365.                                     oca: oca,
  366.                                     range: requestSize,
  367.                                     requestUrl: rangeUrl
  368.                                 });
  369.                                 try {
  370.                                     req.upload(rangeUrl, requestSize, onUploadProgress, onUploadComplete)
  371.                                 } catch (e) {
  372.                                     onUploadComplete({
  373.                                         success: !1,
  374.                                         response: {
  375.                                             type: "Requester error",
  376.                                             message: e
  377.                                         }
  378.                                     })
  379.                                 }
  380.                             }
  381.  
  382.                             function workerIsActive() {
  383.                                 return void 0 !== workerMeasurements[workerId]
  384.                             }
  385.  
  386.                             function onProgress(progress) {
  387.                                 var progressData;
  388.                                 workerIsActive() && isActive() ? progress.success && (progressData = {
  389.                                     bytes: progress.bytes,
  390.                                     time: progress.end - progress.start,
  391.                                     start: progress.start,
  392.                                     end: progress.end
  393.                                 }, void 0 === progressData.bytes && (progressData.bytes = requestSize), stats[currentTestType].bytes[stats[currentTestType].bytes.length - 1] += progressData.bytes, workerMeasurements[workerId].push(progressData), event(events.events.CONNECTION_PROGRESS, {
  394.                                     oca: oca,
  395.                                     bytes: progressData.bytes,
  396.                                     start: progressData.start,
  397.                                     end: progressData.end
  398.                                 })) : req.stop()
  399.                             }
  400.  
  401.                             function onComplete(sendRequest) {
  402.                                 var completeFunc = function(data) {
  403.                                     var requestTime, eventData = {
  404.                                             oca: oca
  405.                                         },
  406.                                         partialSuccess = !1;
  407.                                     if (workerIsActive() && isActive() && "stopped" !== data.reason) {
  408.                                         if (partialSuccess = !data.success && workerMeasurements[workerId].length > 0, !data.success && !partialSuccess) {
  409.                                             var error = new errors.DownloadOcaDataError("Could not download oca data");
  410.                                             return error.url = url, error.response = data.response, eventData.error = error, event(events.events.CONNECTION_FAIL, eventData), eventData.result = "fail", event(events.events.CONNECTION_END, eventData), void test(attempt + 1, error)
  411.                                         }
  412.                                         eventData.bytes = requestSize, eventData.partialSuccess = partialSuccess, event(events.events.CONNECTION_SUCCESS, eventData), eventData.result = "success", req.supportsProgress() ? requestSize = MAX_PAYLOAD_BYTES : (requestTime = data.end - data.start, requestSize = Math.min(Math.floor(requestTimeMs * requestSize / requestTime), 5 * requestSize, MAX_PAYLOAD_BYTES, Math.round(config.maxBytesInFlight / config.connections.max)), requestTimeMs = Math.min(requestTimeMs + config.rangeRequestIncreaseMs, config.maxRangeRequestTimeMs), requestSize = Math.max(requestSize, MIN_REQUEST_SIZE)), event(events.events.CONNECTION_END, eventData), sendRequest(onProgress, completeFunc)
  413.                                     } else req.stop(), eventData.reason = "inactive", eventData.result = "stop", event(events.events.CONNECTION_END, eventData)
  414.                                 };
  415.                                 return completeFunc
  416.                             }
  417.  
  418.                             function onLatencyComplete(data) {
  419.                                 var latency, scheduleDelay, eventData = {
  420.                                     oca: oca
  421.                                 };
  422.                                 if (workerIsActive() && isActive() && "stopped" !== data.reason) {
  423.                                     if (data.success) latency = data.end - data.start, data.timing || (latency /= 2), eventData.latency = latency, eventData.start = data.start, eventData.end = data.end, latencyMeasurements[workerId].push(latency), eventData.timing = data.timing, event(events.events.LATENCY_SUCCESS, eventData), eventData.result = "success";
  424.                                     else {
  425.                                         var error = new errors.DownloadOcaDataError("Could not measure latency to oca " + oca);
  426.                                         error.url = url, error.response = data.response, eventData.error = error, event(events.events.LATENCY_FAIL, eventData), eventData.result = "fail"
  427.                                     }
  428.                                     event(events.events.LATENCY_END, eventData), scheduleDelay = currentTestType !== TEST_TYPE.LATENCY_TEST ? latency ? Math.max(config.latencyMeasurementsFrequencyMs - latency, 0) : config.latencyMeasurementsFrequencyMs : 0, setTimeout(function() {
  429.                                         return sendLatencyRequest(onLatencyComplete)
  430.                                     }, scheduleDelay)
  431.                                 } else pinger.stop(), eventData.reason = "inactive", eventData.result = "stop", event(events.events.LATENCY_END, eventData)
  432.                             }
  433.                             var req = requester(),
  434.                                 pinger = requester(),
  435.                                 requestSize = config.firstRequestBytes,
  436.                                 oca = url.split("/")[2];
  437.                             requesters[workerId] = req, pingers[workerId] = pinger, workerMeasurements[workerId] = [], latencyMeasurements[workerId] = [], req.supportsProgress() && (requestSize = MAX_PAYLOAD_BYTES), config.measureLatency && currentTestType !== TEST_TYPE.LATENCY_TEST && sendLatencyRequest(onLatencyComplete), currentTestType === TEST_TYPE.DOWNLOAD_TEST ? sendDownloadRequest(onProgress, onComplete(sendDownloadRequest)) : currentTestType === TEST_TYPE.UPLOAD_TEST ? sendUploadRequest(onProgress, onComplete(sendUploadRequest)) : currentTestType === TEST_TYPE.LATENCY_TEST && sendLatencyRequest(onLatencyComplete)
  438.                         }
  439.  
  440.                         function reportMetrics(interval) {
  441.                             function testTime() {
  442.                                 var now = timer();
  443.                                 return (now - (downloadStartTime || startTime)) / 1e3
  444.                             }
  445.                             var latestSnapshot, resultData, testComplete, currentSpeed, snapshots, testTimeS, activeSnapshot = !1;
  446.                             if (isActive())
  447.                                 if (currentTestType === TEST_TYPE.LATENCY_TEST) resultData = computeUnloadedLatency(), resultData.stable = !0, testComplete = testTime() > 5 && resultData.count > 50, testComplete ? (stop(), resultData.result = "success", setTimeout(function() {
  448.                                     reportEndEvents(resultData)
  449.                                 }, 10)) : (event(events.events.PROGRESS, resultData), reportTimeout = setTimeout(function() {
  450.                                     reportMetrics(interval)
  451.                                 }, interval));
  452.                                 else {
  453.                                     latestSnapshot = snapshot.compute(workerMeasurements), latestSnapshot && (activeSnapshot = latestSnapshot.end - latestSnapshot.start > 0, event(events.events.SNAPSHOT, {
  454.                                         bytes: latestSnapshot.bytes,
  455.                                         start: latestSnapshot.start,
  456.                                         end: latestSnapshot.end,
  457.                                         time: latestSnapshot.time
  458.                                     })), snapshots = snapshot.all(), testTimeS = testTime(), activeSnapshot && (currentSpeed = speedAggregator(snapshots), testComplete = testTimeS > config.duration.min && stopper({
  459.                                         testTime: testTimeS,
  460.                                         snapshots: snapshots,
  461.                                         progressMeasurements: progressMeasurements,
  462.                                         speed: currentSpeed
  463.                                     })), testTimeS > config.duration.max && (testComplete = !0, currentSpeed = speedAggregator(snapshots)), resultData = {
  464.                                         speed: currentSpeed,
  465.                                         stable: !0,
  466.                                         bytes: stats[currentTestType].bytes[stats[currentTestType].bytes.length - 1],
  467.                                         latency: computeLoadedLatency()
  468.                                     }, testComplete ? (testTimeS > config.duration.max && (resultData.stopperStable = !1, progressMeasurements.length > STABLE_MEASUREMENTS_THRESHOLD && (resultData.stable = !0)), stop(), resultData.result = "success", setTimeout(function() {
  469.                                         reportEndEvents(resultData)
  470.                                     }, 10)) : (activeSnapshot && (progressMeasurements.push({
  471.                                         speed: currentSpeed
  472.                                     }), event(events.events.PROGRESS, {
  473.                                         speed: currentSpeed,
  474.                                         bytes: resultData.bytes,
  475.                                         latency: resultData.latency
  476.                                     })), reportTimeout = setTimeout(function() {
  477.                                         reportMetrics(interval)
  478.                                     }, interval));
  479.                                     var i, desiredWorkers = activeWorkersCount;
  480.                                     if (activeWorkersCount < config.connections.max)
  481.                                         for (currentSpeed > 5e7 ? desiredWorkers = config.connections.max : currentSpeed > 1e7 && 5 > activeWorkersCount ? desiredWorkers = 5 : currentSpeed > 1e6 && 3 > activeWorkersCount && (desiredWorkers = 3), currentSpeed > 5e5 && 2 > activeWorkersCount && (desiredWorkers = 3), i = 0; desiredWorkers - activeWorkersCount > i; ++i) startNewWorker()
  482.                                 }
  483.                         }
  484.  
  485.                         function urlRequestFailure(error) {
  486.                             event(events.events.URL_REQUEST_END, {
  487.                                 error: error,
  488.                                 result: "fail"
  489.                             }), isActive() && test(testAttempt + 1, error)
  490.                         }
  491.  
  492.                         function urlRequestStop() {
  493.                             event(events.events.URL_REQUEST_END, {
  494.                                 result: "stop"
  495.                             })
  496.                         }
  497.  
  498.                         function startNewWorker() {
  499.                             function startForUrl(url) {
  500.                                 var id = uniqueId();
  501.                                 event(events.events.URL_REQUEST_END, {
  502.                                     url: url,
  503.                                     result: "success",
  504.                                     client: urlGetter.clientInfo()
  505.                                 }), downloadStartTime = downloadStartTime || timer(), startTestWorker(id, url, testAttempt)
  506.                             }
  507.  
  508.                             function restartTest(error) {
  509.                                 return urlRequestFailure(error)
  510.                             }
  511.                             activeWorkersCount += 1, event(events.events.URL_REQUEST_START, config.getTestOcasParams), urlGetter.getNext(config.getTestOcasParams, requester, startForUrl, restartTest, urlRequestStop)
  512.                         }
  513.  
  514.                         function test(attempt, error) {
  515.                             function startTest() {
  516.                                 eventData = {
  517.                                     attempt: testAttempt
  518.                                 }, event(events.events.ATTEMPT_START, eventData), startTime = timer();
  519.                                 var i;
  520.                                 if (currentTestType !== TEST_TYPE.LATENCY_TEST)
  521.                                     for (i = 0; i < config.connections.min; i++) startNewWorker();
  522.                                 else
  523.                                     for (i = 0; i < Math.min(5, config.connections.max); i++) startNewWorker();
  524.                                 reportMetrics(config.progressFrequencyMs)
  525.                             }
  526.                             var eventData;
  527.                             return reset(), testAttempt = attempt || 1, testAttempt > config.maxAttempts ? void reportEndEvents({
  528.                                 result: "fail",
  529.                                 error: error
  530.                             }) : (testIsRunning = !0, error && (eventData = {
  531.                                 error: error,
  532.                                 attempt: testAttempt - 1
  533.                             }, event(events.events.ATTEMPT_FAIL, eventData), eventData.result = "fail", event(events.events.ATTEMPT_END, eventData), error.url && urlGetter.reportBadUrl(error.url)), void(testAttempt > 1 ? setTimeout(startTest, Math.min(Math.pow(2, testAttempt), 200)) : startTest()))
  534.                         }
  535.  
  536.                         function event(eventName, data) {
  537.                             var eventInd, genericHandlers, handler, handlers = eventHandlers[eventName];
  538.                             if (data.testType = currentTestType, handlers && handlers.length > 0)
  539.                                 for (eventInd = 0; eventInd < handlers.length; ++eventInd) {
  540.                                     handler = handlers[eventInd];
  541.                                     try {
  542.                                         handlers[eventInd](utils.simpleCopy(data))
  543.                                     } catch (e) {}
  544.                                 }
  545.                             if (genericHandlers = eventHandlers[events.events.ANY], genericHandlers && genericHandlers.length > 0)
  546.                                 for (eventInd = 0; eventInd < genericHandlers.length; ++eventInd) genericHandlers[eventInd]({
  547.                                     event: eventName,
  548.                                     data: utils.simpleCopy(data)
  549.                                 })
  550.                         }
  551.                         var that, currentTestType, reportTimeout, retryTimeout, requestTimeMs, activeWorkersCount, speedAggregator, stopper, timer, testType, testIsRunning = !1,
  552.                             testAttempt = 1,
  553.                             stats = {},
  554.                             startTime = null,
  555.                             downloadStartTime = null,
  556.                             workerMeasurements = {},
  557.                             latencyMeasurements = {},
  558.                             requesters = {},
  559.                             pingers = {},
  560.                             progressMeasurements = [],
  561.                             urlGetter = require("./url_getter")(),
  562.                             uniqueId = utils.uniqueId,
  563.                             snapshot = require("./snapshot"),
  564.                             eventHandlers = {};
  565.                         for (testType in TEST_TYPE) testType = TEST_TYPE[testType], stats[testType] = {}, stats[testType].tests = 0, stats[testType].results = [], stats[testType].bytes = [];
  566.                         if (!requester) throw new errors.TesterArgumentError("requester factory should be provided to the tester");
  567.                         return config = testConfig.get(config), timer = config.timer, speedAggregator = config.aggregator, stopper = config.stopper, that = {
  568.                             download: function() {
  569.                                 currentTestType = TEST_TYPE.DOWNLOAD_TEST, stats[currentTestType].tests += 1, stats[currentTestType].bytes.push(0), urlGetter.reset(), event(events.events.START, {
  570.                                     numberOfTests: stats[currentTestType].tests,
  571.                                     type: currentTestType,
  572.                                     stats: stats
  573.                                 }), test()
  574.                             },
  575.                             upload: function() {
  576.                                 currentTestType = TEST_TYPE.UPLOAD_TEST, stats[currentTestType].tests > 1 && urlGetter.reset(), stats[currentTestType].tests += 1, stats[currentTestType].bytes.push(0), event(events.events.START, {
  577.                                     numberOfTests: stats[currentTestType].tests,
  578.                                     type: currentTestType,
  579.                                     stats: stats
  580.                                 }), test()
  581.                             },
  582.                             latency: function() {
  583.                                 currentTestType = TEST_TYPE.LATENCY_TEST, stats[currentTestType].tests > 1 && urlGetter.reset(), stats[currentTestType].tests += 1, stats[currentTestType].bytes.push(0), event(events.events.START, {
  584.                                     numberOfTests: stats[currentTestType].tests,
  585.                                     type: currentTestType,
  586.                                     stats: stats
  587.                                 }), test()
  588.                             },
  589.                             stop: function() {
  590.                                 stop(), reportEndEvents({
  591.                                     result: "stop",
  592.                                     reason: "user"
  593.                                 })
  594.                             },
  595.                             on: function(eventName, callback) {
  596.                                 validateEvent(eventName, callback);
  597.                                 var handlers = eventHandlers[eventName];
  598.                                 return handlers || (handlers = eventHandlers[eventName] = []), handlers.push(callback), that
  599.                             },
  600.                             off: function(eventName, callback) {
  601.                                 validateEvent(eventName, callback);
  602.                                 var eventInd, event, handlers = eventHandlers[eventName],
  603.                                     outHandlers = [];
  604.                                 if (handlers && handlers.length > 0) {
  605.                                     for (eventInd = 0; eventInd < handlers.length; ++eventInd) event = handlers[eventInd], event !== callback && outHandlers.push(event);
  606.                                     outHandlers.length > 0 && (eventHandlers[eventName] = outHandlers)
  607.                                 }
  608.                                 return that
  609.                             },
  610.                             config: function(attr) {
  611.                                 return attr ? config[attr] : config
  612.                             },
  613.                             setConfig: function(attrs) {
  614.                                 for (var name in attrs) testConfig.validate(name, attrs[name]) && (config[name] = attrs[name]);
  615.                                 return config
  616.                             },
  617.                             isRunning: function() {
  618.                                 return testIsRunning
  619.                             }
  620.                         }
  621.                     });
  622.                 return factory
  623.             };
  624.             module.exports = tester()
  625.         }), require.define("/app/utils.js", function(require, module) {
  626.             var utils = {};
  627.             utils.getXHR = function() {
  628.                 var XMLHttpFactories = ["Msxml2.XMLHTTP.6.0", "Msxml3.XMLHTTP", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.3.0"],
  629.                     xmlhttp = !1;
  630.                 try {
  631.                     if (-1 !== navigator.appVersion.indexOf("MSIE 10")) throw new Error("Error");
  632.                     xmlhttp = new XDomainRequest
  633.                 } catch (e) {
  634.                     try {
  635.                         xmlhttp = new XMLHttpRequest
  636.                     } catch (e) {
  637.                         for (var i = 0; i < XMLHttpFactories.length; i++) try {
  638.                             xmlhttp = new ActiveXObject(XMLHttpFactories[i]);
  639.                             break
  640.                         } catch (e) {}
  641.                     }
  642.                 } finally {
  643.                     return xmlhttp
  644.                 }
  645.             }, utils.uniqueId = function() {
  646.                 var id = 0,
  647.                     getId = function() {
  648.                         var myId = id;
  649.                         return id += 1, myId
  650.                     };
  651.                 return getId
  652.             }(), utils.dummy = function() {}, utils.isFunction = function(obj) {
  653.                 return !!(obj && obj.constructor && obj.call && obj.apply)
  654.             }, utils.simpleCopy = function(obj) {
  655.                 var attr, copy = {};
  656.                 for (attr in obj) copy[attr] = obj[attr];
  657.                 return copy
  658.             }, utils.createObject = function(obj) {
  659.                 function F() {}
  660.                 return F.prototype = obj, new F
  661.             }, utils.median = function(values) {
  662.                 var l, half;
  663.                 return l = values.length, 0 === l ? void 0 : 1 === l ? values[0] : (half = Math.floor(l / 2), values.sort(), l % 2 ? values[half] : (values[half - 1] + values[half]) / 2)
  664.             }, utils.percentile = function(values, p) {
  665.                 if (0 === values.length) return 0;
  666.                 if ("number" != typeof p) throw new TypeError("p must be a number");
  667.                 if (values.sort(function(a, b) {
  668.                         return a - b
  669.                     }), 0 >= p) return values[0];
  670.                 if (p >= 1) return values[values.length - 1];
  671.                 var index = values.length * p,
  672.                     lower = Math.floor(index),
  673.                     upper = lower + 1,
  674.                     weight = index % 1;
  675.                 return upper >= values.length ? values[lower] : values[lower] * (1 - weight) + values[upper] * weight
  676.             }, utils.addEventListener = function(elem, event, listener) {
  677.                 elem.addEventListener ? elem.addEventListener(event, listener, !1) : elem.attachEvent("on" + event, listener)
  678.             }, utils.removeEventListener = function(elem, event, listener) {
  679.                 elem.addEventListener ? elem.removeEventListener(event, listener) : elem.detachEvent("on" + event, listener)
  680.             }, utils.polyfillObjectKeys = function() {
  681.                 Object.keys || (Object.keys = function() {
  682.                     var hasOwnProperty = Object.prototype.hasOwnProperty,
  683.                         hasDontEnumBug = !{
  684.                             toString: null
  685.                         }.propertyIsEnumerable("toString"),
  686.                         dontEnums = ["toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "constructor"],
  687.                         dontEnumsLength = dontEnums.length;
  688.                     return function(obj) {
  689.                         if ("object" != typeof obj && ("function" != typeof obj || null === obj)) throw new TypeError("Object.keys called on non-object");
  690.                         var prop, i, result = [];
  691.                         for (prop in obj) hasOwnProperty.call(obj, prop) && result.push(prop);
  692.                         if (hasDontEnumBug)
  693.                             for (i = 0; dontEnumsLength > i; i++) hasOwnProperty.call(obj, dontEnums[i]) && result.push(dontEnums[i]);
  694.                         return result
  695.                     }
  696.                 }())
  697.             }, utils.onDomReady = function(callback) {
  698.                 function onReadyIe() {
  699.                     "complete" === document.readyState && (document.detachEvent("onreadystatechange", onReadyIe), callback())
  700.                 }
  701.                 document.addEventListener ? document.addEventListener("DOMContentLoaded", callback, !1) : document.attachEvent("onreadystatechange", onReadyIe)
  702.             }, utils.genBlob = function(size) {
  703.                 var blobStr;
  704.                 for (blobStr = ""; 2 * blobStr.length <= size;) blobStr += blobStr + Math.random().toString();
  705.                 blobStr.length > size ? blobStr = blobStr.substring(0, size) : blobStr += blobStr.substring(0, size - blobStr.length);
  706.                 try {
  707.                     blob = new Blob([blobStr], {
  708.                         type: "application/octet-stream"
  709.                     })
  710.                 } catch (e) {
  711.                     try {
  712.                         var bb = new(window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
  713.                         bb.append(blobStr), blob = bb.getBlob("application/octet-stream")
  714.                     } catch (e) {
  715.                         blob = blobStr
  716.                     }
  717.                 }
  718.                 return blob
  719.             }, module.exports = utils
  720.         }), require.define("/app/error.js", function(require, module) {
  721.             function TesterArgumentError() {
  722.                 var temp = Error.apply(this, arguments);
  723.                 temp.name = this.name = "TesterArgumentError", this.stack = temp.stack, this.message = temp.message
  724.             }
  725.  
  726.             function TesterEventError() {
  727.                 var temp = Error.apply(this, arguments);
  728.                 temp.name = this.name = "TesterEventError", this.stack = temp.stack, this.message = temp.message
  729.             }
  730.  
  731.             function RequesterArgumentError() {
  732.                 var temp = Error.apply(this, arguments);
  733.                 temp.name = this.name = "RequesterArgumentError", this.stack = temp.stack, this.message = temp.message
  734.             }
  735.  
  736.             function GetOcaUrlError() {
  737.                 var temp = Error.apply(this, arguments);
  738.                 temp.name = this.name = "GetOcaUrlError", this.stack = temp.stack, this.message = temp.message
  739.             }
  740.  
  741.             function NoOcaUrlsError() {
  742.                 var temp = Error.apply(this, arguments);
  743.                 temp.name = this.name = "NoOcaUrlsError", this.stack = temp.stack, this.message = temp.message
  744.             }
  745.  
  746.             function DownloadOcaDataError() {
  747.                 var temp = Error.apply(this, arguments);
  748.                 temp.name = this.name = "DownloadOcaDataError", this.stack = temp.stack, this.message = temp.message
  749.             }
  750.             var errors = {};
  751.             TesterArgumentError.prototype = Error.prototype, errors.TesterArgumentError = TesterArgumentError, TesterEventError.prototype = Error.prototype, errors.TesterEventError = TesterEventError, RequesterArgumentError.prototype = Error.prototype, errors.RequesterArgumentError = RequesterArgumentError, GetOcaUrlError.prototype = Error.prototype, errors.GetOcaUrlError = GetOcaUrlError, NoOcaUrlsError.prototype = Error.prototype, errors.NoOcaUrlsError = NoOcaUrlsError, DownloadOcaDataError.prototype = Error.prototype, errors.DownloadOcaDataError = DownloadOcaDataError, module.exports = errors
  752.         }), require.define("/app/event.js", function(require, module) {
  753.             var events = {
  754.                     START: "start",
  755.                     END: "end",
  756.                     SUCCESS: "success",
  757.                     FAIL: "fail",
  758.                     STOP: "stop",
  759.                     ATTEMPT_START: "attemptStart",
  760.                     ATTEMPT_END: "attemptEnd",
  761.                     ATTEMPT_SUCCESS: "attemptSuccess",
  762.                     ATTEMPT_FAIL: "attemptFail",
  763.                     PROGRESS: "progress",
  764.                     SNAPSHOT: "snapshot",
  765.                     CONNECTION_START: "connectionStart",
  766.                     CONNECTION_FAIL: "connectionFail",
  767.                     CONNECTION_PROGRESS: "connectionProgress",
  768.                     CONNECTION_SUCCESS: "connectionSuccess",
  769.                     CONNECTION_END: "connectionEnd",
  770.                     LATENCY_START: "latencyStart",
  771.                     LATENCY_FAIL: "latencyFail",
  772.                     LATENCY_SUCCESS: "latencySuccess",
  773.                     LATENCY_END: "latencyEnd",
  774.                     UPLOAD_START: "uploadStart",
  775.                     UPLOAD_FAIL: "uploadFail",
  776.                     UPLOAD_SUCCESS: "uploadSuccess",
  777.                     UPLOAD_END: "uploadEnd",
  778.                     URL_REQUEST_START: "urlRequestStart",
  779.                     URL_REQUEST_END: "urlRequestEnd",
  780.                     AFTER_COMPLETE_ATTEMPT_START: "afterCompleteAttemptStart",
  781.                     AFTER_COMPLETE_ATTEMPT_END: "afterCompleteAttemptEnd",
  782.                     ANY: "event"
  783.                 },
  784.                 schema = {
  785.                     start: {},
  786.                     end: {},
  787.                     success: {},
  788.                     fail: {},
  789.                     stop: {},
  790.                     attemptStart: {},
  791.                     attemptEnd: {},
  792.                     attemptSuccess: {},
  793.                     attemptFail: {},
  794.                     progress: {},
  795.                     snapshot: {},
  796.                     connectionStart: {},
  797.                     connectionFail: {},
  798.                     connectionProgress: {},
  799.                     connectionSuccess: {},
  800.                     connectionEnd: {},
  801.                     latencyStart: {},
  802.                     latencyFail: {},
  803.                     latencySuccess: {},
  804.                     latencyEnd: {},
  805.                     uploadStart: {},
  806.                     uploadFail: {},
  807.                     uploadSuccess: {},
  808.                     uploadEnd: {},
  809.                     urlRequestStart: {},
  810.                     urlRequestEnd: {},
  811.                     afterCompleteAttemptStart: {},
  812.                     afterCompleteAttemptEnd: {},
  813.                     event: {}
  814.                 };
  815.             module.exports = {
  816.                 events: events,
  817.                 schema: schema
  818.             }
  819.         }), require.define("/app/config.js", function(require, module) {
  820.             module.exports = function() {
  821.                 function positiveNumberValidator(name) {
  822.                     return function(value) {
  823.                         if (0 >= value) throw new errors.TesterArgumentError(name + ": expected value should be above 0, given value: " + value)
  824.                     }
  825.                 }
  826.  
  827.                 function update(config, overrides) {
  828.                     var name, value;
  829.                     if (overrides)
  830.                         for (name in overrides) value = overrides[name], validate(name, value), config[name] = value;
  831.                     return config
  832.                 }
  833.  
  834.                 function validate(name, value) {
  835.                     var validator;
  836.                     if (void 0 === defaultConfig[name]) throw new errors.TesterArgumentError("unknown tester config attribute: " + name);
  837.                     return validator = validators[name], validator && validator(value), !0
  838.                 }
  839.                 var errors = require("./error"),
  840.                     timer = require("./timer"),
  841.                     utils = require("./utils"),
  842.                     validators = {
  843.                         maxAttempts: positiveNumberValidator("maxAttempts"),
  844.                         maxConnections: positiveNumberValidator("maxConnections"),
  845.                         progressFrequencyMs: positiveNumberValidator("progressFrequencyMs"),
  846.                         firstRequestBytes: positiveNumberValidator("firstRequestBytes"),
  847.                         maxBytesInFlight: positiveNumberValidator("maxBytesInFlight"),
  848.                         startRangeRequestTimeMs: positiveNumberValidator("startRangeRequestTimeMs"),
  849.                         maxRangeRequestTimeMs: positiveNumberValidator("maxRangeRequestTimeMs"),
  850.                         rangeRequestIncreaseMs: positiveNumberValidator("rangeRequestIncreaseMs"),
  851.                         latencyMeasurementsWindowSize: positiveNumberValidator("latencyMeasurementsWindowSize"),
  852.                         minLatencyMeasurements: positiveNumberValidator("minLatencyMeasurements"),
  853.                         latencyMeasurementsFrequencyMs: positiveNumberValidator("latencyMeasurementsFrequencyMs"),
  854.                         protocol: function(protocol) {
  855.                             if ("http" !== protocol && "https" !== protocol) throw new errors.TesterArgumentError("protocol: only http and https are supported, given value: " + protocol)
  856.                         },
  857.                         version: function() {
  858.                             throw new errors.TesterArgumentError("can not override tester version")
  859.                         },
  860.                         duration: function(duration) {
  861.                             if (void 0 === duration.min) throw new errors.TesterArgumentError("duration: when provided, duration.min is mandatory");
  862.                             if (void 0 === duration.max) throw new errors.TesterArgumentError("duration: when provided, duration.max is mandatory");
  863.                             if (duration.min > duration.max) throw new errors.TesterArgumentError("duration: max duration should be higher or equal to min duration, given value: min=" + duration.min + ", max=" + duration.max);
  864.                             if (duration.min < 0) throw new errors.TesterArgumentError("duration.min can not be negative");
  865.                             if (duration.max < 0) throw new errors.TesterArgumentError("duration.max can not be negative")
  866.                         },
  867.                         connections: function(connections) {
  868.                             if (void 0 === connections.min) throw new errors.TesterArgumentError("connections: when provided, connections.min is mandatory");
  869.                             if (void 0 === connections.max) throw new errors.TesterArgumentError("connections: when provided, connections.max is mandatory");
  870.                             if (connections.min > connections.max) throw new errors.TesterArgumentError("connections: max connections should be higher or equal to min connections, given value: min=" + connections.min + ", max=" + connections.max);
  871.                             if (connections.min <= 0) throw new errors.TesterArgumentError("connections.min should be positive");
  872.                             if (connections.max <= 0) throw new errors.TesterArgumentError("connections.max should be positive")
  873.                         }
  874.                     },
  875.                     defaultDuration = {
  876.                         min: 5,
  877.                         max: 30
  878.                     },
  879.                     defaultConfig = {
  880.                         version: "0.3.11",
  881.                         maxAttempts: 5,
  882.                         connections: {
  883.                             min: 1,
  884.                             max: 3
  885.                         },
  886.                         duration: defaultDuration,
  887.                         progressFrequencyMs: 200,
  888.                         protocol: "https",
  889.                         firstRequestBytes: 2048,
  890.                         maxBytesInFlight: 78643200,
  891.                         collectAfterComplete: !1,
  892.                         getTestOcasParams: {},
  893.                         startRangeRequestTimeMs: 300,
  894.                         maxRangeRequestTimeMs: 1e3,
  895.                         rangeRequestIncreaseMs: 200,
  896.                         timer: timer.time,
  897.                         measureLatency: !1,
  898.                         latencyMeasurementsWindowSize: 5,
  899.                         minLatencyMeasurements: 3,
  900.                         latencyMeasurementsFrequencyMs: 1e3,
  901.                         aggregator: require("./aggregator/movingAverage")(10),
  902.                         stopper: require("./stopper/stableMeasurementsStopper")({
  903.                             minDuration: defaultDuration.min,
  904.                             maxDuration: defaultDuration.max,
  905.                             stabilityDelta: 3,
  906.                             minStableMeasurements: 10
  907.                         })
  908.                     };
  909.                 return {
  910.                     get: function(overrides) {
  911.                         var config = utils.simpleCopy(defaultConfig);
  912.                         return overrides && update(config, overrides), config
  913.                     },
  914.                     validate: function(name, value) {
  915.                         return validate(name, value)
  916.                     }
  917.                 }
  918.             }()
  919.         }), require.define("/app/aggregator/movingAverage.js", function(require, module) {
  920.             module.exports = function(windowSize) {
  921.                 function movingAvg(snapshots) {
  922.                     var snapshotInd, bytes = 0,
  923.                         times = 0,
  924.                         start = snapshots.length - 1,
  925.                         end = Math.max(0, snapshots.length - windowSize);
  926.                     for (snapshotInd = start; snapshotInd >= end; --snapshotInd) snapshot = snapshots[snapshotInd], bytes += snapshot.bytes, times += snapshot.time;
  927.                     return times > 0 ? 1e3 * bytes * 8 / times : 0
  928.                 }
  929.                 return movingAvg
  930.             }
  931.         }), require.define("/app/stopper/stableMeasurementsStopper.js", function(require, module) {
  932.             module.exports = function(config) {
  933.                 function lastWindowMaxInd(measurements, windowSize) {
  934.                     var measurement, curMaxSpeed = 0,
  935.                         curMaxInd = 0,
  936.                         measurementInd = 0,
  937.                         numMeasurements = measurements.length,
  938.                         firstMeasurementInd = Math.max(0, numMeasurements - windowSize);
  939.                     for (measurementInd = numMeasurements - 1; measurementInd >= firstMeasurementInd; --measurementInd) measurement = measurements[measurementInd], measurement.speed >= curMaxSpeed && (curMaxSpeed = measurement.speed, curMaxInd = measurementInd);
  940.                     return curMaxInd
  941.                 }
  942.  
  943.                 function maxDelta(value, measurements) {
  944.                     var measurementInd, delta, maxDelta = 0;
  945.                     for (measurementInd = 0; measurementInd < measurements.length; ++measurementInd) delta = 100 * Math.abs(measurements[measurementInd].speed - value) / value, delta > maxDelta && (maxDelta = delta);
  946.                     return delta
  947.                 }
  948.  
  949.                 function isCompleted(metrics) {
  950.                     var testTime = void 0 !== metrics.testTime ? metrics.testTime : timer() - startTime,
  951.                         measurements = metrics.progressMeasurements,
  952.                         afterCompleteDuration = metrics.afterCompleteDuration,
  953.                         numMeasurements = measurements.length;
  954.                     if (void 0 === afterCompleteDuration) {
  955.                         if (testTime >= maxDuration) return !0;
  956.                         if (minStableMeasurements > numMeasurements) return !1;
  957.                         var maxInd = lastWindowMaxInd(measurements, minStableMeasurements);
  958.                         if (minStableMeasurements > numMeasurements - maxInd) return !1;
  959.                         var delta = maxDelta(measurements[maxInd].speed, measurements.slice(numMeasurements - minStableMeasurements, numMeasurements));
  960.                         return delta > stabilityDelta ? !1 : testTime >= minDuration
  961.                     }
  962.                     return testTime >= afterCompleteDuration
  963.                 }
  964.                 var minDuration, maxDuration, stabilityDelta, minStableMeasurements, timer, startTime;
  965.                 return minDuration = config.minDuration || 5, maxDuration = config.maxDuration || 60, stabilityDelta = config.stabilityDelta || 5, minStableMeasurements = config.minStableMeasurements || 5, timer = config.timer || require("../timer").time, startTime = timer(), isCompleted
  966.             }
  967.         }), require.define("/node_modules/circular-buffer/package.json", function(require, module) {
  968.             module.exports = {
  969.                 main: "index.js"
  970.             }
  971.         }), require.define("/node_modules/circular-buffer/index.js", function(require, module) {
  972.             function CircularBuffer(capacity) {
  973.                 if (!(this instanceof CircularBuffer)) return new CircularBuffer(capacity);
  974.                 if ("object" == typeof capacity && Array.isArray(capacity._buffer) && "number" == typeof capacity._capacity && "number" == typeof capacity._first && "number" == typeof capacity._size)
  975.                     for (var prop in capacity) capacity.hasOwnProperty(prop) && (this[prop] = capacity[prop]);
  976.                 else {
  977.                     if ("number" != typeof capacity || capacity % 1 != 0 || 1 > capacity) throw new TypeError("Invalid capacity");
  978.                     this._buffer = new Array(capacity), this._capacity = capacity, this._first = 0, this._size = 0
  979.                 }
  980.             }
  981.             CircularBuffer.prototype = {
  982.                 size: function() {
  983.                     return this._size
  984.                 },
  985.                 capacity: function() {
  986.                     return this._capacity
  987.                 },
  988.                 enq: function(value) {
  989.                     this._first > 0 ? this._first-- : this._first = this._capacity - 1, this._buffer[this._first] = value, this._size < this._capacity && this._size++
  990.                 },
  991.                 push: function(value) {
  992.                     this._size == this._capacity ? (this._buffer[this._first] = value, this._first = (this._first + 1) % this._capacity) : (this._buffer[(this._first + this._size) % this._capacity] = value, this._size++)
  993.                 },
  994.                 deq: function() {
  995.                     if (0 == this._size) throw new RangeError("dequeue on empty buffer");
  996.                     var value = this._buffer[(this._first + this._size - 1) % this._capacity];
  997.                     return this._size--, value
  998.                 },
  999.                 pop: function() {
  1000.                     return this.deq()
  1001.                 },
  1002.                 shift: function() {
  1003.                     if (0 == this._size) throw new RangeError("shift on empty buffer");
  1004.                     var value = this._buffer[this._first];
  1005.                     return this._first == this._capacity - 1 ? this._first = 0 : this._first++, this._size--, value
  1006.                 },
  1007.                 get: function(start, end) {
  1008.                     if (0 == this._size && 0 == start && (void 0 == end || 0 == end)) return [];
  1009.                     if ("number" != typeof start || start % 1 != 0 || 0 > start) throw new TypeError("Invalid start");
  1010.                     if (start >= this._size) throw new RangeError("Index past end of buffer: " + start);
  1011.                     if (void 0 == end) return this._buffer[(this._first + start) % this._capacity];
  1012.                     if ("number" != typeof end || end % 1 != 0 || 0 > end) throw new TypeError("Invalid end");
  1013.                     if (end >= this._size) throw new RangeError("Index past end of buffer: " + end);
  1014.                     return this._first + start >= this._capacity && (start -= this._capacity, end -= this._capacity), this._first + end < this._capacity ? this._buffer.slice(this._first + start, this._first + end + 1) : this._buffer.slice(this._first + start, this._capacity).concat(this._buffer.slice(0, this._first + end + 1 - this._capacity))
  1015.                 },
  1016.                 toarray: function() {
  1017.                     return 0 == this._size ? [] : this.get(0, this._size - 1)
  1018.                 }
  1019.             }, module.exports = CircularBuffer
  1020.         }), require.define("/app/url_getter.js", function(require, module) {
  1021.             var urlGetter = function() {
  1022.                 function badUrlReporter() {
  1023.                     function parseUrl(url) {
  1024.                         var endpoint, urlParts = url.split("?"),
  1025.                             urlData = {};
  1026.                         return urlData.url = url, urlData.normalizedUrl = url, endpoint = urlParts[0], 2 === urlParts.length && (speedtestInd = endpoint.lastIndexOf("/speedtest"), speedtestInd ? (endpoint = endpoint.substring(0, speedtestInd + 10), urlData.normalizedUrl = endpoint + "?" + urlParts[1]) : urlData.normalizedUrl = url), endpoint = endpoint.replace(/.*?:\/\//g, ""), urlData.oca = endpoint.split("/")[0], urlData.site = urlData.oca.split(".")[2], urlData
  1027.                     }
  1028.  
  1029.                     function reset() {
  1030.                         badUrls = {}, ocaFailures = {}, siteFailures = {}, clientInfo = {}
  1031.                     }
  1032.                     var badUrls, ocaFailures, siteFailures;
  1033.                     return reset(), {
  1034.                         reset: reset,
  1035.                         isBadUrl: function(url) {
  1036.                             var urlData = parseUrl(url),
  1037.                                 ocaFails = void 0 === ocaFailures[urlData.oca] ? 0 : ocaFailures[urlData.oca],
  1038.                                 siteFails = void 0 === siteFailures[urlData.site] ? 0 : siteFailures[urlData.site];
  1039.                             return url = urlData.normalizedUrl, void 0 !== badUrls[url] || ocaFails >= 2 || siteFails >= 3
  1040.                         },
  1041.                         reportBadUrl: function(url) {
  1042.                             var urlData = parseUrl(url);
  1043.                             url = urlData.normalizedUrl, badUrls[url] = !0, urlData.oca && (ocaFailures[urlData.oca] = void 0 === ocaFailures[urlData.oca] ? 1 : ocaFailures[urlData.oca] + 1), 1 === ocaFailures[urlData.oca] && urlData.site && (siteFailures[urlData.site] = void 0 === siteFailures[urlData.site] ? 1 : siteFailures[urlData.site] + 1)
  1044.                         }
  1045.                     }
  1046.                 }
  1047.  
  1048.                 function parseSpeedTestUrls(responseText) {
  1049.                     var response, targets, ind, url, allUrls;
  1050.                     for (allUrls = [], response = JSON.parse(responseText), targets = response.targets || [], clientInfo = response.client || {}, clientInfo.servers = [], ind = 0; ind < targets.length; ++ind) url = targets[ind].url, url = url.replace(/speedtest/, "speedtest/range/"), urlReporter.isBadUrl(url) || (testUrls.push(url), clientInfo.servers.push(targets[ind])), allUrls.push(url);
  1051.                     return 0 === testUrls.length && (testUrls = allUrls), testUrls
  1052.                 }
  1053.  
  1054.                 function formatUrl(params) {
  1055.                     var param, endpoint = params.endpoint || DEFAULT_PARAMS.endpoint,
  1056.                         https = void 0 !== params.https ? params.https : DEFAULT_PARAMS.https,
  1057.                         token = params.token || DEFAULT_PARAMS.token,
  1058.                         urlCount = params.urlCount || DEFAULT_PARAMS.urlCount,
  1059.                         url = "https://" + endpoint + "?https=" + https + "&token=" + token + "&urlCount=" + urlCount;
  1060.                     for (param in params.extraParams) params.extraParams.hasOwnProperty(param) && (url += "&" + param + "=" + params.extraParams[param]);
  1061.                     return url
  1062.                 }
  1063.  
  1064.                 function reset() {
  1065.                     testUrls = [], curUrlInd = 0, req = void 0
  1066.                 }
  1067.                 var DEFAULT_PARAMS, curUrlInd, testUrls, that, errors, req, urlReporter, clientInfo;
  1068.                 return errors = require("./error"), dummy = require("./utils").dummy, DEFAULT_PARAMS = {
  1069.                     https: !0,
  1070.                     token: "YXNkZmFzZGxmbnNkYWZoYXNkZmhrYWxm",
  1071.                     urlCount: 3,
  1072.                     endpoint: "api-global.netflix.com/oca/speedtest",
  1073.                     extraParams: {}
  1074.                 }, that = {}, urlReporter = badUrlReporter(), reset(), that.get = function(urlParams, requester, onComplete, onFail, onStop, refresh) {
  1075.                     function returnUrl(urls) {
  1076.                         urls.length > 0 ? onComplete(urls[curUrlInd]) : onFail(new errors.NoOcaUrlsError("No urls returned from the endpoint " + url))
  1077.                     }
  1078.                     return 0 === testUrls.length || refresh ? void that.refresh(urlParams, requester, returnUrl, onFail, onStop) : void returnUrl(testUrls)
  1079.                 }, that.getNext = function(urlParams, requester, onComplete, onFail, onStop, refresh) {
  1080.                     return testUrls.length < 3 || refresh ? void that.get(urlParams, requester, onComplete, onFail, onStop, !0) : (curUrlInd = (curUrlInd + 1) % testUrls.length, void(testUrls.length > 0 ? onComplete(testUrls[curUrlInd]) : onFail(new errors.NoOcaUrlsError("No urls returned from the endpoint " + urlParams))))
  1081.                 }, that.reportBadUrl = function(url) {
  1082.                     var urlInd, newTestUrls = [];
  1083.                     for (urlReporter.reportBadUrl(url), urlInd = 0; urlInd < testUrls.length; ++urlInd) urlReporter.isBadUrl(testUrls[urlInd]) || newTestUrls.push(testUrls[urlInd]);
  1084.                     return testUrls = newTestUrls
  1085.                 }, that.refresh = function(urlParams, requester, onSuccess, onFail, onStop) {
  1086.                     function urlSuccess(data) {
  1087.                         req = void 0, data.success ? (reset(), testUrls = parseSpeedTestUrls(data.response), onSuccess(testUrls)) : "stopped" !== data.reason ? (error = new errors.GetOcaUrlError("Could not send request to " + url), error.response = data.response, onFail(error)) : onStop(data)
  1088.                     }
  1089.                     req = requester(), url = formatUrl(urlParams), req.start(url, dummy, urlSuccess, !0)
  1090.                 }, that.stop = function() {
  1091.                     req && req.stop()
  1092.                 }, that.reset = function() {
  1093.                     reset(), urlReporter.reset()
  1094.                 }, that.clientInfo = function() {
  1095.                     return clientInfo
  1096.                 }, that
  1097.             };
  1098.             module.exports = urlGetter
  1099.         }), require.define("/app/snapshot.js", function(require, module) {
  1100.             module.exports = function() {
  1101.                 function reset() {
  1102.                     snapshots = [], lastProcessedWorkerMeasurement = {}, overflowMeasurements = {}
  1103.                 }
  1104.  
  1105.                 function getLatestSnapshotMeasurements(workerMeasurements) {
  1106.                     var workerId, snapshotMeasurements, curWorkerMeasurements, workerMeasurementsCount, lastMeasurement, lastMeasurementInd, snapshotEnd, newLastProcessedWorkerMeasurement, curWorkerOverflowMeasurements;
  1107.                     snapshotMeasurements = [], newLastProcessedWorkerMeasurement = {};
  1108.                     for (workerId in workerMeasurements)
  1109.                         if (workerMeasurements.hasOwnProperty(workerId)) {
  1110.                             if (curWorkerMeasurements = workerMeasurements[workerId], workerMeasurementsCount = curWorkerMeasurements.length, lastMeasurementInd = lastProcessedWorkerMeasurement[workerId] || 0, curWorkerOverflowMeasurements = overflowMeasurements[workerId] || [], !(workerMeasurementsCount > lastMeasurementInd || curWorkerOverflowMeasurements.length > 0)) return;
  1111.                             overflowMeasurements[workerId] = [], snapshotMeasurements.push({
  1112.                                 workerId: workerId,
  1113.                                 measurements: [].concat(curWorkerOverflowMeasurements, curWorkerMeasurements.slice(lastMeasurementInd, workerMeasurementsCount))
  1114.                             }), lastMeasurement = curWorkerMeasurements[workerMeasurementsCount - 1], (!snapshotEnd || lastMeasurement.end < snapshotEnd) && (snapshotEnd = lastMeasurement.end), newLastProcessedWorkerMeasurement[workerId] = workerMeasurementsCount
  1115.                         } return lastProcessedWorkerMeasurement = newLastProcessedWorkerMeasurement, {
  1116.                         measurements: snapshotMeasurements,
  1117.                         end: snapshotEnd
  1118.                     }
  1119.                 }
  1120.  
  1121.                 function trimSnapshotMeasurements(snapshotMeasurements, snapshotEnd) {
  1122.                     var curWorkerMeasurements, workerId, lastMeasurement, measurementId, overflowMeasurement, snapshotInd, snapshot;
  1123.                     for (snapshotInd = 0; snapshotInd < snapshotMeasurements.length; ++snapshotInd) {
  1124.                         for (snapshot = snapshotMeasurements[snapshotInd], curWorkerMeasurements = snapshot.measurements, workerId = snapshot.workerId, measurementId = curWorkerMeasurements.length - 1; measurementId >= 0 && (lastMeasurement = curWorkerMeasurements[measurementId], lastMeasurement.start >= snapshotEnd); --measurementId) overflowMeasurements[workerId] || (overflowMeasurements[workerId] = []), overflowMeasurements[workerId].push(lastMeasurement);
  1125.                         snapshot.measurements = curWorkerMeasurements.slice(0, measurementId + 1), measurementId >= 0 && lastMeasurement && lastMeasurement.end > snapshotEnd && (overflowMeasurement = {
  1126.                             start: snapshotEnd,
  1127.                             end: lastMeasurement.end
  1128.                         }, overflowMeasurement.bytes = lastMeasurement.bytes * (lastMeasurement.end - snapshotEnd) / (lastMeasurement.end - lastMeasurement.start), lastMeasurement.bytes -= overflowMeasurement.bytes, lastMeasurement.end = snapshotEnd, overflowMeasurements[workerId] || (overflowMeasurements[workerId] = []), overflowMeasurements[workerId].push(overflowMeasurement))
  1129.                     }
  1130.                     return snapshotMeasurements
  1131.                 }
  1132.  
  1133.                 function makeSnapshot(snapshotMeasurements) {
  1134.                     function minStart(curWorkerMeasurements) {
  1135.                         var snapshotInd, min = {
  1136.                             workerId: null,
  1137.                             measurement: {},
  1138.                             snapshotInd: null
  1139.                         };
  1140.                         for (snapshotInd = 0; snapshotInd < snapshotMeasurements.length; ++snapshotInd) {
  1141.                             var workerId = snapshotMeasurements[snapshotInd].workerId,
  1142.                                 measurements = snapshotMeasurements[snapshotInd].measurements,
  1143.                                 measurementInd = curWorkerMeasurements[workerId] || 0,
  1144.                                 measurement = measurements[measurementInd];
  1145.                             measurement && (void 0 === min.measurement.start || measurement.start < min.measurement.start) && (min.workerId = workerId, min.measurement = measurement, min.snaphotInd = snapshotInd)
  1146.                         }
  1147.                         return min
  1148.                     }
  1149.                     var minTime, maxTime, snapshot, curRange, rangeFinished, curLastTime;
  1150.                     for (snapshot = {
  1151.                             bytes: 0,
  1152.                             time: 0
  1153.                         }, curRange = {}, rangeFinished = 0; rangeFinished < snapshotMeasurements.length;) {
  1154.                         var curMin = minStart(curRange);
  1155.                         null !== curMin.workerId ? (curRange[curMin.workerId] = (curRange[curMin.workerId] || 0) + 1, curRange[curMin.workerId] >= snapshotMeasurements[curMin.snaphotInd].measurements.length && (rangeFinished += 1), void 0 === minTime && (minTime = curMin.measurement.start, curLastTime = minTime), curLastTime = Math.max(curLastTime, curMin.measurement.start), curMin.measurement.end > curLastTime && (snapshot.time += curMin.measurement.end - curLastTime, curLastTime = curMin.measurement.end), snapshot.bytes += curMin.measurement.bytes) : rangeFinished += 1
  1156.                     }
  1157.                     return maxTime = curLastTime, void 0 !== maxTime && void 0 !== minTime ? (snapshot.start = minTime, snapshot.end = maxTime, snapshot) : void 0
  1158.                 }
  1159.                 var snapshots, lastProcessedWorkerMeasurement, overflowMeasurements;
  1160.                 return reset(), {
  1161.                     reset: function() {
  1162.                         reset()
  1163.                     },
  1164.                     compute: function(workerMeasurements) {
  1165.                         var workerSnapshotData = getLatestSnapshotMeasurements(workerMeasurements);
  1166.                         if (workerSnapshotData) {
  1167.                             var snapshotMeasurements = trimSnapshotMeasurements(workerSnapshotData.measurements, workerSnapshotData.end),
  1168.                                 snapshot = makeSnapshot(snapshotMeasurements);
  1169.                             return snapshot && snapshots.push(snapshot), snapshot
  1170.                         }
  1171.                     },
  1172.                     length: function() {
  1173.                         return snapshots.length
  1174.                     },
  1175.                     all: function() {
  1176.                         return snapshots
  1177.                     }
  1178.                 }
  1179.             }()
  1180.         }), require.define("/app/requester/xhr.js", function(require, module) {
  1181.             var requester = function() {
  1182.                 function extractTimingInfo(data, url) {
  1183.                     var entries, timing, end;
  1184.                     window && window.performance && window.performance.getEntriesByName && (entries = window.performance.getEntriesByName(url), 0 != entries.length && (timing = entries[entries.length - 1], end = timing.responseStart || timing.responseEnd, end && (data.timing = timing, data.start = timing.requestStart || timing.connectEnd || timing.startTime || timing.fetchTime || data.start, data.end = end)))
  1185.                 }
  1186.  
  1187.                 function validate(url, onProgressCallback, onCompleteCallback) {
  1188.                     if (!url) throw new errors.RequesterArgumentError("url arguments is mandatory to perform speed test");
  1189.                     if (!onProgressCallback) throw new errors.RequesterArgumentError("onProgressCallback is mandatory");
  1190.                     if (!onCompleteCallback) throw new errors.RequesterArgumentError("onCompleteCallback is mandatory")
  1191.                 }
  1192.  
  1193.                 function startTest(url, withResponse) {
  1194.                     function ontimeout() {
  1195.                         testIsRunning = !1, request && request.abort(), onComplete({
  1196.                             success: !1,
  1197.                             response: {
  1198.                                 type: "Timeout",
  1199.                                 message: "Request timed out. Timeout: " + requestTimeoutMs + "ms"
  1200.                             }
  1201.                         })
  1202.                     }
  1203.  
  1204.                     function onerror() {
  1205.                         var headers, errorMessage;
  1206.                         clearTimeout(xmlHttpTimeout), errorMessage = this.response, !errorMessage && withResponse && (errorMessage = this.responseText), this.getAllResponseHeaders && (headers = this.getAllResponseHeaders()), onComplete({
  1207.                             success: !1,
  1208.                             response: {
  1209.                                 type: "RequestError",
  1210.                                 status: this.status,
  1211.                                 statusText: this.statusText,
  1212.                                 message: errorMessage,
  1213.                                 headers: headers,
  1214.                                 url: url
  1215.                             }
  1216.                         })
  1217.                     }
  1218.  
  1219.                     function onload() {
  1220.                         var now, data, responseSize;
  1221.                         testIsRunning && (now = timer(), clearTimeout(xmlHttpTimeout), data = {
  1222.                             success: !0,
  1223.                             start: lastCompleteTime,
  1224.                             end: now
  1225.                         }, withResponse && (data.response = this.responseText), responseSize = this.response && this.response.size, extractTimingInfo(data, url), onProgress({
  1226.                             start: lastProgressTime,
  1227.                             end: data.end,
  1228.                             success: !0,
  1229.                             bytes: void 0 !== responseSize ? responseSize - lastBytesLoaded : responseSize
  1230.                         }), onComplete(data))
  1231.                     }
  1232.  
  1233.                     function onprogress(e) {
  1234.                         clearTimeout(xmlHttpTimeout);
  1235.                         var now, newBytesLoaded;
  1236.                         200 !== this.status && 304 !== this.status || !e.lengthComputable || (now = timer(), newBytesLoaded = e.loaded - lastBytesLoaded, lastBytesLoaded = e.loaded, onProgress({
  1237.                             bytes: newBytesLoaded,
  1238.                             success: !0,
  1239.                             start: lastProgressTime,
  1240.                             end: now
  1241.                         }), lastProgressTime = now)
  1242.                     }
  1243.                     var lastBytesLoaded, lastProgressTime, lastCompleteTime, xmlHttpTimeout;
  1244.                     lastBytesLoaded = 0, request = xhr(), request.onprogress = onprogress, xmlHttpTimeout = setTimeout(ontimeout, requestTimeoutMs), "onreadystatechange" in request ? request.onreadystatechange = function() {
  1245.                         testIsRunning && 4 === this.readyState && (200 === this.status || 304 === this.status ? onload.apply(this) : onerror.apply(this))
  1246.                     } : (request.onerror = onerror, request.onload = onload, request.ontimeout = function() {}), request.open("GET", url, !0), withResponse || (request.overrideMimeType && request.overrideMimeType("text/plain; charset=x-user-defined"), request.responseType = "blob"), lastProgressTime = lastCompleteTime = timer(), request.timeout = 6e4, setTimeout(function() {
  1247.                         request && request.send()
  1248.                     }, 0)
  1249.                 }
  1250.  
  1251.                 function uploadTest(url, size) {
  1252.                     function ontimeout() {
  1253.                         testIsRunning = !1, request && request.abort(), onComplete({
  1254.                             success: !1,
  1255.                             response: {
  1256.                                 type: "Timeout",
  1257.                                 message: "Request timed out. Timeout: " + requestTimeoutMs + "ms"
  1258.                             }
  1259.                         })
  1260.                     }
  1261.  
  1262.                     function onerror() {
  1263.                         var headers, errorMessage;
  1264.                         clearTimeout(xmlHttpTimeout), errorMessage = this.response, errorMessage || (errorMessage = this.responseText), this.getAllResponseHeaders && (headers = this.getAllResponseHeaders()), onComplete({
  1265.                             success: !1,
  1266.                             response: {
  1267.                                 type: "RequestError",
  1268.                                 status: this.status,
  1269.                                 statusText: this.statusText,
  1270.                                 message: errorMessage,
  1271.                                 headers: headers,
  1272.                                 url: url
  1273.                             }
  1274.                         })
  1275.                     }
  1276.  
  1277.                     function onload() {
  1278.                         var now, data;
  1279.                         testIsRunning && (now = timer(), clearTimeout(xmlHttpTimeout), data = {
  1280.                             success: !0,
  1281.                             start: lastCompleteTime,
  1282.                             end: now
  1283.                         }, extractTimingInfo(data, url), onProgress({
  1284.                             start: lastProgressTime,
  1285.                             bytes: size - lastBytesLoaded,
  1286.                             end: data.end,
  1287.                             success: !0
  1288.                         }), onComplete(data))
  1289.                     }
  1290.  
  1291.                     function onprogress(e) {
  1292.                         clearTimeout(xmlHttpTimeout);
  1293.                         var now, newBytesLoaded;
  1294.                         now = timer(), newBytesLoaded = e.loaded - lastBytesLoaded, lastBytesLoaded = e.loaded, onProgress({
  1295.                             bytes: newBytesLoaded,
  1296.                             success: !0,
  1297.                             start: lastProgressTime,
  1298.                             end: now
  1299.                         }), lastProgressTime = now
  1300.                     }
  1301.                     var lastBytesLoaded, lastProgressTime, lastCompleteTime, xmlHttpTimeout;
  1302.                     lastBytesLoaded = 0, request = xhr(), request.upload && (request.upload.onprogress = onprogress), xmlHttpTimeout = setTimeout(ontimeout, requestTimeoutMs), "onreadystatechange" in request ? request.onreadystatechange = function() {
  1303.                         testIsRunning && 4 === this.readyState && (this.status >= 200 && this.status < 300 || 304 === this.status ? onload.apply(this) : onerror.apply(this))
  1304.                     } : request.upload && (request.upload.onerror = onerror, request.upload.onload = onload, request.upload.ontimeout = function() {}), request.open("POST", url, !0);
  1305.                     try {
  1306.                         size > 0 && request.setRequestHeader("Content-type", "application/octet-stream")
  1307.                     } catch (err) {}
  1308.                     var blob = null;
  1309.                     size > 0 && (blob = utils.genBlob(size)), lastProgressTime = lastCompleteTime = timer(), request.timeout = 6e4, setTimeout(function() {
  1310.                         request && request.send(blob)
  1311.                     }, 0)
  1312.                 }
  1313.                 var testIsRunning, request, errors, xhr, timer, utils, requestTimeoutMs, dummy, onComplete, onProgress, supportsProgress;
  1314.                 return requestTimeoutMs = 1e4, errors = require("../error"), utils = require("../utils"), xhr = utils.getXHR, timer = require("../timer").time, dummy = require("../utils").dummy, testIsRunning = !1, {
  1315.                     start: function(url, onProgressCallback, onCompleteCallback, withResponse) {
  1316.                         validate(url, onProgressCallback, onCompleteCallback), onComplete = onCompleteCallback, onProgress = onProgressCallback, testIsRunning = !0, startTest(url, withResponse)
  1317.                     },
  1318.                     upload: function(url, size, onProgressCallback, onCompleteCallback) {
  1319.                         validate(url, onProgressCallback, onCompleteCallback), onComplete = onCompleteCallback, onProgress = onProgressCallback, testIsRunning = !0, uploadTest(url, size)
  1320.                     },
  1321.                     supportsProgress: function() {
  1322.                         return void 0 !== supportsProgress ? supportsProgress : void(supportsProgress = window.XDomainRequest && -1 === navigator.appVersion.indexOf("MSIE 10") ? !1 : "onprogress" in xhr())
  1323.                     },
  1324.                     stop: function() {
  1325.                         testIsRunning = !1, request && (request.onreadystatechange = dummy, request.onprogress = dummy, request.abort(), request = void 0, onComplete({
  1326.                             success: !1,
  1327.                             reason: "stopped"
  1328.                         }), onComplete = dummy, onProgress = dummy)
  1329.                     }
  1330.                 }
  1331.             };
  1332.             module.exports = requester
  1333.         }), require.define("/app/logger.js", function(require, module) {
  1334.             function logger(tester, config) {
  1335.                 function sendLogs(data) {
  1336.                     function onload() {}
  1337.  
  1338.                     function onerror() {}
  1339.                     var request = require("./utils").getXHR();
  1340.                     request.onload ? (request.onload = onload, request.onerror = onerror) : request.onreadystatechange = function() {
  1341.                         4 === this.readyState && (this.status >= 200 && this.status < 400 ? onload() : onerror())
  1342.                     }, request.open("POST", logUrl, !0), request.timeout = 5e3, request.setRequestHeader && request.setRequestHeader("Content-type", "application/json"), request.send(data)
  1343.                 }
  1344.  
  1345.                 function getPageLoadPerformance() {
  1346.                     return window && window.performance && window.performance.timing ? window.performance.timing : {}
  1347.                 }
  1348.  
  1349.                 function endSessionType(data) {
  1350.                     switch (data.result) {
  1351.                         case "success":
  1352.                             return "SessionEnded";
  1353.                         case "fail":
  1354.                             return "ActionFailed";
  1355.                         case "stop":
  1356.                             return "SessionCancelled"
  1357.                     }
  1358.                 }
  1359.  
  1360.                 function endConnectionSession(oca, data) {
  1361.                     var connectionSessionId = connectionSessionIds[oca];
  1362.                     data.type = endSessionType(data), data.measurements = connectionEvents[oca], nfLogger.endSession(connectionSessionId, data), delete connectionSessionIds[oca], delete connectionEvents[oca]
  1363.                 }
  1364.  
  1365.                 function endMeasureSession(sessionId, data) {
  1366.                     data.type = endSessionType(data), data.snapshots = snapshotEvents, data.progress = progressEvents, data.latencies = latencyEvents, snapshotEvents = [], progressEvents = [], latencyEvents = {}, nfLogger.endSession(sessionId, data)
  1367.                 }
  1368.                 var DEFAULT_URL = "https://ichnaea.test.netflix.com/cl2",
  1369.                     DEFAULT_SOURCE = "netspeed",
  1370.                     SPEED_TEST_SESSION = "SpeedTest",
  1371.                     MEASURE_ATTEMPT_SESSION = "Measure",
  1372.                     CONNECTION_SESSION = "Connection",
  1373.                     AFTER_COMPLETE_SESSION = "AfterCompleteMeasure",
  1374.                     URL_REQUEST_SESSION = "GetUrl",
  1375.                     CLIENT_CONTEXT = "Client";
  1376.                 PAGE_LOAD = "pageLoad";
  1377.                 var logUrl, logSource, that, nfLogger, utils, testSessionId, testAttemptSessionId, afterCompleteSessionId, loggingEnabled = !1,
  1378.                     connectionSessionIds = {},
  1379.                     getUrlSessionIds = [],
  1380.                     snapshotEvents = [],
  1381.                     progressEvents = [],
  1382.                     latencyEvents = {},
  1383.                     connectionEvents = {},
  1384.                     events = require("./event").events;
  1385.                 return config = config || {}, logUrl = config.url || DEFAULT_URL, logSource = config.source || DEFAULT_SOURCE, utils = require("./utils"), nfLogger = require("nf-cl-logger")({
  1386.                     requestSender: sendLogs,
  1387.                     batchInterval: 6e5,
  1388.                     batchSize: 1e5,
  1389.                     source: logSource
  1390.                 }), nfLogger.addContext(CLIENT_CONTEXT, {
  1391.                     deviceType: navigator.deviceData ? "Mobile" : "Browser",
  1392.                     appVersion: navigator.appVersion,
  1393.                     userAgent: navigator.userAgent,
  1394.                     tester: tester.config(),
  1395.                     referrer: document.referrer,
  1396.                     deviceInfo: navigator.deviceData,
  1397.                     href: window && window.location && window.location.href || "NA"
  1398.                 }), that = {
  1399.                     startLogging: function() {
  1400.                         loggingEnabled || (loggingEnabled = !0, tester.on(events.START, function(data) {
  1401.                             testSessionId = nfLogger.startSession(SPEED_TEST_SESSION, data)
  1402.                         }), tester.on(events.END, function(data) {
  1403.                             data[PAGE_LOAD] = getPageLoadPerformance(), data.type = endSessionType(data), nfLogger.endSession(testSessionId, data), nfLogger.flush()
  1404.                         }), tester.on(events.ATTEMPT_START, function(data) {
  1405.                             testAttemptSessionId = nfLogger.startSession(MEASURE_ATTEMPT_SESSION, data)
  1406.                         }), tester.on(events.ATTEMPT_END, function(data) {
  1407.                             endMeasureSession(testAttemptSessionId, data)
  1408.                         }), tester.on(events.CONNECTION_START, function(data) {
  1409.                             var oca = data.oca;
  1410.                             connectionSessionIds[oca] = nfLogger.startSession(CONNECTION_SESSION, data), connectionEvents[oca] = []
  1411.                         }), tester.on(events.CONNECTION_PROGRESS, function(data) {
  1412.                             var oca = data.oca;
  1413.                             connectionEvents[oca] && connectionEvents[oca].push({
  1414.                                 bytes: data.bytes,
  1415.                                 start: data.start,
  1416.                                 end: data.end
  1417.                             })
  1418.                         }), tester.on(events.CONNECTION_END, function(data) {
  1419.                             endConnectionSession(data.oca, data)
  1420.                         }), tester.on(events.URL_REQUEST_START, function(data) {
  1421.                             getUrlSessionIds.push(nfLogger.startSession(URL_REQUEST_SESSION, data))
  1422.                         }), tester.on(events.URL_REQUEST_END, function(data) {
  1423.                             var sessionId = getUrlSessionIds[0];
  1424.                             sessionId && (data.type = endSessionType(data), nfLogger.endSession(sessionId, data), getUrlSessionIds.shift())
  1425.                         }), tester.on(events.LATENCY_END, function(data) {
  1426.                             var oca = data.oca;
  1427.                             latencyEvents[oca] || (latencyEvents[oca] = []), latencyEvents[oca].push(data)
  1428.                         }), tester.on(events.PROGRESS, function(data) {
  1429.                             progressEvents.push(data)
  1430.                         }), tester.on(events.SNAPSHOT, function(data) {
  1431.                             snapshotEvents.push(data)
  1432.                         }), tester.on(events.AFTER_COMPLETE_ATTEMPT_START, function(data) {
  1433.                             afterCompleteSessionId = nfLogger.startSession(AFTER_COMPLETE_SESSION, data)
  1434.                         }), tester.on(events.AFTER_COMPLETE_ATTEMPT_END, function(data) {
  1435.                             endMeasureSession(afterCompleteSessionId, data), nfLogger.flush()
  1436.                         }), tester.on(events.ANY, function(data) {}))
  1437.                     },
  1438.                     endLogging: function() {
  1439.                         loggingEnabled && (loggingEnabled = !1)
  1440.                     },
  1441.                     getLogger: function() {
  1442.                         return nfLogger
  1443.                     },
  1444.                     flush: function() {
  1445.                         nfLogger.flush()
  1446.                     }
  1447.                 }
  1448.             }
  1449.             module.exports = logger
  1450.         }), require.define("/node_modules/nf-cl-logger/package.json", function(require, module) {
  1451.             module.exports = {
  1452.                 main: "index.js"
  1453.             }
  1454.         }), require.define("/node_modules/nf-cl-logger/index.js", function(require, module) {
  1455.             "use strict";
  1456.             module.exports = require("./src/logger")
  1457.         }), require.define("/node_modules/nf-cl-logger/src/logger.js", function(require, module) {
  1458.             "use strict";
  1459.  
  1460.             function createCompactLogger(optionsArg) {
  1461.                 var options = optionsArg || {};
  1462.                 return options.version = options.version || "2.0", options.envelopeName = options.envelopeName || "CompactConsolidatedLoggingEnvelope", new Logger(options)
  1463.             }
  1464.             var Logger = require("./logger-core");
  1465.             module.exports = createCompactLogger
  1466.         }), require.define("/node_modules/nf-cl-logger/src/logger-core.js", function(require, module) {
  1467.             "use strict";
  1468.  
  1469.             function Logger() {
  1470.                 this._init.apply(this, arguments)
  1471.             }
  1472.             var SCHEMA = require("nf-cl-schema-ui"),
  1473.                 VERSION = "2.0.3",
  1474.                 MAX_BITS_COUNT = 53,
  1475.                 INCREMENTING_BITS_COUNT = 28,
  1476.                 RANDOM_BITS_COUNT = MAX_BITS_COUNT - INCREMENTING_BITS_COUNT,
  1477.                 INCREMENTING_BITS_MASK = Math.pow(2, INCREMENTING_BITS_COUNT) - 1,
  1478.                 RANDOM_BITS_SHIFT = Math.pow(2, RANDOM_BITS_COUNT);
  1479.             Logger.prototype = {
  1480.                 constructor: Logger,
  1481.                 batchInterval: 3e4,
  1482.                 batchSize: 50,
  1483.                 timeOffset: 0,
  1484.                 source: "",
  1485.                 requestSender: null,
  1486.                 getClientTime: null,
  1487.                 addContext: function(type, data) {
  1488.                     var context = this._initContext([type], data);
  1489.                     return this._state.pending[context.id] = context, context.id
  1490.                 },
  1491.                 removeContext: function(id) {
  1492.                     return this._state.pending[id] ? (delete this._state.pending[id], id) : this._state.current[id] ? (this._state.currentDelta.push(this._state.current[id]), delete this._state.current[id], id) : null
  1493.                 },
  1494.                 logEvent: function(type, data) {
  1495.                     var context = this._initEventContext([type, "DiscreteEvent"], data);
  1496.                     return this._snapshot(context), context.id
  1497.                 },
  1498.                 startSession: function(type, data) {
  1499.                     var context = this._initEventContext([type, "Session"], data);
  1500.                     return this._state.current[context.id] = context, this._snapshot(), context.id
  1501.                 },
  1502.                 endSession: function(sessionId, data) {
  1503.                     var startContext = this._state.current[sessionId];
  1504.                     if (startContext) {
  1505.                         var type = data && data.type ? [data.type, "SessionEnded"] : ["SessionEnded"],
  1506.                             endContext = this._initEventContext(type, data);
  1507.                         return endContext.duration = endContext.time - startContext.time, endContext.sessionId = sessionId, delete this._state.current[sessionId], this._snapshot(endContext, startContext), sessionId
  1508.                     }
  1509.                     return null
  1510.                 },
  1511.                 flush: function() {
  1512.                     var state = this._state;
  1513.                     if (!state.ending && state.snapshots && state.snapshots.length) {
  1514.                         var envelope = {
  1515.                             currentState: state.current,
  1516.                             reverseDeltas: state.snapshots,
  1517.                             type: "CompactConsolidatedLoggingEnvelope",
  1518.                             version: 2,
  1519.                             clientSendTime: this._timestamp()
  1520.                         };
  1521.                         state.snapshots = [], this.requestSender(JSON.stringify(envelope))
  1522.                     }
  1523.                 },
  1524.                 serialize: function() {
  1525.                     var timer = this._batchTimeout;
  1526.                     this._batchTimeout = null;
  1527.                     var json = JSON.stringify(this);
  1528.                     return this._batchTimeout = timer, json
  1529.                 },
  1530.                 sever: function(severedContext) {
  1531.                     this.end(severedContext || "Severed"), this._init(this)
  1532.                 },
  1533.                 end: function(endingContext) {
  1534.                     endingContext && this.addContext(endingContext), this._state.ending = !0, this._stopBatching();
  1535.                     for (var keys = Object.keys(this._state.current).sort(function(a, b) {
  1536.                             return b - a
  1537.                         }), logId = keys.pop(), i = 0; i < keys.length; i++) {
  1538.                         var context = this._state.current[keys[i]],
  1539.                             types = context.type;
  1540.                         "Session" === types[types.length - 1] && this.endSession(context.id, {
  1541.                             type: "SessionCanceled"
  1542.                         })
  1543.                     }
  1544.                     this.endSession(logId, {
  1545.                         type: "SessionEnded"
  1546.                     }), this._state.ending = !1, this.flush(), this._state = null
  1547.                 },
  1548.                 _init: function(options) {
  1549.                     this._initOptions(options), this._startBatching(), options.existingState || this._startLogSession(), this._logInitializedEvent()
  1550.                 },
  1551.                 _initOptions: function(options) {
  1552.                     options.existingState ? this._restore(options.existingState) : this._initState(), this._initProperties(options)
  1553.                 },
  1554.                 _initState: function() {
  1555.                     var state = {};
  1556.                     state.sequenceNumber = 0, state.lastIncrementingBits = 0, state.pending = {}, state.current = {}, state.snapshots = [], state.currentDelta = [], this._state = state
  1557.                 },
  1558.                 _startLogSession: function() {
  1559.                     this.startSession("Log", {
  1560.                         source: this.source,
  1561.                         schema: {
  1562.                             name: SCHEMA.name,
  1563.                             version: SCHEMA.version
  1564.                         }
  1565.                     })
  1566.                 },
  1567.                 _logInitializedEvent: function() {
  1568.                     this.logEvent("LoggerInitialized", {
  1569.                         version: VERSION
  1570.                     })
  1571.                 },
  1572.                 _restore: function(state) {
  1573.                     for (var existingState = JSON.parse(state), keys = Object.keys(existingState), i = 0; i < keys.length; i++) {
  1574.                         var key = keys[i];
  1575.                         this[key] = existingState[key]
  1576.                     }
  1577.                 },
  1578.                 _initProperties: function(options) {
  1579.                     for (var option in this) "function" != typeof this[option] && options && "_" !== option.charAt(0) && (this[option] = "undefined" != typeof options[option] ? options[option] : this[option])
  1580.                 },
  1581.                 _copyData: function(data) {
  1582.                     var copy = {};
  1583.                     for (var field in data) copy[field] = data[field];
  1584.                     return copy
  1585.                 },
  1586.                 _initContext: function(type, data) {
  1587.                     var context;
  1588.                     return context = data ? this._copyData(data) : {}, context.type = SCHEMA && SCHEMA.types[type[0]] ? SCHEMA.types[type[0]] : type, context.id = this._getNextContextId(), context
  1589.                 },
  1590.                 _initEventContext: function(type, data) {
  1591.                     var context = this._initContext(type, data);
  1592.                     return context.sequence = ++this._state.sequenceNumber, "undefined" == typeof context.time && (context.time = this._timestamp()), context
  1593.                 },
  1594.                 _getClientTime: function() {
  1595.                     return (new Date).getTime()
  1596.                 },
  1597.                 _timestamp: function() {
  1598.                     var getTime = this.getClientTime || this._getClientTime;
  1599.                     return getTime() + this.timeOffset
  1600.                 },
  1601.                 _getNextContextId: function() {
  1602.                     var currentTimeInSeconds = Math.floor(this._timestamp() / 1e3),
  1603.                         incrBitsMask = INCREMENTING_BITS_MASK,
  1604.                         bitsShift = RANDOM_BITS_SHIFT,
  1605.                         incrementingBits = currentTimeInSeconds & incrBitsMask,
  1606.                         randomBits = Math.floor(Math.random() * bitsShift);
  1607.                     return incrementingBits <= this._state.lastIncrementingBits && (incrementingBits = this._state.lastIncrementingBits + 1), this._state.lastIncrementingBits = incrementingBits, incrementingBits * bitsShift + randomBits
  1608.                 },
  1609.                 _snapshot: function() {
  1610.                     for (var count = 1, current = this._state.current, pending = this._state.pending, pendingKeys = Object.keys(pending), i = 0; i < pendingKeys.length; i++) {
  1611.                         var key = pendingKeys[i];
  1612.                         current[key] = pending[key], count++
  1613.                     }
  1614.                     this._state.pending = {}, this._state.currentDelta.push(count), this._state.currentDelta = [], this._state.snapshots.push(this._state.currentDelta), arguments.length && this._state.currentDelta.push.apply(this._state.currentDelta, arguments), this._state.snapshots.length >= this.batchSize && this.flush()
  1615.                 },
  1616.                 _startBatching: function() {
  1617.                     var self = this;
  1618.                     self._batchTimeout = setTimeout(function() {
  1619.                         self.flush(), self._startBatching()
  1620.                     }, self.batchInterval)
  1621.                 },
  1622.                 _stopBatching: function() {
  1623.                     clearTimeout(this._batchTimeout), this._batchTimeout = null
  1624.                 }
  1625.             }, module.exports = Logger
  1626.         }), require.define("/node_modules/nf-cl-schema-ui/package.json", function(require, module) {
  1627.             module.exports = {
  1628.                 main: "dist/schema/nf-cl-schema-netflixApp.js"
  1629.             }
  1630.         }), require.define("/node_modules/nf-cl-schema-ui/dist/schema/nf-cl-schema-netflixApp.js", function(require, module) {
  1631.             module.exports = {
  1632.                 version: "1.19.0",
  1633.                 name: "netflixApp",
  1634.                 types: {
  1635.                     AcceptTermsOfUse: ["AcceptTermsOfUse", "Action", "Session"],
  1636.                     AdaptiveEcomFallbackExperience: ["AdaptiveEcomFallbackExperience", "FallbackExperience"],
  1637.                     AddCachedVideo: ["AddCachedVideo", "Action", "Session"],
  1638.                     AddCachedVideoCommand: ["AddCachedVideoCommand", "Command", "Session"],
  1639.                     AddProfile: ["AddProfile", "Action", "Session"],
  1640.                     AddToPlaylist: ["AddToPlaylist", "Action", "Session"],
  1641.                     AddToPlaylistCommand: ["AddToPlaylistCommand", "Command", "Session"],
  1642.                     BackCommand: ["BackCommand", "Command", "Session"],
  1643.                     BoxartRenderCanceled: ["BoxartRenderCanceled", "BoxartRenderEnded", "DiscreteEvent"],
  1644.                     BoxartRenderFailed: ["BoxartRenderFailed", "BoxartRenderEnded", "DiscreteEvent"],
  1645.                     CachedPlay: ["CachedPlay", "Play", "Action", "Session"],
  1646.                     CancelCommand: ["CancelCommand", "Command", "Session"],
  1647.                     CancelMembership: ["CancelMembership", "Action", "Session"],
  1648.                     ChangeValueCommand: ["ChangeValueCommand", "Command", "Session"],
  1649.                     CloseApp: ["CloseApp", "Action", "Session"],
  1650.                     CloseAppCommand: ["CloseAppCommand", "Command", "Session"],
  1651.                     CloseCommand: ["CloseCommand", "Command", "Session"],
  1652.                     ConnectWithLineAccount: ["ConnectWithLineAccount", "Action", "Session"],
  1653.                     CreateAccount: ["CreateAccount", "Action", "Session"],
  1654.                     DeepLinkInput: ["DeepLinkInput", "UserInput"],
  1655.                     DeleteProfile: ["DeleteProfile", "Action", "Session"],
  1656.                     DirectedGestureInput: ["DirectedGestureInput", "GestureInput", "UserInput"],
  1657.                     Download: ["Download", "Action", "Session"],
  1658.                     EditPaymentCommand: ["EditPaymentCommand", "Command", "Session"],
  1659.                     EditPlanCommand: ["EditPlanCommand", "Command", "Session"],
  1660.                     EditProfile: ["EditProfile", "Action", "Session"],
  1661.                     EnterFullscreenCommand: ["EnterFullscreenCommand", "Command", "Session"],
  1662.                     EnterKidsModeCommand: ["EnterKidsModeCommand", "Command", "Session"],
  1663.                     ExitFullscreenCommand: ["ExitFullscreenCommand", "Command", "Session"],
  1664.                     ExitKidsModeCommand: ["ExitKidsModeCommand", "Command", "Session"],
  1665.                     FastForwardCommand: ["FastForwardCommand", "TrickplayCommand", "Command", "Session"],
  1666.                     FillVideoCommand: ["FillVideoCommand", "Command", "Session"],
  1667.                     FitVideoCommand: ["FitVideoCommand", "Command", "Session"],
  1668.                     ForwardCommand: ["ForwardCommand", "Command", "Session"],
  1669.                     GestureInput: ["GestureInput", "UserInput"],
  1670.                     HomeCommand: ["HomeCommand", "Command", "Session"],
  1671.                     KeyboardInput: ["KeyboardInput", "UserInput"],
  1672.                     LolomoDataModel: ["LolomoDataModel", "DataModel"],
  1673.                     MobileConnection: ["MobileConnection", "NetworkConnection"],
  1674.                     MuteCommand: ["MuteCommand", "Command", "Session"],
  1675.                     Navigate: ["Navigate", "Action", "Session"],
  1676.                     NavigateBackward: ["NavigateBackward", "Navigate", "Action", "Session"],
  1677.                     NavigateForward: ["NavigateForward", "Navigate", "Action", "Session"],
  1678.                     NetflixId: ["NetflixId", "ProfileIdentity", "Session"],
  1679.                     NotifyUms: ["NotifyUms", "Action", "Session"],
  1680.                     PauseCommand: ["PauseCommand", "TrickplayCommand", "Command", "Session"],
  1681.                     PauseDownloadCommand: ["PauseDownloadCommand", "Command", "Session"],
  1682.                     Play: ["Play", "Action", "Session"],
  1683.                     PlayCommand: ["PlayCommand", "Command", "Session"],
  1684.                     PlayNextCommand: ["PlayNextCommand", "Command", "Session"],
  1685.                     PointerInput: ["PointerInput", "UserInput"],
  1686.                     PrepareOnramp: ["PrepareOnramp", "Action", "Session"],
  1687.                     PreparePlay: ["PreparePlay", "Action", "Session"],
  1688.                     ProcessStateTransition: ["ProcessStateTransition", "Action", "Session"],
  1689.                     ProfileGuid: ["ProfileGuid", "ProfileIdentity", "Session"],
  1690.                     PushNotificationAcknowledged: ["PushNotificationAcknowledged", "PushNotificationResolved", "DiscreteEvent"],
  1691.                     PushNotificationDismissed: ["PushNotificationDismissed", "PushNotificationAcknowledged", "PushNotificationResolved", "DiscreteEvent"],
  1692.                     PushNotificationIgnored: ["PushNotificationIgnored", "PushNotificationResolved", "DiscreteEvent"],
  1693.                     RedeemGiftCard: ["RedeemGiftCard", "Action", "Session"],
  1694.                     RedeemGiftCardCommand: ["RedeemGiftCardCommand", "Command", "Session"],
  1695.                     RegisterForPushNotifications: ["RegisterForPushNotifications", "Action", "Session"],
  1696.                     RemoveAllCachedVideosCommand: ["RemoveAllCachedVideosCommand", "Command", "Session"],
  1697.                     RemoveCachedVideo: ["RemoveCachedVideo", "Action", "Session"],
  1698.                     RemoveCachedVideoAndPlayNextCommand: ["RemoveCachedVideoAndPlayNextCommand", "Command", "Session"],
  1699.                     RemoveCachedVideoCommand: ["RemoveCachedVideoCommand", "Command", "Session"],
  1700.                     RemoveDownloadDevice: ["RemoveDownloadDevice", "Action", "Session"],
  1701.                     RemoveFromPlaylist: ["RemoveFromPlaylist", "Action", "Session"],
  1702.                     RemoveFromPlaylistCommand: ["RemoveFromPlaylistCommand", "Command", "Session"],
  1703.                     RemoveFromViewingActivity: ["RemoveFromViewingActivity", "Action", "Session"],
  1704.                     RenderNavigationLevel: ["RenderNavigationLevel", "Action", "Session"],
  1705.                     RequestSharedCredentials: ["RequestSharedCredentials", "Action", "Session"],
  1706.                     ResumeDownloadCommand: ["ResumeDownloadCommand", "Command", "Session"],
  1707.                     RetryDownloadCommand: ["RetryDownloadCommand", "Command", "Session"],
  1708.                     RewindCommand: ["RewindCommand", "TrickplayCommand", "Command", "Session"],
  1709.                     Search: ["Search", "Action", "Session"],
  1710.                     SearchCommand: ["SearchCommand", "Command", "Session"],
  1711.                     SearchSuggestionResults: ["SearchSuggestionResults", "DataModel"],
  1712.                     SearchSuggestionTitleResults: ["SearchSuggestionTitleResults", "DataModel"],
  1713.                     SearchTitleResults: ["SearchTitleResults", "DataModel"],
  1714.                     SeekCommand: ["SeekCommand", "TrickplayCommand", "Command", "Session"],
  1715.                     SelectCommand: ["SelectCommand", "Command", "Session"],
  1716.                     SelectPlan: ["SelectPlan", "Action", "Session"],
  1717.                     SelectProfile: ["SelectProfile", "Action", "Session"],
  1718.                     SetStarRating: ["SetStarRating", "Action", "Session"],
  1719.                     SetThumbRating: ["SetThumbRating", "Action", "Session"],
  1720.                     SeveredForVppa: ["SeveredForVppa", "Severed"],
  1721.                     SeveredForWebpageUnload: ["SeveredForWebpageUnload", "Severed"],
  1722.                     Share: ["Share", "Action", "Session"],
  1723.                     ShareCommand: ["ShareCommand", "Command", "Session"],
  1724.                     SignIn: ["SignIn", "Action", "Session"],
  1725.                     SignInCommand: ["SignInCommand", "Command", "Session"],
  1726.                     SignOut: ["SignOut", "Action", "Session"],
  1727.                     SignOutCommand: ["SignOutCommand", "Command", "Session"],
  1728.                     SignUpCommand: ["SignUpCommand", "Command", "Session"],
  1729.                     SkipAheadCommand: ["SkipAheadCommand", "TrickplayCommand", "Command", "Session"],
  1730.                     SkipBackCommand: ["SkipBackCommand", "TrickplayCommand", "Command", "Session"],
  1731.                     SkipCommand: ["SkipCommand", "Command", "Session"],
  1732.                     StartAppExperience: ["StartAppExperience", "Action", "Session"],
  1733.                     StartMembership: ["StartMembership", "Action", "Session"],
  1734.                     StartMembershipCommand: ["StartMembershipCommand", "Command", "Session"],
  1735.                     StartPlay: ["StartPlay", "Action", "Session"],
  1736.                     StoreSharedCredentials: ["StoreSharedCredentials", "Action", "Session"],
  1737.                     SubmitCommand: ["SubmitCommand", "Command", "Session"],
  1738.                     SubmitOnrampResults: ["SubmitOnrampResults", "Action", "Session"],
  1739.                     ThrottleSearch: ["ThrottleSearch", "Action", "Session"],
  1740.                     UnmuteCommand: ["UnmuteCommand", "Command", "Session"],
  1741.                     UnpauseCommand: ["UnpauseCommand", "TrickplayCommand", "Command", "Session"],
  1742.                     UpdatePaymentInfo: ["UpdatePaymentInfo", "Action", "Session"],
  1743.                     ValidateInput: ["ValidateInput", "Action", "Session"],
  1744.                     ValidateMemberId: ["ValidateMemberId", "Action", "Session"],
  1745.                     ValidatePin: ["ValidatePin", "Action", "Session"],
  1746.                     ViewAccountMenuCommand: ["ViewAccountMenuCommand", "Command", "Session"],
  1747.                     ViewAudioSubtitlesSelectorCommand: ["ViewAudioSubtitlesSelectorCommand", "Command", "Session"],
  1748.                     ViewCachedVideosCommand: ["ViewCachedVideosCommand", "Command", "Session"],
  1749.                     ViewCategoriesCommand: ["ViewCategoriesCommand", "Command", "Session"],
  1750.                     ViewDetailsCommand: ["ViewDetailsCommand", "Command", "Session"],
  1751.                     ViewEpisodesSelectorCommand: ["ViewEpisodesSelectorCommand", "Command", "Session"],
  1752.                     ViewPreviewsCommand: ["ViewPreviewsCommand", "Command", "Session"],
  1753.                     ViewProfilesCommand: ["ViewProfilesCommand", "Command", "Session"],
  1754.                     ViewSettingsCommand: ["ViewSettingsCommand", "Command", "Session"],
  1755.                     ViewTitlesCommand: ["ViewTitlesCommand", "Command", "Session"],
  1756.                     VisitorDeviceId: ["VisitorDeviceId", "AccountIdentity", "Session"],
  1757.                     VoiceInput: ["VoiceInput", "UserInput"],
  1758.                     WatchCreditsCommand: ["WatchCreditsCommand", "Command", "Session"],
  1759.                     WifiConnection: ["WifiConnection", "NetworkConnection"],
  1760.                     WiredConnection: ["WiredConnection", "NetworkConnection"],
  1761.                     "android.SystemBackCommand": ["android.SystemBackCommand", "Command", "Session"],
  1762.                     "cs.Call": ["cs.Call", "Action", "Session"],
  1763.                     "cs.CallCommand": ["cs.CallCommand", "Command", "Session"],
  1764.                     "cs.EndCallCommand": ["cs.EndCallCommand", "Command", "Session"],
  1765.                     "edx.AlertsOperation": ["edx.AlertsOperation", "edx.ApiOperation", "Action", "Session"],
  1766.                     "edx.ApiOperation": ["edx.ApiOperation", "Action", "Session"],
  1767.                     "edx.AtlasOperation": ["edx.AtlasOperation", "edx.ApiOperation", "Action", "Session"],
  1768.                     "edx.ChronosOperation": ["edx.ChronosOperation", "edx.ApiOperation", "Action", "Session"],
  1769.                     "edx.CommandLineInterface": ["edx.CommandLineInterface", "Action", "Session"],
  1770.                     "edx.DashboardsOperation": ["edx.DashboardsOperation", "edx.ApiOperation", "Action", "Session"],
  1771.                     "edx.ElasticSearchOperation": ["edx.ElasticSearchOperation", "edx.ApiOperation", "Action", "Session"],
  1772.                     "edx.GitOperation": ["edx.GitOperation", "edx.ApiOperation", "Action", "Session"],
  1773.                     "edx.HttpRequest": ["edx.HttpRequest", "Action", "Session"],
  1774.                     "edx.KeymasterOperation": ["edx.KeymasterOperation", "edx.ApiOperation", "Action", "Session"],
  1775.                     "edx.MantisOperation": ["edx.MantisOperation", "edx.ApiOperation", "Action", "Session"],
  1776.                     "edx.NodeQuarkIndexOperation": ["edx.NodeQuarkIndexOperation", "edx.ApiOperation", "Action", "Session"],
  1777.                     "edx.PagerDutyOperation": ["edx.PagerDutyOperation", "edx.ApiOperation", "Action", "Session"],
  1778.                     "edx.PrimerIndexOperation": ["edx.PrimerIndexOperation", "edx.ApiOperation", "Action", "Session"],
  1779.                     "edx.PrimerOperation": ["edx.PrimerOperation", "edx.ApiOperation", "Action", "Session"],
  1780.                     "edx.RavenOperation": ["edx.RavenOperation", "edx.ApiOperation", "Action", "Session"],
  1781.                     "edx.SkipperOperation": ["edx.SkipperOperation", "edx.ApiOperation", "Action", "Session"],
  1782.                     "edx.SpinnakerOperation": ["edx.SpinnakerOperation", "edx.ApiOperation", "Action", "Session"],
  1783.                     "edx.TitusOperation": ["edx.TitusOperation", "edx.ApiOperation", "Action", "Session"],
  1784.                     "iko.EndCommand": ["iko.EndCommand", "Command", "Session"],
  1785.                     "iko.EnterBattleCommand": ["iko.EnterBattleCommand", "Command", "Session"],
  1786.                     "iko.Presentation": ["iko.Presentation", "Presentation", "Session"],
  1787.                     "ios.DeepLinkInput": ["ios.DeepLinkInput", "UserInput"],
  1788.                     "ios.LoadConfigurationService": ["ios.LoadConfigurationService", "Action", "Session"],
  1789.                     "ios.LoadDownloadService": ["ios.LoadDownloadService", "Action", "Session"],
  1790.                     "ios.LoadIdentityService": ["ios.LoadIdentityService", "Action", "Session"],
  1791.                     "ios.LoadNrdService": ["ios.LoadNrdService", "Action", "Session"],
  1792.                     "ios.RegisterForPushNotifications": ["ios.RegisterForPushNotifications", "Action", "Session"],
  1793.                     "tvui.JankMeasurementReported": ["tvui.JankMeasurementReported", "MeasurementReported", "DiscreteEvent"],
  1794.                     "tvui.MetadataDownloadPlayDelay": ["tvui.MetadataDownloadPlayDelay", "tvui.PlayDelay", "Session"],
  1795.                     "tvui.PlatformPlayDelay": ["tvui.PlatformPlayDelay", "tvui.PlayDelay", "Session"],
  1796.                     "tvui.RequestImeCandidateList": ["tvui.RequestImeCandidateList", "Action", "Session"],
  1797.                     "tvui.UiPlayDelay": ["tvui.UiPlayDelay", "tvui.PlayDelay", "Session"],
  1798.                     "tvui.VideoPresentationPlayDelay": ["tvui.VideoPresentationPlayDelay", "tvui.PlayDelay", "Session"],
  1799.                     "www.ExtendedAreaFocus": ["www.ExtendedAreaFocus", "Focus", "Session"]
  1800.                 }
  1801.             }
  1802.         }), require.define("/app/ui.js", function(require, module) {
  1803.             function convertSpeed(result) {
  1804.                 var bitsPerS = result.speed,
  1805.                     speedMbs = bitsPerS / 1e3 / 1e3 || 0,
  1806.                     units = "Mbps";
  1807.                 return 1 > speedMbs ? (speedMbs *= 1e3, units = "Kbps") : speedMbs >= 995 && (speedMbs /= 1e3, units = "Gbps"), speedMbs = 9.95 > speedMbs ? (Math.round(10 * speedMbs) / 10).toFixed(1) : 100 > speedMbs ? Math.round(speedMbs) : 10 * Math.round(speedMbs / 10), {
  1808.                     speed: speedMbs,
  1809.                     units: units
  1810.                 }
  1811.             }
  1812.  
  1813.             function convertToMB(result) {
  1814.                 var mb = result.bytes / 1e3 / 1e3;
  1815.                 return mb = mb > 1 ? 10 > mb ? (Math.round(10 * mb) / 10).toFixed(1) : 10 * Math.round(mb / 10) : (Math.round(100 * mb) / 100).toFixed(2)
  1816.             }
  1817.  
  1818.             function convertLatency(result) {
  1819.                 var units = "ms",
  1820.                     latency = result.value;
  1821.                 return 0 !== latency && !latency && result.latency && (latency = result.latency.value), 0 !== latency && !latency || latency > 1e6 ? {
  1822.                     speed: 0,
  1823.                     units: units
  1824.                 } : (latency >= 1e3 ? (latency = (Math.round(latency / 1e3 * 10) / 10).toFixed(1), units = "s") : latency = Math.round(latency), {
  1825.                     speed: latency,
  1826.                     units: units
  1827.                 })
  1828.             }
  1829.             var TEST_CONFIG = {
  1830.                     showAdvanced: {
  1831.                         "default": !1,
  1832.                         elem: "always-show-metrics-input"
  1833.                     },
  1834.                     measureUploadLatency: {
  1835.                         "default": !1,
  1836.                         elem: "measure-latency-during-upload"
  1837.                     },
  1838.                     minConnections: {
  1839.                         "default": 1,
  1840.                         testerFunc: function(config) {
  1841.                             return config.connections.min
  1842.                         },
  1843.                         elem: "min-connections-input"
  1844.                     },
  1845.                     maxConnections: {
  1846.                         "default": 8,
  1847.                         testerFunc: function(config) {
  1848.                             return config.connections.max
  1849.                         },
  1850.                         elem: "max-connections-input"
  1851.                     },
  1852.                     minDuration: {
  1853.                         "default": 5,
  1854.                         testerFunc: function(config) {
  1855.                             return config.duration.min
  1856.                         },
  1857.                         elem: "min-duration-input"
  1858.                     },
  1859.                     maxDuration: {
  1860.                         "default": 30,
  1861.                         testerFunc: function(config) {
  1862.                             return config.duration.max
  1863.                         },
  1864.                         elem: "max-duration-input"
  1865.                     },
  1866.                     shouldPersist: {
  1867.                         "default": !1,
  1868.                         elem: "persist-config-input"
  1869.                     }
  1870.                 },
  1871.                 ui = function(tester, logger) {
  1872.                     function getElement(elementId) {
  1873.                         var element;
  1874.                         return element || (elemCache[elementId] = element = document.getElementById(elementId)), element
  1875.                     }
  1876.  
  1877.                     function hideElement(element) {
  1878.                         return element.setAttribute("style", "display: none"), element
  1879.                     }
  1880.  
  1881.                     function showElement(element) {
  1882.                         return element.setAttribute("style", "display: block"), element
  1883.                     }
  1884.  
  1885.                     function removeClass(element, className) {
  1886.                         return element.className = element.className.replace(new RegExp("(\\s|^)" + className + "(\\s|$)", "g"), " "), element
  1887.                     }
  1888.  
  1889.                     function addClass(element, className) {
  1890.                         element.className;
  1891.                         return hasClass(element, className) || (element.className += " " + className), element
  1892.                     }
  1893.  
  1894.                     function hasClass(element, className) {
  1895.                         var curClass = element.className;
  1896.                         return -1 !== curClass.search(new RegExp("(\\s|^)" + className + "(\\s|$)"))
  1897.                     }
  1898.  
  1899.                     function toggleClass(element, className) {
  1900.                         hasClass(element, className) ? removeClass(element, className) : addClass(element, className)
  1901.                     }
  1902.  
  1903.                     function localizePage(language, localized) {
  1904.                         var i, key, elem, localizedElems = document.getElementsByClassName("localized"),
  1905.                             alignElems = document.getElementsByClassName("align-container"),
  1906.                             localizedLang = localized[language],
  1907.                             rightAligned = localizedLang.rightAligned,
  1908.                             messageContentElem = getElement("share-msg");
  1909.                         for (i = 0; i < localizedElems.length; i++) elem = localizedElems[i], key = elem.getAttribute("loc-str"), elem.innerHTML = localizedLang[key];
  1910.                         for (messageContentElem.setAttribute("my-speed", localizedLang.my_internet_speed), messageContentElem.setAttribute("your-speed", localizedLang.your_internet_speed), messageContentElem.setAttribute("yours-speed", localizedLang.how_fast_is_yours), messageContentElem.setAttribute("share-on-fb", localizedLang.share_on_facebook), messageContentElem.setAttribute("share-on-tw", localizedLang.share_on_twitter), i = 0; i < alignElems.length; ++i) elem = alignElems[i], rightAligned ? addClass(elem, "right-aligned-text") : removeClass(elem, "right-aligned-text")
  1911.                     }
  1912.  
  1913.                     function setupHelp() {
  1914.                         {
  1915.                             var testHelpElem = getElement("test-help-btn"),
  1916.                                 helpContentElem = getElement("help-content");
  1917.                             getElement("language-selector-container")
  1918.                         }
  1919.                         utils.addEventListener(testHelpElem, "click", function() {
  1920.                             testHelpElem.active ? (removeClass(testHelpElem.children[0], "active"), testHelpElem.active = !1, hideElement(helpContentElem)) : (addClass(testHelpElem.children[0], "active"), testHelpElem.active = !0, showElement(helpContentElem), setTimeout(function() {
  1921.                                 scrollToElement(testHelpElem)
  1922.                             }, 0), logger.logEvent("HelpPresented", {
  1923.                                 view: "Help"
  1924.                             }))
  1925.                         })
  1926.                     }
  1927.  
  1928.                     function setupLanguageControls() {
  1929.                         function addLanguageChangeListener(elem) {
  1930.                             utils.addEventListener(elem, "click", function(event) {
  1931.                                 event.preventDefault();
  1932.                                 var language = elem.innerText,
  1933.                                     langPath = elem.getAttribute("language");
  1934.                                 if (window.localized) localizePage(langPath, window.localized), utils.removeEventListener(languageSelectorBtn, toggleSelector), languageSelectorBtn.innerHTML = languageSelectorBtn.innerHTML.replace(languageSelectorBtn.innerText, language), utils.addEventListener(languageSelectorBtn, "click", toggleSelector), curLang = langPath;
  1935.                                 else {
  1936.                                     var xhr = utils.getXHR(),
  1937.                                         path = localizedPath;
  1938.                                     xhr.onreadystatechange = function() {
  1939.                                         4 !== this.readyState || 200 !== this.status && 304 !== this.status || (window.localized = JSON.parse(this.responseText), localizePage(langPath, window.localized), utils.removeEventListener(languageSelectorBtn, toggleSelector), languageSelectorBtn.innerHTML = languageSelectorBtn.innerHTML.replace(languageSelectorBtn.innerText, language), utils.addEventListener(languageSelectorBtn, "click", toggleSelector), curLang = langPath)
  1940.                                     }, (!window.location || "localhost" != window.location.hostname && "https:" !== window.location.protocol) && (path = "https://fast.com" + path), xhr.open("GET", path, !0), xhr.onerror = function(e) {}, logger.logEvent("LanguageChangeStart", {
  1941.                                         from: curLang,
  1942.                                         to: langPath
  1943.                                     }), setTimeout(function() {
  1944.                                         xhr && xhr.send()
  1945.                                     }, 0)
  1946.                                 }
  1947.                             })
  1948.                         }
  1949.  
  1950.                         function toggleSelector() {
  1951.                             var languageSelector = getElement("language-selector"),
  1952.                                 languageSelectorIcon = getElement("language-selector-icon");
  1953.                             toggleClass(languageSelector, "show"), toggleClass(languageSelectorIcon, "oc-icon-keyboard_arrow_down"), toggleClass(languageSelectorIcon, "oc-icon-keyboard_arrow_up")
  1954.                         }
  1955.                         var localizedPath = "/localized.json";
  1956.                         window && window.document && window.document.body && (localizedPath = window.document.body.getAttribute("localized")); {
  1957.                             var languageSelectorBtn = getElement("language-selector-btn");
  1958.                             getElement("help-content"), getElement("your-speed-message"), getElement("compare-on")
  1959.                         }
  1960.                         utils.addEventListener(languageSelectorBtn, "click", toggleSelector), utils.addEventListener(document, "click", function(event) {
  1961.                             var target = event.target || event.srcElement,
  1962.                                 languageSelector = getElement("language-selector");
  1963.                             hasClass(target, "dropbtn") || hasClass(languageSelector, "show") && toggleSelector()
  1964.                         });
  1965.                         var elem, elemInd, languageOptions = document.querySelectorAll(".language-option");
  1966.                         for (elemInd = 0; elemInd < languageOptions.length; ++elemInd) elem = languageOptions[elemInd], addLanguageChangeListener(elem)
  1967.                     }
  1968.  
  1969.                     function pause() {
  1970.                         tester.isRunning() && tester.stop(), utils.addEventListener(pauseElem, "click", restartTest)
  1971.                     }
  1972.  
  1973.                     function setupPause() {
  1974.                         utils.removeEventListener(pauseElem, "click", restartTest), utils.removeEventListener(pauseElem, "click", pause), utils.addEventListener(pauseElem, "click", pause)
  1975.                     }
  1976.  
  1977.                     function restartTest() {
  1978.                         tester.isRunning() && tester.stop(), setupPause(), reset(), tester.download()
  1979.                     }
  1980.  
  1981.                     function setupAfterTestActions() {
  1982.                         var actionsElem = getElement("after-test-actions"),
  1983.                             speedMsg = getElement("your-speed-message");
  1984.                         showElement(actionsElem), showElement(speedMsg), showElement(resultsExplanationElem), showingMoreDetails || showElement(showMoreDetailsBtn)
  1985.                     }
  1986.  
  1987.                     function reset() {
  1988.                         var speedElem = getElement("speed-value"),
  1989.                             speedUnitsElem = getElement("speed-units"),
  1990.                             speedProgressIndicator = getElement("speed-progress-indicator"),
  1991.                             speedProgressIndicatorIcon = getElement("speed-progress-indicator-icon"),
  1992.                             actionsElem = getElement("after-test-actions"),
  1993.                             unstableResultsElem = getElement("unstable-results-msg"),
  1994.                             testErrorElem = getElement("error-results-msg"),
  1995.                             speedMsg = getElement("your-speed-message"),
  1996.                             infoElem = getElement("test-info-container"),
  1997.                             latencyElem = getElement("latency-value"),
  1998.                             latencyUnitsElem = getElement("latency-units"),
  1999.                             latencyLabelElem = getElement("latency-label"),
  2000.                             bufferbloatElem = getElement("bufferbloat-value"),
  2001.                             bufferbloatUnitsElem = getElement("bufferbloat-units"),
  2002.                             bufferbloatLabelElem = getElement("bufferbloat-label"),
  2003.                             bytesDownElem = getElement("down-mb-value"),
  2004.                             bytesUpElem = getElement("up-mb-value"),
  2005.                             uploadElem = getElement("upload-value"),
  2006.                             uploadUnitsElem = getElement("upload-units"),
  2007.                             uploadLabelElem = getElement("upload-label");
  2008.                         hideElement(actionsElem), hideElement(unstableResultsElem), hideElement(testErrorElem), hideElement(speedMsg), hideElement(showMoreDetailsBtn), speedElem.innerHTML = 0, speedUnitsElem.innerHTML = "&nbsp", removeClass(detailsElem, "succeeded"), removeClass(latencyContainer, "succeeded"), removeClass(speedElem, "succeeded"), removeClass(speedElem, "failed"), removeClass(speedUnitsElem, "succeeded"), removeClass(speedUnitsElem, "failed"), removeClass(speedProgressIndicator, "succeeded"), removeClass(speedProgressIndicator, "stopped"), removeClass(speedProgressIndicator, "failed"), addClass(speedProgressIndicator, "in-progress"), removeClass(speedProgressIndicatorIcon, "oc-icon-refresh"), addClass(speedProgressIndicatorIcon, "oc-icon-pause"), removeClass(infoElem, "succeeded"), removeClass(infoElem, "failed"), removeClass(latencyElem, "succeeded"), removeClass(latencyElem, "failed"), removeClass(latencyUnitsElem, "succeeded"), removeClass(latencyUnitsElem, "failed"), removeClass(latencyLabelElem, "succeeded"), removeClass(latencyLabelElem, "failed"), removeClass(bufferbloatElem, "succeeded"), removeClass(bufferbloatElem, "failed"), removeClass(bufferbloatUnitsElem, "succeeded"), removeClass(bufferbloatUnitsElem, "failed"), removeClass(bufferbloatLabelElem, "succeeded"), removeClass(bufferbloatLabelElem, "failed"), removeClass(uploadElem, "succeeded"), removeClass(uploadElem, "failed"), removeClass(uploadUnitsElem, "succeeded"), removeClass(uploadUnitsElem, "failed"), removeClass(uploadLabelElem, "succeeded"), removeClass(uploadLabelElem, "failed"), latencyElem.innerHTML = "0", bufferbloatElem.innerHTML = "0", uploadElem.innerHTML = "0", bytesDownElem.innerHTML = "0", bytesUpElem.innerHTML = "0"
  2009.                     }
  2010.  
  2011.                     function scrollToElement(elem) {
  2012.                         function findPos(obj) {
  2013.                             var curtop = 0;
  2014.                             if (obj.offsetParent) {
  2015.                                 do curtop += obj.offsetTop; while (obj == obj.offsetParent);
  2016.                                 return curtop
  2017.                             }
  2018.                         }
  2019.                         elem.scrollIntoView ? elem.scrollIntoView() : window.scroll(0, findPos(elem))
  2020.                     }
  2021.  
  2022.                     function popupCenter(url, title, w, h) {
  2023.                         var dualScreenLeft = void 0 !== window.screenLeft ? window.screenLeft : screen.left,
  2024.                             dualScreenTop = void 0 !== window.screenTop ? window.screenTop : screen.top,
  2025.                             width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width,
  2026.                             height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height,
  2027.                             left = width / 2 - w / 2 + dualScreenLeft,
  2028.                             top = height / 2 - h / 2 + dualScreenTop,
  2029.                             newWindow = window.open(url, title, "scrollbars=yes, width=" + w + ", height=" + h + ", top=" + top + ", left=" + left);
  2030.                         newWindow.focus && newWindow.focus()
  2031.                     }
  2032.                     var fbShareEventListener, twShareEventListener, fbShareBtn, twShareBtn, showMoreDetailsBtn, testAgainBtn, resultsExplanationElem, configContainerElem, detailsElem, latencyContainer, pauseElem, downloadResult, showingMoreDetails, config, elemCache = {},
  2033.                         utils = require("./utils"),
  2034.                         events = require("./event").events,
  2035.                         curLang = document.body.getAttribute("language"),
  2036.                         canUseLocalStorage = !1;
  2037.                     try {
  2038.                         window && window.localStorage && (window.localStorage.__test__ = 1, canUseLocalStorage = !0)
  2039.                     } catch (e) {}
  2040.                     var showMoreDetails = function() {
  2041.                             if (logger.logEvent("AdvancedMetricsPresented"), tester.isRunning()) {
  2042.                                 var speedProgressIndicator = getElement("speed-progress-indicator"),
  2043.                                     speedProgressIndicatorIcon = getElement("speed-progress-indicator-icon");
  2044.                                 removeClass(speedProgressIndicator, "succeeded"), removeClass(speedProgressIndicator, "stopped"), removeClass(speedProgressIndicator, "failed"), addClass(speedProgressIndicator, "in-progress"), removeClass(speedProgressIndicatorIcon, "oc-icon-refresh"), addClass(speedProgressIndicatorIcon, "oc-icon-pause"), setupPause()
  2045.                             }
  2046.                             showingMoreDetails = !0, hideElement(showMoreDetailsBtn), showElement(detailsElem)
  2047.                         },
  2048.                         hideMoreDetails = function() {
  2049.                             hideElement(detailsElem), showElement(showMoreDetailsBtn), showingMoreDetails = !1
  2050.                         },
  2051.                         updateClientInfo = function(event) {
  2052.                             var locationsStr, curLocation, i, client = event.client,
  2053.                                 locationsMap = {};
  2054.                             for (getElement("user-ip").innerHTML = client.ip, getElement("user-isp").innerHTML = client.isp ? client.isp.replace(/_/g, " ") : "", getElement("user-location").innerHTML = client.location.city + ", " + client.location.country, curLocation = client.servers[0].location.city + ", " + client.servers[0].location.country, locationsStr = curLocation, locationsMap[curLocation] = !0, i = 1; i < Math.min(client.servers.length, 3); ++i) curLocation = client.servers[i].location.city + ", " + client.servers[i].location.country, locationsMap[curLocation] || (locationsStr += "&nbsp&nbsp|&nbsp&nbsp" + curLocation, locationsMap[curLocation] = !0);
  2055.                             getElement("server-locations").innerHTML = locationsStr
  2056.                         },
  2057.                         showConfig = function() {
  2058.                             logger.logEvent("SettingsPresented"), renderConfig(config), hideElement(detailsElem), showElement(configContainerElem)
  2059.                         },
  2060.                         cancelConfig = function() {
  2061.                             hideElement(configContainerElem), config.showAdvanced === !0 || "true" === config.showAdvanced ? showMoreDetails() : hideMoreDetails()
  2062.                         },
  2063.                         applyConfig = function() {
  2064.                             var currentConfig = getTestSettings(tester),
  2065.                                 shouldPersist = getElement(TEST_CONFIG.shouldPersist.elem).checked;
  2066.                             for (var name in TEST_CONFIG) {
  2067.                                 var testerValue = getElement(TEST_CONFIG[name].elem);
  2068.                                 testerValue && (config[name] = "boolean" == typeof TEST_CONFIG[name].default ? testerValue.checked : testerValue.value, shouldPersist && canUseLocalStorage && (window.localStorage[name] = String(config[name])))
  2069.                             }
  2070.                             try {
  2071.                                 applyTesterConfig(config), hideElement(configContainerElem), config.showAdvanced === !0 || "true" === config.showAdvanced ? showMoreDetails() : hideMoreDetails(), restartTest()
  2072.                             } catch (e) {
  2073.                                 alert(e.message), renderConfig(currentConfig)
  2074.                             }
  2075.                         },
  2076.                         resetConfig = function() {
  2077.                             var defaultConfig = {};
  2078.                             for (var name in TEST_CONFIG) defaultConfig[name] = TEST_CONFIG[name].default, canUseLocalStorage && delete window.localStorage[name];
  2079.                             applyTesterConfig(defaultConfig), config = getTestSettings(tester), renderConfig(config)
  2080.                         },
  2081.                         renderConfig = function(config) {
  2082.                             for (var name in TEST_CONFIG) {
  2083.                                 var testerValue = getElement(TEST_CONFIG[name].elem);
  2084.                                 "boolean" == typeof TEST_CONFIG[name].default ? testerValue.checked = config[name] === !0 || "true" === config[name] : testerValue.value = config[name]
  2085.                             }
  2086.                         },
  2087.                         getTestSettings = function(tester) {
  2088.                             var config = {},
  2089.                                 testerConfig = tester.config();
  2090.                             if (canUseLocalStorage)
  2091.                                 for (var name in TEST_CONFIG) {
  2092.                                     var testerValue;
  2093.                                     TEST_CONFIG[name].testerFunc && (testerValue = TEST_CONFIG[name].testerFunc(testerConfig)), config[name] = window.localStorage[name] || testerValue || TEST_CONFIG[name].default
  2094.                                 }
  2095.                             return config
  2096.                         },
  2097.                         applyTesterConfig = function(config) {
  2098.                             var testerConfig = {};
  2099.                             return testerConfig.connections = {
  2100.                                 min: parseInt(config.minConnections),
  2101.                                 max: parseInt(config.maxConnections)
  2102.                             }, testerConfig.duration = {
  2103.                                 min: parseInt(config.minDuration),
  2104.                                 max: parseInt(config.maxDuration)
  2105.                             }, tester.setConfig(testerConfig), testerConfig
  2106.                         },
  2107.                         that = {
  2108.                             onProgress: function(result) {
  2109.                                 var updateMetric = function(testType) {
  2110.                                     var speedElemId, speedUnitId, speedData, speed, speedElem, speedUnitsElem, bytesElemId, bytesElem, convertFunc;
  2111.                                     "download" == testType ? (speedElemId = "speed-value", bytesElemId = "down-mb-value", speedUnitId = "speed-units", convertFunc = convertSpeed) : "upload" == testType ? (speedElemId = "upload-value", bytesElemId = "up-mb-value", speedUnitId = "upload-units", convertFunc = convertSpeed) : "latency" == testType ? (speedElemId = "latency-value", speedUnitId = "latency-units", convertFunc = convertLatency) : "bufferbloat" == testType && (speedElemId = "bufferbloat-value", speedUnitId = "bufferbloat-units", convertFunc = convertLatency), speedElem = getElement(speedElemId), speedUnitsElem = getElement(speedUnitId), bytesElemId && (bytesElem = getElement(bytesElemId), bytesElem.innerHTML = convertToMB(result)), speedData = convertFunc(result), speed = speedData.speed + speedData.units, speedElem && (speedElem.innerHTML = speedData.speed), speedUnitsElem && (speedUnitsElem.innerHTML = speedData.units)
  2112.                                 };
  2113.                                 updateMetric(result.testType), "download" === result.testType && updateMetric("bufferbloat"), "true" !== config.measureUploadLatency && config.measureUploadLatency !== !0 || "upload" !== result.testType || updateMetric("bufferbloat")
  2114.                             },
  2115.                             onComplete: function(result) {
  2116.                                 var className, speedElemId, speedUnitId, speedLabelId, speedElem, speedUnitsElem, speedLabelElem, unstableResultsElem, testErrorElem, testType = result.testType,
  2117.                                     speedProgressIndicator = getElement("speed-progress-indicator"),
  2118.                                     speedProgressIndicatorIcon = getElement("speed-progress-indicator-icon"),
  2119.                                     showAfterTestActions = !1;
  2120.                                 if (downloadResult = result, "download" == testType ? (speedElemId = "speed-value", speedUnitId = "speed-units", speedLabelId = "speed-value", showAfterTestActions = !0, convertFunc = convertSpeed) : "upload" == testType ? (speedElemId = "upload-value", speedUnitId = "upload-units", speedLabelId = "upload-label", convertFunc = convertSpeed) : "latency" == testType ? (speedElemId = "latency-value", speedUnitId = "latency-units", speedLabelId = "latency-label", convertFunc = convertLatency) : "bufferbloat" == testType && (speedElemId = "bufferbloat-value", speedUnitId = "bufferbloat-units", speedLabelId = "bufferbloat-label", convertFunc = convertLatency), speedElem = getElement(speedElemId), speedUnitsElem = getElement(speedUnitId), speedLabelElem = getElement(speedLabelId), "success" === result.result ? (that.onProgress(result), result.stable ? className = "succeeded" : (className = "failed", unstableResultsElem = getElement("unstable-results-msg"), showElement(unstableResultsElem))) : "stop" !== result.result ? (testErrorElem = getElement("error-results-msg"), showElement(testErrorElem), className = "failed") : (className = "stopped", showAfterTestActions = !1), showAfterTestActions && setupAfterTestActions(), ("download" === testType && !showingMoreDetails || "upload" === testType || "stop" === result.result) && (removeClass(speedProgressIndicator, "in-progress"), removeClass(speedProgressIndicatorIcon, "oc-icon-pause"), addClass(speedProgressIndicatorIcon, "oc-icon-refresh"), addClass(speedProgressIndicator, className), utils.removeEventListener(testAgainBtn, "click", pause), utils.addEventListener(testAgainBtn, "click", restartTest)), addClass(speedElem, className), addClass(speedUnitsElem, className), addClass(speedLabelElem, className), logger.flush(), "download" === testType && "success" === result.result) {
  2121.                                     var speedData = convertSpeed(result);
  2122.                                     fbShareBtn = getElement("share-on-facebook-link"), twShareBtn = getElement("share-on-twitter-link"), fbShareEventListener && utils.removeEventListener(fbShareBtn, "click", fbShareEventListener), twShareEventListener && utils.removeEventListener(fbShareBtn, "click", twShareEventListener), fbShareEventListener = function() {
  2123.                                         var location, targetUrl, facebookUrl, hostname, protocol, messageContentElem = getElement("share-msg"),
  2124.                                             shareDescription = messageContentElem.getAttribute("my-speed") + speedData.speed + speedData.units + ". " + messageContentElem.getAttribute("yours-speed"),
  2125.                                             shareTitle = messageContentElem.getAttribute("share-on-fb");
  2126.                                         location = window.location, hostname = location.hostname, protocol = location.protocol, "http:" !== location.protocol && "https:" !== location.protocol && (hostname = "fast.com", protocol = "https:"), targetUrl = protocol + "//" + hostname + "/" + curLang + "/share/" + speedData.speed + speedData.units + ".html", facebookUrl = "https://www.facebook.com/sharer/sharer.php?u=" + encodeURI(targetUrl) + "&title=" + encodeURI("FAST speed test") + "&description=" + encodeURI(shareDescription), logger.logEvent("FacebookShare", {
  2127.                                             url: targetUrl
  2128.                                         }), popupCenter(facebookUrl, shareTitle, 400, 400)
  2129.                                     }, twShareEventListener = function() {
  2130.                                         var location, targetUrl, twitterUrl, hostname, protocol, messageContentElem = getElement("share-msg"),
  2131.                                             shareDescription = messageContentElem.getAttribute("my-speed") + " " + speedData.speed + speedData.units + ". " + messageContentElem.getAttribute("yours-speed"),
  2132.                                             shareTitle = messageContentElem.getAttribute("share-on-tw");
  2133.                                         location = window.location, hostname = location.hostname, protocol = location.protocol, "http:" !== location.protocol && "https:" !== location.protocol && (hostname = "fast.com", protocol = "https:"), targetUrl = protocol + "//" + hostname + "/" + curLang + "/share/" + speedData.speed + speedData.units + ".html", twitterUrl = "https://twitter.com/intent/tweet?url=" + encodeURI(targetUrl) + "&text=" + encodeURI(shareDescription), twitterUrl = twitterUrl.replace(/;/g, "%3B"), logger.logEvent("TwitterShare", {
  2134.                                             url: targetUrl
  2135.                                         }), popupCenter(twitterUrl, shareTitle, 400, 400)
  2136.                                     }, utils.addEventListener(fbShareBtn, "click", fbShareEventListener), utils.addEventListener(twShareBtn, "click", twShareEventListener);
  2137.                                     var infoElem = getElement("test-info-container"),
  2138.                                         showInfo = function(uploadResult) {
  2139.                                             tester.off(events.END, showInfo), "stop" !== uploadResult.result && (addClass(infoElem, "succeeded"), addClass(detailsElem, "succeeded"))
  2140.                                         },
  2141.                                         doUpload = function(latencyResult) {
  2142.                                             tester.off(events.END, doUpload), "stop" !== latencyResult.result && (addClass(latencyContainer, "succeeded"), !downloadResult || void 0 !== downloadResult.latency && null !== downloadResult.latency || (downloadResult.latency = downloadResult.latency || {
  2143.                                                 value: "NA"
  2144.                                             }), that.onComplete({
  2145.                                                 testType: "bufferbloat",
  2146.                                                 stable: !0,
  2147.                                                 value: downloadResult.latency.value,
  2148.                                                 result: "success"
  2149.                                             }), tester.on(events.END, showInfo), tester.upload())
  2150.                                         };
  2151.                                     tester.on(events.END, doUpload), tester.latency()
  2152.                                 }
  2153.                             },
  2154.                             setupEvents: function() {
  2155.                                 var testConfigBtn, cancelConfigBtn, applyConfigBtn, resetConfigBtn;
  2156.                                 config = getTestSettings(tester);
  2157.                                 try {
  2158.                                     applyTesterConfig(config)
  2159.                                 } catch (e) {
  2160.                                     resetConfig()
  2161.                                 }
  2162.                                 renderConfig(config), pauseElem = getElement("speed-progress-indicator"), fbShareBtn = getElement("share-on-facebook-link"), twShareBtn = getElement("share-on-twitter-link"), testAgainBtn = getElement("speed-progress-indicator"), showMoreDetailsBtn = getElement("show-more-details-link"), cancelConfigBtn = getElement("cancel-config"), applyConfigBtn = getElement("apply-config"), resetConfigBtn = getElement("reset-config"), testConfigBtn = getElement("settings-link"), resultsExplanationElem = getElement("test-context-container"), configContainerElem = getElement("test-config-container"), detailsElem = getElement("extra-details-container"), latencyContainer = getElement("latency-container"), setupHelp(), setupPause(), setupLanguageControls(), utils.addEventListener(showMoreDetailsBtn, "click", showMoreDetails), utils.addEventListener(testConfigBtn, "click", showConfig), utils.addEventListener(cancelConfigBtn, "click", cancelConfig), utils.addEventListener(applyConfigBtn, "click", applyConfig), utils.addEventListener(resetConfigBtn, "click", resetConfig), tester.on(events.URL_REQUEST_END, updateClientInfo), (config.showAdvanced === !0 || "true" === config.showAdvanced) && showMoreDetails()
  2163.                             }
  2164.                         };
  2165.                     return that
  2166.                 };
  2167.             module.exports = ui
  2168.         }), require.define("/app/viewport-units-buggyfill.js", function(require, module, exports) {
  2169.             ! function(root, factory) {
  2170.                 "use strict";
  2171.                 "function" == typeof define && define.amd ? define([], factory) : "object" == typeof exports ? module.exports = factory() : root.viewportUnitsBuggyfill = factory()
  2172.             }(this, function() {
  2173.                 "use strict";
  2174.  
  2175.                 function debounce(func, wait) {
  2176.                     var timeout;
  2177.                     return function() {
  2178.                         var context = this,
  2179.                             args = arguments,
  2180.                             callback = function() {
  2181.                                 func.apply(context, args)
  2182.                             };
  2183.                         clearTimeout(timeout), timeout = setTimeout(callback, wait)
  2184.                     }
  2185.                 }
  2186.  
  2187.                 function initialize(initOptions) {
  2188.                     if (!initialized) {
  2189.                         if (options = initOptions || {}, options.isMobileSafari = isMobileSafari, options.isBadStockAndroid = isBadStockAndroid, !isMobileSafari && !isBadStockAndroid && !isOperaMini) return window.console, window.console, {
  2190.                             init: function() {}
  2191.                         };
  2192.                         initialized = !0, styleNode = document.createElement("style"), styleNode.id = "patched-viewport", document.head.appendChild(styleNode);
  2193.                         var _refresh = debounce(refresh, options.refreshDebounceWait || 100);
  2194.                         window.addEventListener("orientationchange", _refresh, !0), window.addEventListener("pageshow", _refresh, !0), refresh()
  2195.                     }
  2196.                 }
  2197.  
  2198.                 function updateStyles() {
  2199.                     styleNode.textContent = getReplacedViewportUnits(), styleNode.parentNode.appendChild(styleNode)
  2200.                 }
  2201.  
  2202.                 function refresh() {
  2203.                     initialized && (findProperties(), setTimeout(function() {
  2204.                         updateStyles()
  2205.                     }, 1))
  2206.                 }
  2207.  
  2208.                 function processStylesheet(ss) {
  2209.                     try {
  2210.                         if (!ss.cssRules) return
  2211.                     } catch (e) {
  2212.                         if ("SecurityError" !== e.name) throw e;
  2213.                         return
  2214.                     }
  2215.                     for (var rules = [], i = 0; i < ss.cssRules.length; i++) {
  2216.                         var rule = ss.cssRules[i];
  2217.                         rules.push(rule)
  2218.                     }
  2219.                     return rules
  2220.                 }
  2221.  
  2222.                 function findProperties() {
  2223.                     return declarations = [], forEach.call(document.styleSheets, function(sheet) {
  2224.                         var cssRules = processStylesheet(sheet);
  2225.                         cssRules && "patched-viewport" !== sheet.ownerNode.id && (sheet.media && sheet.media.mediaText && window.matchMedia && !window.matchMedia(sheet.media.mediaText).matches || forEach.call(cssRules, findDeclarations))
  2226.                     }), declarations
  2227.                 }
  2228.  
  2229.                 function findDeclarations(rule) {
  2230.                     if (7 === rule.type) {
  2231.                         var value;
  2232.                         try {
  2233.                             value = rule.cssText
  2234.                         } catch (e) {
  2235.                             return
  2236.                         }
  2237.                         return viewportUnitExpression.lastIndex = 0, void(viewportUnitExpression.test(value) && declarations.push([rule, null, value]))
  2238.                     }
  2239.                     if (!rule.style) {
  2240.                         if (!rule.cssRules) return;
  2241.                         return void forEach.call(rule.cssRules, function(_rule) {
  2242.                             findDeclarations(_rule)
  2243.                         })
  2244.                     }
  2245.                     forEach.call(rule.style, function(name) {
  2246.                         var value = rule.style.getPropertyValue(name);
  2247.                         rule.style.getPropertyPriority(name) && (value += " !important"), viewportUnitExpression.lastIndex = 0, viewportUnitExpression.test(value) && declarations.push([rule, name, value])
  2248.                     })
  2249.                 }
  2250.  
  2251.                 function getReplacedViewportUnits() {
  2252.                     dimensions = getViewport();
  2253.                     var open, close, css = [],
  2254.                         buffer = [];
  2255.                     return declarations.forEach(function(item) {
  2256.                         var _item = overwriteDeclaration.apply(null, item),
  2257.                             _open = _item.selector.length ? _item.selector.join(" {\n") + " {\n" : "",
  2258.                             _close = new Array(_item.selector.length + 1).join("\n}");
  2259.                         return _open && _open === open ? (_open && !open && (open = _open, close = _close), void buffer.push(_item.content)) : (buffer.length && (css.push(open + buffer.join("\n") + close), buffer.length = 0), void(_open ? (open = _open, close = _close, buffer.push(_item.content)) : (css.push(_item.content), open = null, close = null)))
  2260.                     }), buffer.length && css.push(open + buffer.join("\n") + close), isOperaMini && css.push("* { content: normal !important; }"), css.join("\n\n")
  2261.                 }
  2262.  
  2263.                 function overwriteDeclaration(rule, name, value) {
  2264.                     var _value, _selectors = [];
  2265.                     _value = value.replace(viewportUnitExpression, replaceValues), name && (_selectors.push(rule.selectorText), _value = name + ": " + _value + ";");
  2266.                     for (var _rule = rule.parentRule; _rule;) _selectors.unshift("@media " + _rule.media.mediaText), _rule = _rule.parentRule;
  2267.                     return {
  2268.                         selector: _selectors,
  2269.                         content: _value
  2270.                     }
  2271.                 }
  2272.  
  2273.                 function replaceValues(match, number, unit) {
  2274.                     var _base = dimensions[unit],
  2275.                         _number = parseFloat(number) / 100;
  2276.                     return _number * _base + "px"
  2277.                 }
  2278.  
  2279.                 function getViewport() {
  2280.                     var vh = window.innerHeight,
  2281.                         vw = window.innerWidth;
  2282.                     return {
  2283.                         vh: vh,
  2284.                         vw: vw,
  2285.                         vmax: Math.max(vw, vh),
  2286.                         vmin: Math.min(vw, vh)
  2287.                     }
  2288.                 }
  2289.                 var options, dimensions, declarations, styleNode, initialized = !1,
  2290.                     userAgent = window.navigator.userAgent,
  2291.                     viewportUnitExpression = /([+-]?[0-9.]+)(vh|vw|vmin|vmax)/g,
  2292.                     forEach = [].forEach,
  2293.                     isOperaMini = userAgent.indexOf("Opera Mini") > -1,
  2294.                     isMobileSafari = /(iPhone|iPod|iPad).+AppleWebKit/i.test(userAgent) && function() {
  2295.                         var iOSversion = userAgent.match(/OS (\d)/);
  2296.                         return iOSversion && iOSversion.length > 1 && parseInt(iOSversion[1]) < 10
  2297.                     }(),
  2298.                     isBadStockAndroid = function() {
  2299.                         var isAndroid = userAgent.indexOf(" Android ") > -1;
  2300.                         if (!isAndroid) return !1;
  2301.                         var isStockAndroid = userAgent.indexOf("Version/") > -1;
  2302.                         if (!isStockAndroid) return !1;
  2303.                         var versionNumber = parseFloat((userAgent.match("Android ([0-9.]+)") || [])[1]);
  2304.                         return 4.4 >= versionNumber
  2305.                     }();
  2306.                 return {
  2307.                     version: "0.6.0",
  2308.                     findProperties: findProperties,
  2309.                     getCss: getReplacedViewportUnits,
  2310.                     init: initialize,
  2311.                     refresh: refresh
  2312.                 }
  2313.             })
  2314.         }), require.define("/app/browser_test.js", function(require) {
  2315.             function startTest() {
  2316.                 tester.download()
  2317.             }
  2318.  
  2319.             function flushLogger(force) {
  2320.                 !force && tester && tester.isRunning() || logger.flush()
  2321.             }
  2322.             var aggregator, stopper, testerFactory, tester, utils, events, requester, logging, logger, ui, apiEndpoint, loggingEndpoint;
  2323.             window.onerror = function(errorMsg, url, lineNumber, column, errorObj) {
  2324.                 var l, errorData;
  2325.                 errorData = {
  2326.                     message: errorMsg,
  2327.                     url: url,
  2328.                     line: lineNumber,
  2329.                     column: column
  2330.                 }, errorObj && (errorData.error = {
  2331.                     name: errorObj.name,
  2332.                     stack: errorObj.stack,
  2333.                     data: errorObj
  2334.                 }), logger && (l = logger.getLogger(), l.logEvent("ExceptionOccurred", errorData), flushLogger(!0))
  2335.             }, aggregator = require("./aggregator/stableMovingAverage")(5), stopper = require("./stopper/stableDeltaMeasurementsStopper")({
  2336.                 minDuration: 7,
  2337.                 maxDuration: 30,
  2338.                 stabilityDelta: 2,
  2339.                 minStableMeasurements: 6,
  2340.                 measureLatency: !1
  2341.             }), testerFactory = require("./tester"), utils = require("./utils"), events = require("./event").events, requester = require("./requester/xhr"), logging = require("./logger"), apiEndpoint = "api.fast.com/netflix/speedtest/v2", loggingEndpoint = "https://ichnaea-web.netflix.com/cl2", window.console || (console = {
  2342.                 log: utils.dummy
  2343.             }), utils.polyfillObjectKeys(), tester = testerFactory(requester, {
  2344.                 collectAfterComplete: !1,
  2345.                 duration: {
  2346.                     min: 5,
  2347.                     max: 30
  2348.                 },
  2349.                 connections: {
  2350.                     min: 1,
  2351.                     max: 8
  2352.                 },
  2353.                 maxAttempts: 10,
  2354.                 aggregator: aggregator,
  2355.                 measureLatency: !0,
  2356.                 getTestOcasParams: {
  2357.                     https: !0,
  2358.                     endpoint: apiEndpoint,
  2359.                     token: "YXNkZmFzZGxmbnNkYWZoYXNkZmhrYWxm",
  2360.                     urlCount: 5
  2361.                 },
  2362.                 stopper: stopper,
  2363.                 progressFrequencyMs: 150
  2364.             }), logger = logging(tester, {
  2365.                 url: loggingEndpoint
  2366.             });
  2367.             var testLogger = logger.getLogger();
  2368.             if (testLogger.logEvent("PageVisit"), flushLogger(!0), setInterval(flushLogger, 2e3), window.location.pathname.indexOf("/share/") >= 0) {
  2369.                 var l = testLogger,
  2370.                     data = {
  2371.                         referrer: document.referrer,
  2372.                         url: window.location.href
  2373.                     };
  2374.                 l.logEvent("ShareLinkClicked", data), flushLogger(!0), setTimeout(function() {
  2375.                     window.location.href = location.protocol + "//" + location.hostname
  2376.                 }, 5)
  2377.             }
  2378.             ui = require("./ui")(tester, testLogger), tester.on(events.START, function() {
  2379.                 tester.on(events.END, ui.onComplete).on(events.PROGRESS, ui.onProgress)
  2380.             }).on(events.END, function() {
  2381.                 tester.off(events.PROGRESS, ui.onProgress).off(events.END, ui.onComplete)
  2382.             }), logger.startLogging(), ui.setupEvents(), startTest(), require("./viewport-units-buggyfill").init()
  2383.         }), require("/app/browser_test.js")
  2384. }();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement