Commit 55d92252 authored by Łukasz Nowak's avatar Łukasz Nowak

Initial version

Imported from https://lab.nexedi.com/snippets/569/raw
parents
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTPing | Access servers based on network response time!</title>
<script type="text/javascript">
(function(globals) {
var define, requireModule;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requireModule = function(name) {
if (seen[name]) { return seen[name]; }
seen[name] = {};
var mod = registry[name];
if (!mod) {
throw new Error("Module '" + name + "' not found.");
}
var deps = mod.deps,
callback = mod.callback,
reified = [],
exports;
for (var i=0, l=deps.length; i<l; i++) {
if (deps[i] === 'exports') {
reified.push(exports = {});
} else {
reified.push(requireModule(deps[i]));
}
}
var value = callback.apply(this, reified);
return seen[name] = exports || value;
};
})();
define("rsvp/all",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
/* global toString */
function promiseAtLeast(expected_count, promises) {
if (Object.prototype.toString.call(promises) !== "[object Array]") {
throw new TypeError('You must pass an array to all.');
}
function canceller() {
var promise;
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function' &&
typeof promise.cancel === 'function') {
promise.cancel();
}
}
}
return new Promise(function(resolve, reject) {
var results = [], remaining = promises.length,
promise, remaining_count = promises.length - expected_count;
if (remaining === 0) {
if (expected_count === 1) {
resolve();
} else {
resolve([]);
}
}
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function resolveAll(index, value) {
results[index] = value;
if (--remaining === remaining_count) {
if (remaining_count === 0) {
resolve(results);
} else {
resolve(value);
canceller();
}
}
}
function cancelAll(rejectionValue) {
reject(rejectionValue);
canceller();
}
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function') {
promise.then(resolver(i), cancelAll);
} else {
resolveAll(i, promise);
}
}
}, canceller
);
}
function all(promises) {
return promiseAtLeast(promises.length, promises);
}
function any(promises) {
return promiseAtLeast(1, promises);
}
__exports__.all = all;
__exports__.any = any;
});
define("rsvp/async",
["exports"],
function(__exports__) {
"use strict";
var browserGlobal = (typeof window !== 'undefined') ? window : {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var async;
var local = (typeof global !== 'undefined') ? global : this;
function checkNativePromise() {
if (typeof Promise === "function" &&
typeof Promise.resolve === "function") {
try {
/* global Promise */
var promise = new Promise(function(){});
if ({}.toString.call(promise) === "[object Promise]") {
return true;
}
} catch (e) {}
}
return false;
}
function useNativePromise() {
var nativePromise = Promise.resolve();
return function(callback, arg) {
nativePromise.then(function () {
callback(arg);
});
};
}
// old node
function useNextTick() {
return function(callback, arg) {
process.nextTick(function() {
callback(arg);
});
};
}
// node >= 0.10.x
function useSetImmediate() {
return function(callback, arg) {
/* global setImmediate */
setImmediate(function(){
callback(arg);
});
};
}
function useMutationObserver() {
var queue = [];
var observer = new BrowserMutationObserver(function() {
var toProcess = queue.slice();
queue = [];
toProcess.forEach(function(tuple) {
var callback = tuple[0], arg= tuple[1];
callback(arg);
});
});
var element = document.createElement('div');
observer.observe(element, { attributes: true });
// Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
window.addEventListener('unload', function(){
observer.disconnect();
observer = null;
}, false);
return function(callback, arg) {
queue.push([callback, arg]);
element.setAttribute('drainQueue', 'drainQueue');
};
}
function useSetTimeout() {
return function(callback, arg) {
local.setTimeout(function() {
callback(arg);
}, 1);
};
}
if (checkNativePromise()) {
async = useNativePromise();
} else if (typeof setImmediate === 'function') {
async = useSetImmediate();
} else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
async = useNextTick();
} else if (BrowserMutationObserver) {
async = useMutationObserver();
} else {
async = useSetTimeout();
}
__exports__.async = async;
});
define("rsvp/cancellation_error",
["exports"],
function(__exports__) {
"use strict";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function CancellationError(message) {
this.name = "cancel";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
CancellationError.prototype = new Error();
CancellationError.prototype.constructor = CancellationError;
__exports__.CancellationError = CancellationError;
});
define("rsvp/config",
["rsvp/async","exports"],
function(__dependency1__, __exports__) {
"use strict";
var async = __dependency1__.async;
var config = {};
config.async = async;
__exports__.config = config;
});
define("rsvp/defer",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function defer() {
var deferred = {
// pre-allocate shape
resolve: undefined,
reject: undefined,
promise: undefined
};
deferred.promise = new Promise(function(resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
__exports__.defer = defer;
});
define("rsvp/events",
["exports"],
function(__exports__) {
"use strict";
var Event = function(type, options) {
this.type = type;
for (var option in options) {
if (!options.hasOwnProperty(option)) { continue; }
this[option] = options[option];
}
};
var indexOf = function(callbacks, callback) {
for (var i=0, l=callbacks.length; i<l; i++) {
if (callbacks[i][0] === callback) { return i; }
}
return -1;
};
var callbacksFor = function(object) {
var callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
};
var EventTarget = {
mixin: function(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
return object;
},
on: function(eventNames, callback, binding) {
var allCallbacks = callbacksFor(this), callbacks, eventName;
eventNames = eventNames.split(/\s+/);
binding = binding || this;
while (eventName = eventNames.shift()) {
callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if (indexOf(callbacks, callback) === -1) {
callbacks.push([callback, binding]);
}
}
},
off: function(eventNames, callback) {
var allCallbacks = callbacksFor(this), callbacks, eventName, index;
eventNames = eventNames.split(/\s+/);
while (eventName = eventNames.shift()) {
if (!callback) {
allCallbacks[eventName] = [];
continue;
}
callbacks = allCallbacks[eventName];
index = indexOf(callbacks, callback);
if (index !== -1) { callbacks.splice(index, 1); }
}
},
trigger: function(eventName, options) {
var allCallbacks = callbacksFor(this),
callbacks, callbackTuple, callback, binding, event;
if (callbacks = allCallbacks[eventName]) {
// Don't cache the callbacks.length since it may grow
for (var i=0; i<callbacks.length; i++) {
callbackTuple = callbacks[i];
callback = callbackTuple[0];
binding = callbackTuple[1];
if (typeof options !== 'object') {
options = { detail: options };
}
event = new Event(eventName, options);
callback.call(binding, event);
}
}
}
};
__exports__.EventTarget = EventTarget;
});
define("rsvp/hash",
["rsvp/defer","exports"],
function(__dependency1__, __exports__) {
"use strict";
var defer = __dependency1__.defer;
function size(object) {
var s = 0;
for (var prop in object) {
s++;
}
return s;
}
function hash(promises) {
var results = {}, deferred = defer(), remaining = size(promises);
if (remaining === 0) {
deferred.resolve({});
}
var resolver = function(prop) {
return function(value) {
resolveAll(prop, value);
};
};
var resolveAll = function(prop, value) {
results[prop] = value;
if (--remaining === 0) {
deferred.resolve(results);
}
};
var rejectAll = function(error) {
deferred.reject(error);
};
for (var prop in promises) {
if (promises[prop] && typeof promises[prop].then === 'function') {
promises[prop].then(resolver(prop), rejectAll);
} else {
resolveAll(prop, promises[prop]);
}
}
return deferred.promise;
}
__exports__.hash = hash;
});
define("rsvp/node",
["rsvp/promise","rsvp/all","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var all = __dependency2__.all;
function makeNodeCallbackFor(resolve, reject) {
return function (error, value) {
if (error) {
reject(error);
} else if (arguments.length > 2) {
resolve(Array.prototype.slice.call(arguments, 1));
} else {
resolve(value);
}
};
}
function denodeify(nodeFunc) {
return function() {
var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;
var thisArg = this;
var promise = new Promise(function(nodeResolve, nodeReject) {
resolve = nodeResolve;
reject = nodeReject;
});
all(nodeArgs).then(function(nodeArgs) {
nodeArgs.push(makeNodeCallbackFor(resolve, reject));
try {
nodeFunc.apply(thisArg, nodeArgs);
} catch(e) {
reject(e);
}
});
return promise;
};
}
__exports__.denodeify = denodeify;
});
define("rsvp/promise",
["rsvp/config","rsvp/events","rsvp/cancellation_error","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
var config = __dependency1__.config;
var EventTarget = __dependency2__.EventTarget;
var CancellationError = __dependency3__.CancellationError;
function objectOrFunction(x) {
return isFunction(x) || (typeof x === "object" && x !== null);
}
function isFunction(x){
return typeof x === "function";
}
var Promise = function(resolver, canceller) {
var promise = this,
resolved = false;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if ((canceller !== undefined) && (typeof canceller !== 'function')) {
throw new TypeError('You can only pass a canceller function' +
' as the second argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver, canceller);
}
var resolvePromise = function(value) {
if (resolved) { return; }
resolved = true;
resolve(promise, value);
};
var rejectPromise = function(value) {
if (resolved) { return; }
resolved = true;
reject(promise, value);
};
this.on('promise:failed', function(event) {
this.trigger('error', { detail: event.detail });
}, this);
this.on('error', onerror);
this.cancel = function () {
// For now, simply reject the promise and does not propagate the cancel
// to parent or children
if (resolved) { return; }
if (canceller !== undefined) {
try {
canceller();
} catch (e) {
rejectPromise(e);
return;
}
}
// Trigger cancel?
rejectPromise(new CancellationError());
};
try {
resolver(resolvePromise, rejectPromise);
} catch(e) {
rejectPromise(e);
}
};
function onerror(event) {
if (config.onerror) {
config.onerror(event.detail);
}
}
var invokeCallback = function(type, promise, callback, event) {
var hasCallback = isFunction(callback),
value, error, succeeded, failed;
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
if (hasCallback) {
try {
value = callback(event.detail);
succeeded = true;
} catch(e) {
failed = true;
error = e;
}
} else {
value = event.detail;
succeeded = true;
}
if (handleThenable(promise, value)) {
return;
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (failed) {
reject(promise, error);
} else if (type === 'resolve') {
resolve(promise, value);
} else if (type === 'reject') {
reject(promise, value);
}
};
Promise.prototype = {
constructor: Promise,
isRejected: undefined,
isFulfilled: undefined,
rejectedReason: undefined,
fulfillmentValue: undefined,
then: function(done, fail) {
this.off('error', onerror);
var thenPromise = new this.constructor(function() {},
function () {
thenPromise.trigger('promise:cancelled', {});
});
if (this.isFulfilled) {
config.async(function(promise) {
invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue });
}, this);
}
if (this.isRejected) {
config.async(function(promise) {
invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason });
}, this);
}
this.on('promise:resolved', function(event) {
invokeCallback('resolve', thenPromise, done, event);
});
this.on('promise:failed', function(event) {
invokeCallback('reject', thenPromise, fail, event);
});
return thenPromise;
},
fail: function(fail) {
return this.then(null, fail);
},
always: function(fail) {
return this.then(fail, fail);
}
};
EventTarget.mixin(Promise.prototype);
function resolve(promise, value) {
if (promise === value) {
fulfill(promise, value);
} else if (!handleThenable(promise, value)) {
fulfill(promise, value);
}
}
function handleThenable(promise, value) {
var then = null,
resolved;
try {
if (promise === value) {
throw new TypeError("A promises callback cannot return that same promise.");
}
if (objectOrFunction(value)) {
then = value.then;
if (isFunction(then)) {
promise.on('promise:cancelled', function(event) {
if (isFunction(value.cancel)) {
value.cancel();
}
});
then.call(value, function(val) {
if (resolved) { return true; }
resolved = true;
if (value !== val) {
resolve(promise, val);
} else {
fulfill(promise, val);
}
}, function(val) {
if (resolved) { return true; }
resolved = true;
reject(promise, val);
});
return true;
}
}
} catch (error) {
reject(promise, error);
return true;
}
return false;
}
function fulfill(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true;
promise.fulfillmentValue = value;
});
}
function reject(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:failed', { detail: value });
promise.isRejected = true;
promise.rejectedReason = value;
});
}
__exports__.Promise = Promise;
});
define("rsvp/queue",
["rsvp/promise","rsvp/resolve","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var resolve = __dependency2__.resolve;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function ResolvedQueueError(message) {
this.name = "resolved";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
ResolvedQueueError.prototype = new Error();
ResolvedQueueError.prototype.constructor = ResolvedQueueError;
var Queue = function() {
var queue = this,
promise_list = [],
promise,
fulfill,
reject,
resolved;
if (!(this instanceof Queue)) {
return new Queue();
}
function canceller() {
for (var i = 0; i < 2; i++) {
promise_list[i].cancel();
}
}
promise = new Promise(function(done, fail) {
fulfill = function (fulfillmentValue) {
if (resolved) {return;}
queue.isFulfilled = true;
queue.fulfillmentValue = fulfillmentValue;
resolved = true;
return done(fulfillmentValue);
};
reject = function (rejectedReason) {
if (resolved) {return;}
queue.isRejected = true;
queue.rejectedReason = rejectedReason ;
resolved = true;
return fail(rejectedReason);
};
}, canceller);
promise_list.push(resolve());
promise_list.push(promise_list[0].then(function () {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill();
}
}));
queue.cancel = function () {
if (resolved) {return;}
resolved = true;
promise.cancel();
promise.fail(function (rejectedReason) {
queue.isRejected = true;
queue.rejectedReason = rejectedReason;
});
};
queue.then = function () {
return promise.then.apply(promise, arguments);
};
queue.push = function(done, fail) {
var last_promise = promise_list[promise_list.length - 1],
next_promise;
if (resolved) {
throw new ResolvedQueueError();
}
next_promise = last_promise.then(done, fail);
promise_list.push(next_promise);
// Handle pop
promise_list.push(next_promise.then(function (fulfillmentValue) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill(fulfillmentValue);
} else {
return fulfillmentValue;
}
}, function (rejectedReason) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
reject(rejectedReason);
} else {
throw rejectedReason;
}
}));
return this;
};
};
Queue.prototype = Object.create(Promise.prototype);
Queue.prototype.constructor = Queue;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
});
define("rsvp/reject",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function reject(reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
}
__exports__.reject = reject;
});
define("rsvp/resolve",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function resolve(thenable) {
return new Promise(function(resolve, reject) {
if (typeof thenable === "object" && thenable !== null) {
var then = thenable.then;
if ((then !== undefined) && (typeof then === "function")) {
return then.apply(thenable, [resolve, reject]);
}
}
return resolve(thenable);
}, function () {
if ((thenable !== undefined) && (thenable.cancel !== undefined)) {
thenable.cancel();
}
});
}
__exports__.resolve = resolve;
});
define("rsvp/rethrow",
["exports"],
function(__exports__) {
"use strict";
var local = (typeof global === "undefined") ? this : global;
function rethrow(reason) {
local.setTimeout(function() {
throw reason;
});
throw reason;
}
__exports__.rethrow = rethrow;
});
define("rsvp/timeout",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function promiseSetTimeout(millisecond, should_reject, message) {
var timeout_id;
function resolver(resolve, reject) {
timeout_id = setTimeout(function () {
if (should_reject) {
reject(message);
} else {
resolve(message);
}
}, millisecond);
}
function canceller() {
clearTimeout(timeout_id);
}
return new Promise(resolver, canceller);
}
function delay(millisecond, message) {
return promiseSetTimeout(millisecond, false, message);
}
function timeout(millisecond) {
return promiseSetTimeout(millisecond, true,
"Timed out after " + millisecond + " ms");
}
Promise.prototype.delay = function(millisecond) {
return this.then(function (fulfillmentValue) {
return delay(millisecond, fulfillmentValue);
});
};
__exports__.delay = delay;
__exports__.timeout = timeout;
});
define("rsvp",
["rsvp/events","rsvp/cancellation_error","rsvp/promise","rsvp/node","rsvp/all","rsvp/queue","rsvp/timeout","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
"use strict";
var EventTarget = __dependency1__.EventTarget;
var CancellationError = __dependency2__.CancellationError;
var Promise = __dependency3__.Promise;
var denodeify = __dependency4__.denodeify;
var all = __dependency5__.all;
var any = __dependency5__.any;
var Queue = __dependency6__.Queue;
var ResolvedQueueError = __dependency6__.ResolvedQueueError;
var delay = __dependency7__.delay;
var timeout = __dependency7__.timeout;
var hash = __dependency8__.hash;
var rethrow = __dependency9__.rethrow;
var defer = __dependency10__.defer;
var config = __dependency11__.config;
var resolve = __dependency12__.resolve;
var reject = __dependency13__.reject;
function configure(name, value) {
config[name] = value;
}
__exports__.CancellationError = CancellationError;
__exports__.Promise = Promise;
__exports__.EventTarget = EventTarget;
__exports__.all = all;
__exports__.any = any;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
__exports__.delay = delay;
__exports__.timeout = timeout;
__exports__.hash = hash;
__exports__.rethrow = rethrow;
__exports__.defer = defer;
__exports__.denodeify = denodeify;
__exports__.configure = configure;
__exports__.resolve = resolve;
__exports__.reject = reject;
});
window.RSVP = requireModule("rsvp");
})(window);
</script>
<script type="text/javascript">
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global console, window, document, RSVP, XMLHttpRequest */
(function (document, RSVP, XMLHttpRequest) {
"use strict";
var promise_list = [],
relative_url_path = "/app",
test_url_list = [
{ name : 'US-West-A',
testurl: "http://demoapp7.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp7.erp5.cn" + relative_url_path },
{ name : 'France-O',
testurl: "http://demoapp3.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp3.erp5.cn" + relative_url_path },
{ name : 'France-I',
testurl: "http://demoapp6.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp6.erp5.cn" + relative_url_path },
{ name : 'Canada-O',
testurl: "http://demoapp8.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp8.erp5.cn" + relative_url_path },
{ name : 'China-CT',
testurl: "http://demoapp26.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp26.erp5.cn" + relative_url_path },
{ name : 'China-CNC',
testurl: "http://demoapp27.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp27.erp5.cn" + relative_url_path },
{ name : 'China-CM',
testurl: "http://demoapp28.erp5.cn/WebSite_getTestFrontendResponse",
redirect: "http://demoapp28.erp5.cn" + relative_url_path }
];
function ajax(param) {
var xhr = new XMLHttpRequest();
return new RSVP.Promise(function (resolve, reject, notify) {
var k;
xhr.open(param.type || "GET", param.url, true);
xhr.responseType = param.dataType || "";
if (typeof param.headers === 'object' && param.headers !== null) {
for (k in param.headers) {
if (param.headers.hasOwnProperty(k)) {
xhr.setRequestHeader(k, param.headers[k]);
}
}
}
xhr.addEventListener("load", function (e) {
if (e.target.status >= 400) {
return reject(e);
}
return resolve(e);
});
xhr.addEventListener("error", reject);
xhr.addEventListener("progress", notify);
if (typeof param.xhrFields === 'object' && param.xhrFields !== null) {
for (k in param.xhrFields) {
if (param.xhrFields.hasOwnProperty(k)) {
xhr[k] = param.xhrFields[k];
}
}
}
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
xhr.send(param.data);
}, function () {
xhr.abort();
});
}
function testURL(url) {
return RSVP.Queue()
.push(function () {
return ajax({
url: url
});
})
.push(function (evt) {
return evt.target.responseText;
}, function () {
return "FAIL";
});
}
function launchTest(test_case) {
var start = new Date().getTime(),
url = test_case.testurl;
return new RSVP.Queue()
.push(function () {
return RSVP.any([RSVP.delay(4000),
testURL(url + "?nocache=" + start)]);
})
.push(function (result) {
var elapsed = new Date().getTime() - start;
if (result === undefined) {
return {url: test_case.redirect,
time: 1000000001,
name: test_case.name};
}
if (result === "FAIL") {
return {url: test_case.redirect,
time: 1000000000,
name: test_case.name};
}
return {url: test_case.redirect,
name: test_case.name,
time: elapsed};
});
}
function runOnce(test_queue) {
return new RSVP.Queue()
.push(function () {
var u;
for (u in test_url_list) {
if (test_url_list.hasOwnProperty(u)) {
test_queue.push(launchTest(test_url_list[u]));
}
}
return RSVP.all(test_queue);
})
.push(undefined, function (reason) {
console.log(reason);
return "OK";
});
}
function renderPartialResult(result, main_div, winner_div) {
var u, y, interaction, winner = 0,
t_i, table_dict = {}, interaction_dict = {}, msg = "";
msg += "<table width=100%>";
for (u in result) {
if (result.hasOwnProperty(u)) {
interaction = parseInt(u / test_url_list.length, 10);
if (!interaction_dict.hasOwnProperty(interaction)) {
interaction_dict[interaction] = 1;
}
if (!table_dict.hasOwnProperty(result[u].name)) {
table_dict[result[u].name] = {
total: 0,
name: result[u].name,
url: result[u].url
};
}
if (result[u].time === 1000000000) {
table_dict[result[u].name][interaction] = "FAIL";
table_dict[result[u].name].total = "REJECT";
} else if (result[u].time === 1000000001) {
table_dict[result[u].name][interaction] = "TIMEOUT";
table_dict[result[u].name].total = "REJECT";
} else {
table_dict[result[u].name][interaction] = result[u].time;
if (table_dict[result[u].name].total !== "REJECT") {
table_dict[result[u].name].total += result[u].time;
}
}
}
}
msg += "</tr> <th> </th>";
t_i = 0;
for (u in interaction_dict) {
if (interaction_dict.hasOwnProperty(u)) {
msg += '<th> Test ' + u + "</th>";
t_i += 1;
}
}
msg += "<th class='avg'> AVERAGE </th></tr>";
for (u in table_dict) {
if (table_dict.hasOwnProperty(u)) {
if (winner === 0) {
winner = table_dict[u];
}
if (table_dict[u].total !== "REJECT") {
table_dict[u].average = table_dict[u].total / t_i;
if (winner.average === "REJECT") {
winner = table_dict[u];
}
if (winner.average > table_dict[u].average) {
winner = table_dict[u];
}
} else {
table_dict[u].average = "REJECT";
}
}
}
for (u in table_dict) {
if (table_dict.hasOwnProperty(u)) {
if (u === winner.name) {
msg += "<tr class='winner'>";
} else {
msg += "<tr>";
}
msg += "<td>" + u + "</td>";
for (y in interaction_dict) {
if (interaction_dict.hasOwnProperty(y)) {
msg += "<td>" + table_dict[u][y] + "</td>";
}
}
msg += "<td>" + table_dict[u].average + "</td>";
msg += "</tr>";
}
}
msg += "</table>";
msg = "<h2> The best frontend for you is ... <strong>" +
winner.name + " </strong> </h2>" + msg;
main_div.innerHTML = msg;
msg = "";
msg += "<p><a class=btn href=" + winner.url + "> Click to Connect via ";
msg += winner.name + "</a> </p>";
winner_div.innerHTML = msg;
return winner;
}
function runAll() {
return runOnce(promise_list)
.push(function () {
return runOnce(promise_list)
.push(function () {
return runOnce(promise_list)
.push(function () {
return runOnce(promise_list)
.push(function () {
return runOnce(promise_list)
.push(function (result) {
var winner,
div = document.getElementsByClassName("table"),
winner_div = document.getElementsByClassName("link");
winner = renderPartialResult(result,
div[0],
winner_div[0]);
return winner;
});
});
});
});
});
}
runAll();
}(document, RSVP, XMLHttpRequest));
</script>
<style type="text/css">
/* === animation === */
@-ms-keyframes spin {
from { -ms-transform: rotate(0deg); }
to { -ms-transform: rotate(360deg); }
}
@-moz-keyframes spin {
from { -moz-transform: rotate(0deg); }
to { -moz-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
from { -webkit-transform: rotate(0deg); }
to { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
/* === font (embedded icons (hehe) and text === */
@charset "UTF-8";
body {
font-family: Arial;
}
@font-face {
font-family: "untitled-font-1";
src: url('untitled-font-1.eot') format('embedded-opentype');
/*
src:url("fonts/untitled-font-1.eot");
src:url("fonts/untitled-font-1.eot?#iefix") format("embedded-opentype"),
url("fonts/untitled-font-1.woff") format("woff"),
url("fonts/untitled-font-1.ttf") format("truetype"),
url("fonts/untitled-font-1.svg#untitled-font-1") format("svg");
*/
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'untitled-font-1';
src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAYUAA0AAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAF+AAAABoAAAAccMNEjEdERUYAAAXYAAAAHwAAACAANAAGT1MvMgAAAaAAAABDAAAAVk/WXJhjbWFwAAAB/AAAAEwAAAFSBH8EQWdhc3AAAAXQAAAACAAAAAj//wADZ2x5ZgAAAlgAAAJVAAADWMuB/dpoZWFkAAABMAAAAC8AAAA2BZDizGhoZWEAAAFgAAAAHQAAACQD+AIFaG10eAAAAeQAAAAVAAAAFgZcADJsb2NhAAACSAAAABAAAAAQAcYCqG1heHAAAAGAAAAAHgAAACAAUQBubmFtZQAABLAAAADjAAAB5t04W7Zwb3N0AAAFlAAAADkAAABPoMpE/3jaY2BkYGAA4nxBDvl4fpuvDNxMDCBwyenCPTgtxKDKeJvxHZDLwQCWBgAAQAnmAHjaY2BkYGB8x8DAoMfEAAKMtxkYGVABCwAysQICAAAAeNpjYGRgYGBnyGbgYAABJiBmZACJOYD5DAAOIQDAAAB42mNgZGJgnMDAysDB6MOYxsDA4A6lvzJIMrQwMDAxsHIywAAjAxIISHNNYXBgSGRIZXzw/wGDHuO7/7dhagCNTgwRAHjaY2KAACYIVmAwZxBiUAUAAsAAlQAAAHjaY2BgYGaAYBkGRgYQ8AHyGMF8FgYDIM0BhExgmWSG1P//waxECOv/ov8LobrAgJGNAc5lBOlhYkAFjAyUA2YWVgY2hkELAMs0CcIAAAAAAAAAAACYAPwBLgGseNp9UkFrE0EUfm9mMrMzuzOz3d1sFqTtZmM2pbYV0iRbKGoUL730IEiF5Bd48ehPEAQPIt4F67HiTQ/+gTY/wEMvBb0J/gAPwdnEUnvxDXzzvp157Pu+eeBBDl38hr/AhwxKGMADOIQpPAUYJYJ3inIw2u230kXeW5LyklQ1Y9fY9bPRP2Xpf+59DIyNsiyy5oWvbKj81Br/vTZRbLQ2cWSwUlIb6cJo+cOGcRJaGyZx+Ca0rcyGeSv96gkdCM8TgRYDLqQSnAslxesiCLIiCx7mSil/LfU/ldoYXcPbNJDSz3wp55vWGFvD5k7oYif/nnouaniecCF4DQBAoHJ+neI5DOE+QNfJuIVOx9AJuVpO0hpprWF/VN0lC5perZ6rWVSM+imeNpPO707SREIYpcqTytoVoognmL9xc8NnwnNsxVolPUUpIwQnefGkyMfT8bOiGcfNIpGJn0oVGal4gyNvEBEEgixSrqSJlEz9RP7cKvK82Noej50MQIicjpnTsQpQcdfOilOwjmlruNwTi+7rHeRinVPGKJ9RpFSccX4mKCVkg1Mi8FiQ+oTOPCSNGTd81iDozRip/9GALhzgOX4GDTfgNuzD46Vjf/2qXbnOhoNe2WkXgq9i0kp32/1q1G33yl7pPBZcNJr11LSLuq/BPXRG76Mrc93i4fKFaji4SnNK2PyYkdo4nDqH5+/w0TY6ORdOE+KRG7H5cWQ02+OBDi9CHfA99sGTy9HhXIrLMXJYTZgTTtlye/WSuNdg5Mskrucpnkh9YqVS0p5o+AMxl2fXAAAAeNqVj0+KwjAUh79oLQwKbgaXkgNMSlJx42JwMx5BPIBVCtJCrVdx64U8ikdwMS+d52LciIG8fPnl9/4EGHHGEJdhjFXukfKt3OeLi3IinpvygKH5VE4Zm6U4TfIhyrTLityT+l65z4Yf5UQ8V+UBE+7KKVNjOVHRUso+ULDFsaPuNEeAU9WW7aHYul1dtU6Ef68PcaViPBv2UsiSk8k4lgWvmvx5ArPu5iXmEgNzKSwNVnWzL2yeebuwT+OIEmYueJf7IO53/7IWV8NRMqIrjhBHZl00x7KubMj82zV/ARVDTeoAeNpjYGJABowM6IAdLMrEyMTIzMjCyMpeXJCZl5daxFJanFrEnpZfVJ5YlMKZASRSijLLUgG4GQunAAAAAAAAAf//AAJ42mNgZGBg4ANiCQYQYGJgZGBmYAOSLGAeAwAEpgA7AHjaY2BgYGQAgjO2i86D6EtOF+7BaABQ9wgAAAA=) format('woff'), url('untitled-font-1.ttf') format('truetype'), url('untitled-font-1.svg#untitled-font-1') format('svg');
font-weight: normal;
font-style: normal;
}
/* === set font on classes === */
[class^="custom-icon-"]:before,
[class*=" custom-icon-"]:before {
font-family: "untitled-font-1" !important;
font-style: normal !important;
font-weight: normal !important;
font-variant: normal !important;
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.custom-icon-spinner:before {
content: "a";
}
.custom-icon-user:before {
content: "b";
}
.custom-icon-forward:before {
content: "c";
}
.custom-icon-harddrive:before {
content: "e";
}
/* === logo === */
.custom-grandenet-logo {
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogPCEtLSBDcmVhdGVkIHdpdGggTWV0aG9kIERyYXcgLSBodHRwOi8vZ2l0aHViLmNvbS9kdW9waXhlbC9NZXRob2QtRHJhdy8gLS0+CiA8Zz4KICA8dGl0bGU+YmFja2dyb3VuZDwvdGl0bGU+CiAgPHJlY3QgZmlsbD0iI2ZmZiIgaWQ9ImNhbnZhc19iYWNrZ3JvdW5kIiBoZWlnaHQ9IjEyMiIgd2lkdGg9IjIwMiIgeT0iLTEiIHg9Ii0xIi8+CiAgPGcgZGlzcGxheT0ibm9uZSIgb3ZlcmZsb3c9InZpc2libGUiIHk9IjAiIHg9IjAiIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiIGlkPSJjYW52YXNHcmlkIj4KICAgPHJlY3QgZmlsbD0idXJsKCNncmlkcGF0dGVybikiIHN0cm9rZS13aWR0aD0iMCIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIxMDAlIiB3aWR0aD0iMTAwJSIvPgogIDwvZz4KIDwvZz4KIDxnPgogIDx0aXRsZT5MYXllciAxPC90aXRsZT4KICA8aW1hZ2Ugc3Ryb2tlPSJudWxsIiB4bGluazpocmVmPSJkYXRhOmltYWdlL3BuZztiYXNlNjQsaVZCT1J3MEtHZ29BQUFBTlNVaEVVZ0FBQVZZQUFBQ2lDQVlBQUFBYTIrVTRBQUFnQUVsRVFWUjQydTFkZVh3VDFkcHV5NjZBaUx0eTNSVVZSVkhjRU53RkJaZUw2M1c3S29vYlh1VzY0NEtDY04wdXFCOTgrb0c0bzRoUWxySVZzS1cwZElHV1V1amVVa3JUZEV2VHBFM1ROa21UenBkbjhNUnBtSm5NVENadEd0OC9uaDgwbVp3NTYzUGU4MjRuaXVPNEtBS0JRQ0RvQitvRUFvRkFJR0lsRUFnRUl0WWVCYWU3SStyWC9jMFh2SnBobm5iemhxcmtzWEhHdk9kM21ONWVWTkEweHVKd1IxTWZFUWdFSWxZVktMUTYrOSsxcFdibCtTc01uQmhBc3I5WHRaNUlmVVVnRUloWUZTREw1Qmc4Y3FYQkxrV3FRdnhVYWh0SmZVWWdFSWhZWldCdjkwU04zMWlWcUlSVWdVdGlLeTM3bTF4OXFlOElCQUlScXdUbTdiUGVxcFJVR2Y2WlZQYzU5UjJCUUNCaWxjRGtMVFhMMVJMcmhTc05qaGF2cEV2OVJ5QVFpRmo5QUhJY3NjTGdVa3Vzd0U1VDJ4Q2FSQVFDZ1lqVkR3ZHNyajVhU0JWWWU5QitCazBpQW9GQXhPb0hkMGRIMU9qVmxVWXR4RnBLQml3Q2dVREVLbzRudDlkOXFKWlVMMTlkYWZCNFNabjZqMEFnRUxHS1lKT2haWmhhWXYxNHIvVjI2anNDZ1VERUtvTlhNc3d2S0NYVjIrT3I0eHh1a2xZSkJBSVJxeXlzVG5mMEF3bTFTd0tSNm8wYnFsSVIra3A5UmlBUWlGZ1ZvTjNURWJXNHNPbXFrYkVHbXhpcHZyWExQS1haUmI2ckJBS0JpRlUxR3AyZTZKU2ExbU1XNWpkZUIxM3E1c3FXayt0YTNUSFVOd1FDZ1lpVlFDQVFpRmdKQkFLQmlKVkFJQkFJUkt3RUFvRkF4RW9nRUFoRXJBUUNnVURFU2lBUUNBUWlWZ0tCUUNCaTFST3RGYjI0L2YrOWpDdDQvVGF1Yk42bFhGMzhNVnhIRDR1SVFtWXM4L2FqdUxMUFJuRUZiMDdnU3VhTzRld2xmV2hTRWdoRXJGMExWMk1VdDNQU1RHNmQ5MDkvSkkxWXpkV3M2UmxYVTF2U0IzSTd4bndsMm83VWNRdTR0bXFLOGlJUWlGaTdRa28xeG5CSkY2MFVKU09HOWIwY3ZQUWF6dTFvM0RPQTJ6VElLTnVPMzA5TjVXeDUvV2lDRWdoRXJLRkY1ajJ2eUpJUnc2YkJCcTdsWUsrd2JJUFRFczF0T1NsYlVUdVNMb3psT3R3MFNRa0VJdFlRQWRLYkVqSmlLSjQ5TGl6YlVmSE5PYXJhVWZrajNhbEZJQkN4aGdqN1B4MnRpcEFTaDI4TXkzWmtUSmlqcWgxWjk3NU1rNVJBSUdJTkRmSmZuYVNLa0RiMGF3ekxkZ1RTRWZzajQ3WlpORWtKQkNMVzBDRDNYL2VvSXFUNG9hVmgyWTcwbXo5UzFZNmR0NzlMazVSQUlHSU5EZW8ySGF1S2tKSkhmeDhSRzBURjE4TnBraElJUkt5aGdjY1Z4VzArdGxBeElZV3JQeXNDQU5aRnV4V3JNK0JGUUpPVVFDQmlEWjNVR244TTc2Y2FpSkRTYi9xRWoyb0sxM2JrUFBXb0ltSTFmSGMyVFZBQ2dZaTFDOXlWbHB3clM2NTdIcC9DdVIxaEhzcnFpZUpEY2VWSXRYREdlSnFjQkVLRUV1ditKbGZmZDdNYUhuMWtXOTJDT3pmWHhONjFwV2JsL0Z6citIeUxjMEJIZDBtRnpVVjllRGVralFPcitXUDFodjRXYnZjREwzSzE2NDdYVTFKdGRScDdsOVIrZnRmdTh1Y1c3Q3A3YkZsQjFRZFBtV3hKSituV2p2ckVJZHlleDU3eVJXRnRQTEtXeXhnL2w3UHVPcUtyK3hSamlUSEYyR0tNQVl3NXhyN01Pd2Rvb1JBSU9oQ3IzZVdKZWptOS9zVUxWaGpjWXRkQUF5K2sxci9SMnQ3TmlVOUFwSjUyM2N1dHNXNDhjM3ZSemRXSkJXTTRmK1JXdmpPOTNXM1hzUTN1cU81TUlJTXhuSlphUDBOcW5ERUhYc2t3djJCdnAydS9DUVROeE9yeWRFUTluV3lhTGJYUWhQaEhRdTFpaThNZFVRWVdhOHVlb3hJTHhyckVTSldodUdiZWZaSFExZ2J2MkQyUVVMdEV5VmcvbldLYTFlN3BvRVZESUdnaDF0blpsdnVWTERTR3QzYVpwMFJTaDZUdmYyQ3pIS2t5TkxlVjlPL3BiWDF6bDNtcW1ySCtJTnR5SHkwYUFrRWxzVFk1UGRFalZ4cnNhaGJiaFNzTmprcDdlKzlJNkF4bmUwT01FbElGS3N3L2orN0piVFUwdC9mRzJLa1o2NUd4QnB2TlJTb0JBa0VWc2E0OWFEOUR6VUpqbUwvUE9pRXkxQUE1ZzVVU2E3NXgxak05dWEzejlsbHYxVExXY1JYMjAybmhFQWdxaVBXTm5lYW50U3cySENram9UTWNMcE5paWZWZy9ROWplbkpiWDk5cGZrYkxXTS9ZWlg2S0ZnNkJvSUpZNFY2alpiRTl2OFAwZGlSMEJ0eU9kcFRjbWFXRVdHSGs2c2x0ZlRiRk5GUExXTCtYMWZBd0xSd0NRUVd4ZnJyWE9sSExZbnR0cC9tNVNPbVF1cWJmaHdVaTFiMkdWMmQyZFBSc0MvbHJHZWJudFl3MVZBaTBjQWdFRmNTNnZzSitxcGJGdHRYWWNsSWtkUW9DQTZSSU5XUC93MnZiWERXOWVub2JOMWUybkt4bHJEY1lXazZsaFVNZ3FDQlcrQ21PMzFpVnFHYWhYYmFxc3JiTkhYbVdZbk56eGpGN0tsNzZhSHZSemNadEJlTmFNdzlNK2Y1Zy9ROVhlenBjRWRFK0JBWmN1cXJTcEdhc0oyeXEza3ErckFTQ0JqL1dOUWZ0WjZwWmJLdks3UkdkTEtTand4M2w5amdpc20yeEI1clBVZVVSY05CT1Y4VVFDRnBEV24vWmJ4c3hZb1hCRldpaC9XOSs0N1hVaVQwYkMvTWJydzgwenBnTHYrNXZ2b0Q2aTBBSU1nbExZbFhyQ1VpOElyYlE3dGhjdlJyNjJKNXV3Q0VjOG9SWVYyRS83ZmJOMVd2RnhocHpZRnQxNi9IVVZ3U0NEc1RLZ094R1M0cWFydmkvZ3FacklNa20xN1FlNnlGQ2pUaGdUREcyR0dPTU5jYWNNbHNSQ0NFaVZnS0JRQ0FRc1JJSUJBSVJxMXlJS2VMM3pjMXB4N1c3bTRNdjA1YlhqNnZiY0J4bnpUeEM5K3RiMnFwaXVQcUVvL2tyWk5xYmRTbXpvOE1UMWRpU083Q2hlZWRRVzF2SmdJNE9Tb0JDSUJDeGFnUWM4QXVxUHBpNnJXQ2Nnem5tSnhWZTM1aG5mRythcTcxUmZmNVgzRHF3L1pKbG5hNC93ZCtXblVjR1hWK1BNNHJiOSt5RDNQbys5azRYQWVMNmxTQ0kwR2haZlg1YTZkMDdoTUVKTzhzZVhhbnJMUVlFQXVHdlFhek9kbXQwV3VtOWlWS1JUK21sOTI5dGNWYjJVVTZxeFgyNCtDSGxvbmRMNGVaWGU1bjJsSWVRZW5IUGx0VGRWZmt2MzZGRlNrVWliYm1RMnRyR0xSVDlSQ0FRc1NwSFRzWExzd0xGNnUrcGVPbER4V1ZtUC9xMDdNVjkrRjVyZmFGU2tDc2JVbXlyTVVaTm1aQklBN1VmMG51TG82SVBUV0FDZ1lnMWNKaWxzN3EzMHJSOVRhMEZ5aTdkMjN4OG5pejViUnBzMEt4dkxYcjN4b0RYV0J0L1VTVmRaaDZZOHFPUzlwZWJ2aDFIRTVoQUlHSU5pTXFHRlNPVkVtdTFkYjJ5VUZyYzRpcEhmQnVQTUdtdU02NnBEa1NzQnhlZHAwWU5rRmh3alZ0Sis3UEtuL21LSmpDQlFNU3F3R0N6YW9SU1lzV3ppc3BOSGJ0UWx2aGd4TkphNThvZnpneElySTI3QnlndEQ1NFBTdHVmZFdEcUVwckFCQUlSYTBEVTIzWWNyN3Nxb09LYmMyU0pEK1FZakVmQTFyK2xTNVAyeGN2VnFCa1FYcHErL3grYmxMUi9yK0cxZC84cWs3VEszdDRMYVE2WGx6V2Z0N2l3NmFwRmhVMVhJMExzOTZyV0V3ODJ1L3E0S1JKUTFSeERmb2cvY0IzMXlWK0FXRDBkN1ZIcHBmY2xCQ0tWMUpMSnFZcnpGT0E1dUQ2SkVWL0JheE9EOW1lMUZmVGxFczVNUEt6c2hMTVMxQnF1QU9oT2xSQnJnejN6NkVpZW1Ma056aVBtNTFySDM3V2xabVdnSkRGWHJUV1d2cHBobm9iOEZRNDNrV3lnMEdYV2J4ZXNNTGk3Z2N4OXFHMXRqNG5VelNEc0ttU3liVDlKNkwvcUQrUkd0ZGl6MUpOS1UwNS9MbS82WFZ6Mnc4L3hibEFOS1lPbG5qVzN1YU5YSG1nK0Y0TzR5ZEF5ekJsb3NicXMwZHlCQlJkeE95ZSt4M3NaUUVwMmEwczE2UGEwd1lEMXZSeXA1aGxudmhDcENYQk1iZTRZM0VpaEpRazNjUFZhWXpIZGNoQmV4Q3A4cDVhNzhzSmdNN2l1eHhNcjBHRGZOVFM1YUVLWlA2RWtGOTlhV3R1NCtiUlF2bnVYcWUwb1NFRCsyYnl3NExzdTRxdytCc1lwTVZJdHJwbDNMM0xFUnVLaTMxM3ZHQVJpOUYrQTE2MnJ5c0pGbHgvbldPLzRQTGZ4NW85eUxIZStrbUYrWWZMV21tVmk2UzI3YXZFUnNRWkhySGgvdnNVNUlOenJyT1dkWVRzQmtGemFaRXMrc2FUMml6dExheGRPZ3NkQXU5c2UydUFFVDBmVWpSdXFVc1VtQVk2YVhhMExnMlMrdis2ckNTVTFuMDgybUg4ZEJYZTBTRDc2NHpZSzF0OGpWeHJzLzA2dmZ3a1p0K1IwcU0wdVR4Uk9GVk9UVFhPSVdIc0dzVDZXVkRlZi9mL3hwTHA1Z1U1ZlJLdzlITVdOem41eWVqektQeHVpRUdhM0orcTJUZFdiV0Y5ZnU4Nll2YS9CY1lUYWN2SXN6aU1lMlZhM2dJZzF2RWtLNHlSY1c4ajlUTVFhd2RocmRod3BSYXlqVmxXYUtROXRhSURqUGV2bnkxZFhHZzdZWEgyQ2tmUmh4QW9GK2U5dGNCeUo2Mm0rTFdvYWpkc3p2aWxxdWp5MjNINDJOb0ZnUFJQc1hza2JsM0orVzl4MDJkZUZUVmZDQzhLcDBSRFgwdTZKU3ZDUzFmZkZ0bEhJcll2Ykg3THJIWU53WDVsV3drQy9acGtjZzVmdGIrYno5ZjVjYXJzUXFoc2x3b2JZTzZIYVlaOU5qSy9lNkpLNVN5MWM2a3pFcW5WeXkxeXc5OCtrdXMrcGowS2c4dkZPNExGeHhqeld6N2lIcTZ1TkVVMU9UelRVQ1NDMEwvTWJ4LzFXMWp4Y3VNRHUvNzMydXd0WEdoeHlSalBvZ1VGZ2NwdXZXQjFNcmU2WTkzWTNQSFJ4cktIUnY4d3hhNDJGZUU0cGFhTWQwRU5EQ0JDckkvcjVpN3pHbTlRUUJ0WUUyZ1U3ZzFpWjBIUGozcnQybGNSWTNkTGU2NUxZU2d2N0hPNXplcEZjTUhWbVk3UWdyL0VHNFcvRXZCcmtqRnEwdVAyQUFmRWZDSkN0RWlVN1FUMHlUVzFIc1g0ZXZiclNpTnRqdStwb1ozVzZvLyt6eHpKNVpLekJKbVg4a2pLOFNHSE9Ic3M5U3V1d3hkaHk4aFZyS2lzQ2xwbHR1VGRRK3d6TjdiMXYzVlM5V1UxZEE1RlVwVjE1bVMrbjE3OG9SYTVTeENnOHFXQVRzYms4UVJOcnNIVldNOTV5ZGFIRkxZTDB1cmFqWit3eVAvWEU5cnBQUHNpMjNGZmhuYlRVTDZFQmlJMU4xTGQybWFkMGxjNE1oQTRwTTlDQ0VmNE9WOFAvSzdYK2RYZ2xRUExEdjgra21ON3psemJUdlBOSGpYVWN1SHRyemMrenN5MzN6OTlubllBRkx6VGtBZEQveXhud0pteXNUdkNYZHQvTmFuajBzMXpyTFpCaVgweXJmM1ZjbkhHZlVtS0E5SHZUaHFvZG5hNC85NzRESkk4eWNaU0gya2I0UFZRamFvZ1I2Zy9oYVFWdEQ0Wlk5YWd6RVNzaElqQjVTODF5TmxGVGE5dUdkcFZsbXJscFlYSEFTajEzaitWdUhQLzhqM2c0R3E0NDBEeGN6cGtka1dHVDRxdlhzN0xoQ3FhVVdKOU9OczB1c0I1K0drS1pRdUtIamxEcS9lOWxOVHdzUEYxQm5TSVdLSUgzUTJoUVFnelk1SVRHeEIyMWJjZjQ2eVp4dW9EUE1Ic09td0YwMFdxSThkZXk1dlBaZDlpZzBHNnR4S3BIbmNOUEZlQTBSM01WUzg3bGNxZmR4MlhjTm92TGUya3lIeTdxYmlFQzZVNWdZcG0yRHVXS1oxM0xaVTUrbmN0KzZIbHUvNmVqdWFaOS9jS2hma0x5Q0tXdnNCaXBnUUNOZG4xT0l5azFyY2V3Y2lFMUthbkQ2bkw3V1hLR2xCOUxiSmV3Wng5TXJGMGs5Z3gwbFhCTlk4L0JQUzFZb3d3a1NhYi94TCtsamE1K2NucGpuT3hZbWVnSE5lL0VVVng0US9CckdlYm53NzNPWFdlOHdzTGRlbktXYU5obzRqbWJPVXY2UUNLNWJvRFRFczN0ZnVCRjhWeXh2Ung4MmtPUHExdmoxb1g2VGFlblExTm9wQkpKd3AvVU5ocGEvcVpuVzZBZkZIcVE2R0dFS2JBNEIvamMvZFlZeThTZWdjR05QZk5Vc21tdUh0WnVlRld3WjJCWUMxVG1tb1AyTTluelVPMm9mZWQyNzJZZ0hCdDRYNFI3blVOUHJOV3JUZ3FZNFdsZGpJc3pieHRDWk5lRndFbGgyL2x4QWNjbTgrNVh1VzV5SXdOUll0S3lDUnpJY0tWVS95VzJFUFQwaGNReGUzK1RxMjlHWGR1UURZYVdVK0hPczZpZ2FVeWc4dFhXd2VKd1I3UG40WlVnOXN5VDIrcytaTS9BblVnUFluMGh0ZjROOWt5QkFxTnRYYXM3UmhpbHFQYWRtQWZDZHNBWFdTakpoMk9kUTB1c1NESVNQN1EwTUxIK2taUkVUN1VBcmx6WjkvejkzSTVydnVSVkR5VnpydWt4YW9lR0hZTzRuQ21QY3lsWEx1R0pyZnpMRVp6ZVlhcTRoMHZKdUFCVnk0ZDFWMThJcmVJNDFvWXJzY0lQRkVZUTZGSUR1VjdwUmF6d1l3MzAvQTNycTNheVo1UVlXWlhVQWZwSjlneU1Qb0ZPQ0VMclBnSnB0THl6eU9yc0w5eGs0ZE1iN25VT0hiRWltWW5TeFFzZ09Za2VDeEpFSUpiQU91SHNyVnhMZVhoYjhMRUJySXQySDFaMzVJMXQxeWxrdDlYUVM5VzRKRjIwc3J2NjQvNkUybS9ZQk41VTJUSk1xeXBBYUd6UWsxamhaL3JjRHRPN2VyZ3hxYTJEa3VlRlBxdEtNbnNwS1ZQb1g2b1dGNjAwdEdwdDl6dVpEWS81clBtYnFyY3kxVkE0MXprMHhKcDIvWHhWQ3hqWnBZSmRqQzBIZTNFYkIxWkx2aVB0aG5sY3VGNFRiVTQ2U3JaL2NsKzRWNWYzMUt3NVVkVzRyTy9keXVlVzdZWStlU3V6NFFrMmdaOU5NYzBNVlFpaWxvWFM2UFJFQ3cwclF1djE3ZkhWY1RDQXdBQTJhN2ZsQWFHbHVhdUlGUnVOTUFtTlc0ZUlJcFFKb3RGS1VzSDBQVFl4b1p2WlR5VzJpOE85enFFaDFzM0g1YXRhd0JtM3pnNTZNWmIrNStxQTc0R2FJQnlKRlJaNXVYcERDdmUwNnlBVnp4MmphbHo0UGl2czJ4MTlndVRWUWoxaXZVYlBnRkFRSy93cS9kUGM3VEU3Qm9vNXdpc3BQeFFTNjVWcktzdlpNMWFITzFxUE1vVytwZkRYVldnd1ZHUTRETlJ1aEFvTGoralkzTUs5enZvVDYvWlJQNnRhdkRsUFBScHljZ0txWTA4T1MySk5HdmxiWUlJckNuNVRVSEpsakQ5Y1RkMlRJdEpMQmtMUEFJU1VoZ3V4M3JtNUpwYjlaa2xSMHhYQkVsWW9pQlhobVhKdVExcktmSFJiM1Jmc21ab0FlbSs5RTVvZ3o0SFFCZS9qdmRiYnU3dk9nTm9FVE1FdGpKd25IMU8xZUdHa0NYWXhLdEhyTnFRT0NrdGl6UmcvTnpEQk5RYi9ucWJjZnFyR0JTNXgzZGd2UWdkM1NLM3BFcEZMWFVtc1dFaENJeFVrcDNBazF2L3VzOTdHbm9GQlJvOHloVTc3U2p3TjlFNW9namgrWC9wSTc2YUw2M2U2czg2OEs2QzdLNG5Wa25Fazd3K3BaUEhHRHlubjJxcUNkd0N2WFgrYy9IdUdsc29aZ2VDL2lLUWFPRUxoWDBUVkJKTU9FTGNOSUJRUmVWeHhkSUdEcytSeHR2U2pLMlhybm56Wmovb0VCWGlpdUxUclBsZE1yRkFkZENPeGdyU0VWbDFFRHlHTFZIY1RxOUF3QlAyZlV0ZW9yaVJXdUh5eForQWhFTWhsVFppeVQ2cE1qQWM3UlNEeGVLV0tJQXF4QUFXMTdZYXVXQ2lKVDArcmY3bXI2d3dJRXpLVk5ybjZkaDJ4cXRIblZTNDlYYmRJb3F6N3BrdSt4N2hNTW1VY01oaUpLYSsxWHVXQmFJKy9iNm41emI4OHhDYzNpeVdVZ0lGSVNoMEFBNUtLRzEwVmVRYkVIMTBXY0Z6ZzhxV0hYbGVIeUNWL055WkVHMEg2YUpEUkhZSUFrWUFFS2ZMMFZnVUlFekxEWWkyVzJnN2pqRFNDd2xzbnVwSllZVGxIOEFCN0RoNE1Za2RodUxMQlhVell4M0oxUUw0QzloeHlES3lyc0o4bWxXUUYzZ2h3ajNvaXFlNVR2Zm8rVFJCNnF6UStYODg2QS85SXFGMHN6R01oRk1Edy8rM1ZyY2VGamxnaEhaVitlQlZQREtJR21YNk5mQWlsbms3b3VFK3E4SzFidUExOWJiNzNiRDBsazZ0Wks1c3dWeXFOR0N5S1VwbDE1QUNwU3NyUytGVkI0MWpKYUNqY2k0V2dDYUc3a3pYekNOMEpxM0hQQU5rZ0FVaTFJT0F3VVpYQTNVck1iUVprTUdWNzNVY3pzeG9lZ1VFSngxK29EK0JVTHBSMDlTWlcxTWMvTlNDUzgyQWpCa2toMGttc3ZsMUpyR0laMlRDZmtYUUY5Y1JwNnFIRTJxK0VQcUpLeXNSbTVwL1FCSWxkcHFYV3owRHlHVnowaVBHQVo0U3dEL1Mwc01OTHBMdnFET0EyWUdGWk9PR2luRS8yV2lmZHM3Vm1hZGNrWVFFeDdKMzZDSmR5K1hmOGNUemxpbTk0WXhWdU1RM1ZZa1E0Sm1MZVd5dDZCU0p1N0Z4aWs4c1hSbWZ1SEVhbkJKL3V0VTZVS2c5WmtBSkdSalZtRCtBY3RhRzlTd3Z2S2ZsZ0xIL1JJY0tPRTg1STRqTC8vZ1pYc1hoNE9McWxJY1piR0lXakZ2Q0xoWTVPajhXTk9TTzg4a1VLSVA1QWZyU2hKRlk4SjNSYms2c25rcm1vU2NFSEFsSFQvM0QvMG90WUVkM21mNHJwcWpvektWOHVyU05sdC9wRGI0UGRTNnBqdGJqNUlKUlJxdE9Sc28xQ2E3V0h1aUxUMWZUMCt1bFNZeWFjM01pUUJiZVpFcG5FRzFvWE40Nk1pQ1gzVCtQSHlvRlVoZXhVM1dXOEVzNXZuS0RnWHl2V1R5eUxsdG82b1AwSTI1MjJ3L1NXTU5tTFAzQUxBTnljeEhTYndiZ3V3VWRZN1VXUmV0U1pBUUtYMER2RS8xWVJTaHZvbDFoWENPUnAxRkllL0FiRnNyVmpNTFZJd0FSeGtpMjN1ZnJncWhKazlsOWMySFFWdXhJRjNnUE5DbFU0d1Y1bkRIMTZYSVg5ZEVoOGVEL1VCTUxOV0VuNWF1dWdwYzRnTVJoamZpaXhqWUtMR09wYzE2cXVubkpHUnRncGZpcTFqWVNxQzltMzRpdGJUZ21VSVN5WWQrSjRyelJWbjU1MTlnYzJiUVFzb0F6by9YZWEyb2JJR1FyL1Vvc1V1enAwWThKb0ZSdzc3VUZrcllmUlJTak5RQStYRU9CeU5BS0JFTm40U3pZYW9qOHNlc2pLcnNmTnE5aTVRTERJU3U5dzA0V0RCQUlSYTFnZkF6MVJka2Q1M3paWFRTKzN4L0dYUEFZNzJ5M1Jkc2ZCdmk1M1V6Uk5XQUtCaUZWNzFJUEhHVlZXdCtqbTFKSy83MHdzR01NQnlVWGpLOHBOMzE3YjdyYi9KUWpWWkVzK2NkZUJKNWF5OW04cnVOYSt6L0RtRzgxdEpmMXA0aElJUkt5cTRHeTNSdTh1ZjNZaEl4Ui9lTC83TXRLbFY0UDUxMUZTN2Q5ZWVGTnR2VzNIOFRSNUNRUWlWc1hJTjc3L3ZCU3BNT0FaVGVVajYxVkR5bURPWHFKLzlpdDNheFJuVGg3TVdiT09DQ2F2cXNXZWRYUml3VFZ1dWZZbkZWNXZjYlNiWTJnQ0V3aEVyQUhSNHFqb2sxZ3cxaFdJV0xjVmpITkFzbFdWbENUMTJpODZKM2Nlc1pwMzBOY2o4cXpvblpzNjVZaU5QNmFZSy90c2xKYnk5aG5lbUJHby9VQnA3Y0pKTklFSkJDTFdnS2kyYmpoYkNha0FOWTN4eXBKMDRQb1loTHVLaFhSdUdtemdiUG5CUllidGVld3B5WkRSQXdzdVV1VU81bW1MZ2k1VlNmdWhMb240Q2VwMlJuSDdmLzBiMTk1R2k3V3JBRUhod0txVHFNOGppRmdONW1XWEtpWFdLc3ZhNFlySzNmdjB3N0pKU0xJZmZrNTdHSy8zMkMrYnVMcXZUVTBhUU9pT2xiWWZocTJJbjZDYmJuK1hXK1Q5NzhiYlp2RWtHNVlrRkh2eVlYVnoyYjBid3ZKaG5ET0lGSkJJakxOMy9paSsvTU5PWUFkNmN3MEtyaS9QZXU5NkhtcmVtL2pJTTN5ZmI1Z3doOC9Kb1dkL3RiZEVjZG4vdVpyYi9jRlkvditxaFppUHIrRGJvMGNvdHNjZHhaV3ZPVkZ4V1NyN01xd21xcms1N1ZpbHhGTFg5THV5Qy9BMkg1OG5uMmJ3NkRMTmRTNmVkVzNBN0ZFeTJiWkVVNWdWamE5UTB2NTg0NnhuSXA1WW15dDZjZDhmVTh3djlHMlBUMUgxVzdZUWdvWGNPM0svdUppdjIvcWJQdW4wZWV4bFAvS2Y3L253S2tWMWRWaWp1Y0p2enVHc2dwc3ZtaXQ3OFdYOGNGeitZYyt2dTJFZXQ3aDNLN2Z6elFsY2U2dDB1Zmc5b0tiZnNCbXN2K1ZEL25jZ1diM0cwbW1MNHVLdSs1d3ZkM0V2QjFleDRUalZaZnh3YkNIL2U0OE9GMjl1R0QrWEwydmY1NWNvZWw1bFg0YVhtMVZIZTFSSzhhUzhRS1N5bytTT0hNV2VBWnVPcXBDLzc4azd5RnFEQkpRazNUWjhmNWFxSkNTMUN5Y3BJVmFUTGVta2lDRFBRTVFHMHZwMllEVlByR3BJa0MyRVlDRjVXaW5xd3kwWllPWVdSYnU1NnVUQm5iNHJXWG82Lzl0dkJ4bTVaZ1hadzNiUHVlYVFaRDd4UGQ5bmRSbEg4cC9GWG5yNHlhU2xKb2FYNHZIOTh2UGpPSk5FWmpTeE5palpURURZM3h4aDR0YmQrR2xRRzQ5dmsvRDJ3ZXFyRnZOMVdUdHVBZDhtbEYrM1MxMUd0eCs5UXBKZXhGcXg4VmkrckNYOUxaejFqMnVKNU5ySitsSmhYNFRkUXF0clNqd2xFS2xVV3pjb0o2dGs3eUNHS3JsMDVZOW5CQ1JXczkraUMraHUxaENUVWp3eFg2NzlPUlV2ejhZbUZCSEVxaGNCK2hOSW9JV2c5SHV4T3R1cllyaGZ6a2ppdjkveHIzc09WeEYwSEpJcUdWbktiZHl1NWtPUzJPSVlGOWVROStmeHZtekZLZnp2Ti8vOURYRTFoTGZNbkU5SDg3L2JjdmVyaW9sVnovNE9KTUhobUozM3Z5UDREUWJQUXJVRENSc2J3eStuSjNQZkRTbm55bGNyRnhBZ3ZhTWN1ZjVVc3hFa1BmR0VqK3hSVngzN0ltejlPTVdNT1Bpc3BqSCtkRlhsR1g4NVZaYjREbnh4c2ZialRVTzBiREpwa0xvR2FialZXZFU3WS8vRGE2WDhlTnZkelpGejNGY2lDV2c1dGdkYStGcS9iek5IYzcrTldNMS9CL0tVMGtNMmx2NGgwWHIvQkFGS1Nxc2ZqT1dmU1huKy9zUDBpZmc4L1ZWNTc0K2ExRUU4T2FzbDFtRDdPcUJFNzVVQzExenpwZS9vRHlrWWFUNTkzeGYzNFg0K2JZZHZjNUl5bGduZnlmcFRyRDdtdmYwVmJSeitZOG5VQy9uL2QzNUVTNndNYmE2NlhqZ1c1MWErTXgwUlI0YUdYMGNodEZXOWdhRkRXaGNLdzFhd3VRSXNhUU5GcitORzNsTjdxV1ovV2FnNjZtM0pKeFJVelgxaVgrV00xL09yWmo5dGJrNC90cU1qd25JUmFORUQ2bEd1bHUraEoxeDErWGY4NXl0Ry9oYlFPQVc5S1NzbmI2SDRQVXpmRGpZY0lwY1g3dTIwU0ZkZnVlU1FFV244WEYwM0Y3bDJRNUtFc1V5SlI0QlVPU0JQcURhKzdtdno5Wk1wUy96STMycUs1dUt1bjg4L3QvS1NaWjEwekdvbGJIaVB5RzBjVXZVdC92NnNReWVEeWE5SHJJNDFwQUFCNWs2N2o5dDF4OXRjenBUSCtVQUIzUlR6OWRFOGVlUEtHQ1NSTHZjdUlueEdiaWZCRXlBV0d3akszMGdEeXpzV0JReGNYVUdza0FyWjhmN25VMU41ZFlBaVMvYUhWL25LZ25UcXJ4OE01ZEZjVHNjcWRteG5CcDN0Q201VDlpK0h2OVhETzA3THoxdlBsL0YxSHp1WDVWMFRnYnc1UU1TUVdQR2JiNDZzNVkxSlFvOEJJVGt5c3NiejdEUG9hL0ZaMmNxVFpjZFY2bk1JS29iNFkzd0NsaGFKbGJKYkVjS2FXRnRxWTdqaUg4L29KREg5Y21ZaS83M3g5ODQzdDJJaDRmUGZMbGlyU2NXZ1JzY0sxNlpmaDIvay8vNSthS25QMktIMHhMUnp4bmhmZVd2SEx1UnM1YjFsZFlMcHI5eCt5UGpsUFFtRlVoMGlwbnBJZXZJeDFYZWc1WHh5T2ZmakNmdDg3NE5VcjhRZHpGKzZaOFNKNC9tZWo2NDg3QmtZdlBDOXNQK1N2RUlTUG9QcmxGSmlsZXMzbmZUTXRMQUo0VU9zUzRmdDVQOWZsVGprVCtQajB3L3puMEZISi94ZHdrUFArNlRBVUJyRm1Ic1Qvci8wbEV6SlkyMGdsUDV5cW84WThDK2tPYWxuaTc0N20zOHUrWm1IZEQwTlNKRnh4dXUzOFhyUW4wN0s1akpuM3FDYXpObkdBVGV6OHJVbmFDb0RzT1QzOVVuTldlOWZkOWdHQlVNZHZtdXAvdk8wa1BqUHFmeG5GZXVQVTB5c2N1UVlTSldnVU05TUM1dlF2UTcyd3NtWjhQQnovUDh6MzczeFQ4bjB0MkdIcEtEUjMzYzZkakxkWktPSUhoc1NsNTRTS3lRa0xIam9JTFVhZStEZUE2di9pb3RXOHVVVy8zQ21aTDh3dlNOSUl4aC9XeW5TMEJzNHVxTjk3RGdkekNhR01pbzNIM09ZVVJCL3MrY2NnbkIydHNIaU4xb2tWcWsrVW1NSUpHSWxoQlZnREdMNk9QNDR1T1JjMzNIWlo3bXRqK1o5UlFHMm9PQmN6bzZjb2k1TTlqOG52WlN4VDZzT1ZpdHBNT3MrU0I4a0pQVmVrTGhlYms5S0pOYmZIM2hSa1pIcys2UExEdWszcDkwWGtOUzFibWF5b2VtbWFOL3ZoYXFLcmZlL2RFaFZsSEMwSmgwclNheUV5SXVzK2lPNkNINk9mS2htV1c4ZjBRb05HRXpLcTl3NjlKQmU3Y25IK0w4UjhpbFdibHREZE1pSVZiaXdkcjE5OHlGM0l1OFJWWW9zbHY0dC9UQXBYQTdicHo1eVNPMHdiQ2RmN3E2M2JwRXNPL1hGdS9sbnZ6dXFRbE1iMFRmWXhGQkhaNU4wbmVES2hIS1duYjAxS0MrYVlEeEFhdE1IaWthaXdZY1huMWR2UDBwWFlnMmxqaFhYLytMQ3RybDdMSGZqdHNUUGNxMjNyRHpRZkc2YjIwT2tFR0hBbUdKc01jWVlhOXhNaXJISEhBaWRwK1hkMVNRQUFBd2dTVVJCVkVaK1gxLzBFRnZvVE04cWpHWmlCb3JzdVdONGFZWDNQZlJLc0haampLUURQOU5saHNvZGkzK1BNY1pIZ2xMUFFJWEIxLzAvVndmc2ovbzlBL2gyd1loaktlakxxenRnMUpGNm51azJvZFBVMG9haWI4OFdOUXo2eDlRejFRU2U3eTdYT25hYVFUU1lXRDZKMnJTQklaRllzWGt5WDFjOUpOWVZCNXFIUzEwOVBDN091Ty9ib3FiUjdnNjYzNm1uQThTSlcwZkh4aG56eE1ZYWN3Q0VHNUwzTTh0Ky9CMXYvM20wdTIvNm9UajdqNi80TTNUNHl4RytLS1RLTFVOOXp2bVNCSlU5NEZDNDRnQnpTQ0t2R0JBMXhPY0t1T1ZEeVdmZ3RhQWtKaDFyaWNYU3B6ejNnTTlTRDNJVnM3QlhiUnZDRzV3Z1JVcEptM0p0Y0ZpaStlTTlQQS93SGh5MXBTeitMQitDa3ZXdW81TjlKNlQ5KzA3K3Q2a3ZUZTcwT2VxRnordi9TQUVhTExFSzY0RVRGRFlWRmpuR0cxRzlteG1DQ2VERHJNYmRDZzdvdUd0YjdpNTNodG5abHZzN2lGeDdMREIyNys5dWVGREpXQy9JYTd4Qjk3R0dVendtYTlyMHUvN01SdVk5M3ZzN2JDT21ISi85ZEhLV2ozaGhaWmNrdkxVbmRFbklKcXUvWExJVkpvRUhrdmJZSm9PakxveGt6TGdIQ1ExbENLVnorTzVDaWxyU3I1RXo1L1RYTENIaWVNMGZwZitRa21FSVFpUVhHK2VhSFlONHRRdzJLSkJNVjRRb1MyMDZ2NTZ6V2JRZjE0ejVpdi9jOGtmNnoyQ0psZmtzZzBEUkp3aGNnSUdVUFlmRU5GRFJvRS93LzZxa284UTJuTU0rd04zWlNoWWFBKzR1ajB4SnJpV3F5aEkzdktqNmt3ZnpqZTgvVjJGZWVybmRVZFl2a3RxNHVMRHBLalZqdmJUVWRwR3VkV0M2VXlRdDhTMzJ0SUUrRWhWYWhPR0NWTGZ6U0g2aEkrT1ZYSFRRdnM4T2tUUGkwVU1sc2FKT3FDT2VRY0lVcWVmZzlDNTBZQmMxek5SRiszU3h6RzFJcUc2QTd5elVKVkFQNE8vZkxvemxueTM4ZXJndVIrL0drajY4V3h2ekl3V1p3TjBKcWhTUUNJdHFDamFwamxhSkZXVFBuMEM4R3dra2JlRjNxQ3UrYTlyZk8yaGloZEVUdVEzZ2RzWk9EbXllQ1o4RDBTTFBBRTRNK096WGN6ZjVuMGc2dlFSWE4xKzd6cGl0WnJGZEhHdG9iSFpGbHM2MXNXWGZvTFRTZXhORmJpNW94WVdHa1pBQXhlWWRzNUd4QnB1YXNiNStmVldtVTYvcnZSR256UXhWUXZjWlRPU1NuODdneVVicVdKcjI4aDJ5WmNmZk5lT1FzL3VVeDBPbVk0VXhpbDlVd3pkSzV2UUVDYkJ5b01JUXpWSGFHdVhML0NTVzBJVnROamkyZytqWW9vZStPZEFKUXExT0UvNmgyeDU3cXBNRWlRUWxiVHBGRVdyVnNiSTZKWWprVG1hQkd6Q0VxaVZXb1ZzZS92Nzl3V244LzZHUzhmZFhGdnM5QWtWd3NtTGVFbExFQ2wyYW1vWEdzTDdDZm1xa2tHcUw0MkNmcE1JYlRYTFpwY3Jydnh2YjA5c1pWMkUvWGN0WXh4NW9Qa2VYT3V5ZVBjNlh4RnBwQkJQMGlmZ05VdmJKU1pMTXg3VllKbVdqVm1KRlBmZzhySEFCUThUUDJoTWs2OHU4Qm5pSjZrQnZVWC9iTGZlOGNpaUN6Q3VGU2tuaE1Hb3hJdzJUNXVFaG9TZXhncGh3L0dWQkRDQXNsazBLZWxoOEZ5ekJhaUZXNkpMWjc1amxYd2gyYW1CMVU1b3JBTWQ5WkI1am4yUHpnc1FLVno2eGZwV3JPMzdyMXplZEh2aDRyL1YyTFl2dDMrbjFMMFVLc1dhVlQxMnM0TTZ0VnR6UDFaUGJPVDJ0L21VdFkvM3BYdXZFb044UHlZZ2RrU0hKS1hHQWgwVEFRa3JsanBFRmk0ZjdudE9hQkJyeDdhSkpXQnIvSkVJZzQ0MWJPK1ZVOVZtUzM3bkpKOG5JK2R1eXNtQWNhY2p0MTRtVWNlekg4WkxGd2pPTE9DUmw1bE1LVlFvaXo2UTJta0JFaG5ISS9aK1J2TXNWZTVZWnM5QUg4RE9HU2dDZnNlL2cvZ1YzTmkySnhkVmsxbUxrdCt5c0JNbWsyd2k0WUNvQ2RtcFFvc05GNkRSTFJwNzQ2Tk44bmxmb3JHRWtsY3BXRnN3TkFtOW5Oanl1WmJGTlM2MmZFUW1rNm5MYm9wWGVZRkRac09LaW50elc1M2VZM3RZeTF1OW1OVHdhOVB1WkpaZmxHdFhUME1TT2hobXZUVlFsUFRGaWhPc1NDSXpGckhlS2h6OHh4NmUrZ1B1UHNEem9RTVhxaEp5alVvbW84VDc0b0RJZExWUWdPTzZ5NHo0akRYd21kRCtERndBSUZXV3o1MEN5TUFJaWo2c2NzVUpmaXVmV1hMM0lKM1VEK0J0dEFwbUs2WUNocHNDdEJUekJlazhFT0hHRU9wa01TK1lOcVpUcFZxRUNZV0cwMEtFemRZVlNCMytoWGhha0NrSStHSGVDVDFLSHJodSt4RGh0WUZOQk96RTM4RjRZS1pIREFLNXorQnRqZ08veG5OQ0x4WjlZMzhwc2VFTExZbnRybDNsS1pPaFc5dzVTU3F3RlZSOU03Y2x0ZlhPWGVhcVdzY2JtRy9UNzk4NjdsQ2NGZGp6Vzh4b1ZsQTFDREpSOXlwOTB4SWhSbU9xUEpTbUJCQVZyK1dIcDUzNDRrMStNSUhSSTE4blBQc2psTHJqSVorR1hVbHV3UEtJQTNzZmVqY1VQaVZWTU9oUkswSEQ5WVpaeFBuM2U4bUd5eE1xQ0s1ai9MZlRWRm9VWGFpTFRHRFpETVQyM1htUFlhU3puaitJM0ZtSHVpSlVYTCs4MFJqQWdRVldpeGtnRmw2MzFOMy9VS2ZNV1BDMVlzSUVXYkpvMFU1Slk0eXRiVHRHeTJCYm1OMTdmWmFUZ2J2UHVNRjlkd08yNmF3YVhjdFZpL2dhQVl1OUFOQmYyRGJic1ZxZXh0MUppTGF0YmRITnd4MkV2cVpSNmQ4Q1VLNWR3S1ZkOHc2Y3ozUC9wYU03VjFDWDlDUGNwTFdPOXhkaHlzaTQ1QXVUY2hJSXVlMi9nc3YyUGRzaXF4VDZERk9Ldk80V2pQRzR2OVlUUWNNbm5EdmJXUTVpOVNTbGcyZmV2c3hqSmdFRGdoV0U3MkV0ekZCVmNqSnhkTUU4eGx2N1NQdG9JaVpGSmlWQ1pLRG15Q3orSFBsa3FseTU4aHBHVHRtRFJlZnhHQnc4VEpDcm4zNG5Vb043M2lxR21jeHJTem9hYmRrL1VxRldWWnJWZUFhWTJkMHlYa0dwOXd0SGNWdSt4UUNwai8zN3ZjUzBJWDhzTzcwQnVMN3JGb0lSWTYyMDdqdGZjRHR5RHRkNTdyQks5a3R0N05LeVVTZENoRStwYTNURVlPelZqZmVtcVNsTnJPMFhkL2VXVGlCUFU2VmlCLzhscnZGSE5ZcHVUYmJtM1N5cmI1SlZ3TmcweUJyeGpLczh2TWtNbEtzdy9qMVp5OWJSbWw2c3k3dzRZcUEyNDROQzBkV2lvK3hRQkhtckdHbEl1TFpvZUJDM1hYeE5DUTZ5SXJubDlwL2taSlFzTkJwQ1dycEJnY0NUQVZTZUJDSW5Ca2o1UXU5VHFqc3FwbUQ1WGlsU1RpeWFVMjFxTEJtZ3EzMUVYelcwWVlGYlVCdHlsNVc0SmFiOWk3SjVOTWMxVU10YlF5VktVSFlHZ2tWZ0JwNmNqNnFNY3k1MVNhb0VMVmhqY2tIWkNtcUNqazk0alpiQmlVZ1hTL0JJMWFDQlhnM25acGJobW14SHE5c0tiYWhHQjVXZzNhMWQ3Rkw1OXM2cDJWS21JZUFraVZ3Q1NybUJNeGNZYWMrRGpIT3NkVGcrUktvRVFGTEV5bU52YzBUait2WkpoZm1GcXNtbk8wOG1tMlQrWDJpNkVmcTVMSzVyL3l1MnFDR216d0UwbXlGaDZYR0RZM0ZiYUgyUWJkSmtaRSthb2FrZm1QYTkwVlI5alRERzJHR01BWXc2akpPWUFMUlFDUVVkaURSdmsvZnRPVllTMG9iOGxMTnV4L1pKbHF0cVJjZXRzbXFRRUFoRnJhRkQ2MFpXcUNHbXJUSDdNN2tUcTJJWGhLckVTQ0lTL0dyRTJwQTVTUlVnRnIwME15M1ljK1B3U1ZlMG9tM2NwVFZJQ2dZaTErNlU5K0lmYThzSXp2VjliZFF5M2NXQzFvblpzT1NtYmM3ZlNKQ1VRaUZoRENPdXVJN2o0b2FVQkNhbGk4ZkN3YmtmZHhtTjVQMVZaSGZFQU0xZTc3bmlhb0FRQ0VXdm9ZUy90dzIwN2I3Mmt3YXBrempVOW9oMlZQNTNCKzZsSzZZY2JkdytneVVrZ0VMRjJIUkNyYmRveWxOdnorQlRlWHhVeDl2a3YzOEcxVmNYMHFIWTRMZEZjMFRzMzhUa1AwbS82aE52OTREU3VOdTRFenVPa2lVa2dFTEVTQ0FRQ2dZaVZRQ0FRaUZnSkJBS0JpSlZBSUJDSVdBa0VBb0ZBeEVvZ0VBaEVyQVFDZ1VERVNpQVFDQVFpVmdLQlFDQmlKUkFJQkNKV0FvRkFJQkN4RWdnRUFoRXJnVUFnRUxGR0ZEd2RIVkdsVGE2K3NRZWF6L21weEhieDNnYkhrWFJMS1lGQUlHTFZBTnpLR2xkaFAvMmFPR08rL3pYUWw4UldXbkJqcllzSWxrQWdFTEVxUTB1N0orckZ0UHBYL1FuVkgvZHNyVmxhYVcvdlRYMUdJQkNJV0FQZ2cyekxmWUZJbGVFZkNiV0wyMGx5SlJBSVJLelNTS3R0RzZxVVZCa1dGVFpkVFgxSElCQ0lXQ1h3YklwcHBscGlIYlBXV0FpZExQVWZnVUFnWWhVeFdGMjkxbGlzbGxpQmltYlN0UklJQkNMV3d3QnkxRUtxd0xvSysyazBpUWdFQWhHckh4cWRubWl0eEpwYTJ6YVVKaEdCUUNCaUZjR2srT3IxYWtuMWdoVUd0ODNsb2Y0akVBaEVyR0tZblcyNVh5Mnh3cCtWK281QUlCQ3hTcURCNFk0ZUcyZk1VMHFxSTFZWVhEbG14MERxT3dLQlFNUXFnOStyV2s5VVNxeWY1VnB2b1Q0akVBaEVyQXFRWE5ONjdIWHJxcktrQ0hYVXFrcnowbExiUlI3eVh5VVFDRVNzNnJ3RTV1ZGF4eitjV1BzbEVxL2cyRDk1UzgzeTkzYzNQRmh1Yy9XaFBpSVFDRVNzUVFDcEF1M3RaUGtuRUFoRXJBUUNnVURFU2lBUUNFU3NCQUtCUUpERS93Tnl0Z3FTS3duMzdBQUFBQUJKUlU1RXJrSmdnZz09IiBpZD0ic3ZnXzEiIGhlaWdodD0iOTguMjA2NTIxIiB3aWR0aD0iMTk0Ljk5OTk5OCIgeT0iMTAuNzkzNDc5IiB4PSIzLjAwMDAwMiIvPgogPC9nPgo8L3N2Zz4=) no-repeat center center;
display: block;
}
.custom-header-logo {
width: 200px;
height: 120px;
margin: 0 auto;
}
.custom-icon-logo {
width: 100px;
height: 60px;
}
/* === corners === */
.custom-corner-all {
border: 1px solid #9FD9F2;
-webkit-border-radius: .375em;
border-radius: .375em;
}
/* === wrapper === */
.custom-wrapper {
max-width: 40em;
padding: 0 1em;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: block;
margin: 0 auto;
}
@media (max-width: 40em) {
.custom-wrapper {
max-width: none;
width: 100%;
}
}
/* === description === */
.custom-description {
padding: .5em 0;
}
.custom-description > span {
display: inline-block;
vertical-align: middle;
text-align: center;
width: 15%;
line-height: 90px;
height: 90px;
}
.custom-description .custom-icon {
color: #24A9E1;
font-size: 2em;
}
.custom-description > .custom-icon-logo {
width: 35%;
}
/* === ipv4/6 info === */
.custom-description .custom-icon:nth-child(2):after {
content: "IPv4";
}
.custom-description .custom-icon:nth-child(4):after {
content: "IPv6";
}
.custom-description .custom-icon:nth-child(2):after,
.custom-description .custom-icon:nth-child(4):after {
display: block;
line-height: 1em;
font-family: Arial;
font-size: .6em;
margin-top: -32px;
}
@media (max-width: 40em) {
.custom-description > .custom-grandenet-logo {
background-position: 13px 0px;
background-size: 9em;
}
.custom-description > .custom-grandenet-logo {
width: 30%;
}
}
/* === button === */
.btn {
background-color:#FFC131;
background-image:-webkit-linear-gradient(left top, #FFC131 42%, #FFB100 70%);
background-image:linear-gradient(left top, #FFC131 42%, #FFB100 70%);
display: block;
text-align: center;
padding: .5em 0;
-webkit-border-radius: .375em;
border-radius: .375em;
color: black;
text-decoration: none;
font-weight: bold;
}
.btn:hover {
background: #FFB100;
text-decoration: none;
font-weight: bold;
}
.btn:hover {
background: #FFC131;
text-decoration: none;
}
/* === table === */
table {
background: rgba(159,217,242,.15);
border: 1px solid #24A9E1;
border-radius: .375em;
padding: .5em;
margin-bottom: 1em;
}
table th, table td {
text-align: right;
}
table tr td:first-child {
text-align: left;
}
table tr {
border: 1px solid #24A9E1;
}
/* result */
.table h2 {
font-weight: normal;
text-align: center;
}
.table p {
display: inline-block;
vertical-align: middle;
max-width: 80%;
}
.winner th, .winner td {
background: #24A9E1;
}
@media (max-width: 26em) {
table {
padding: 0;
}
table tr td, table tr th {
font-size: .6em;
}
}
/* spinner */
.custom-icon-spinner {
margin-right: .25em;
padding-top: 10px;
-webkit-animation-name: spin;
-webkit-animation-duration: 600ms;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
animation-name: spin;
animation-duration: 600ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
font-size: 2em;
display: inline-block;
width: auto;
height: auto;
vertical-align: middle;
}
</style>
</head>
<body>
<div class="custom-wrapper custom-corner-all">
<header>
<span class="custom-grandenet-logo custom-header-logo"></span>
</header>
<div class="table">
<span class="custom-icon custom-icon-spinner"></span>
<p>Testing all possible frontends, please wait...</p>
</div>
<div class="custom-description">
<span class="custom-corner-all custom-icon custom-icon-user"></span>
<span class="custom-icon custom-icon-forward"></span>
<span class="custom-corner-all custom-grandenet-logo custom-icon-logo"></span>
<span class="custom-icon custom-icon-forward"></span>
<span class="custom-corner-all custom-icon custom-icon-harddrive"></span>
</div>
<div class=link></div>
</div>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment