<!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">
/* Configuration start */
var test_url_list = [
      { name : 'Site 1',
        testurl: "http://site1.app.example.com/MYTESTFILE.txt",
        redirect: "http://site1.app.example.com"},
      { name : 'Site 2',
        testurl: "http://site2.app.example.com/MYTESTFILE.txt",
        redirect: "http://site2.app.example.com"}
    ];
/* Configuration finishes */
/* DO NOT EDIT BELOW */
      (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 = [];

  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>";
        if (table_dict[u].url) {
          msg += '<a href="' + table_dict[u].url + '">' + u + '</a>';
        } else {
          msg += u;
        }
        msg += "</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>