(function (w,d){
  var api = function () {
    return api.dispatch(arguments);
  };

  api.dispatch = function dispatch_(cmd) {
    var method = cmd[0];
    var params = Array.prototype.slice.call(cmd, 1);
    var fn = (typeof method === "function") ? method : this[method];
    fn.apply(this, params);
  };

  // Set the config value for `k` to be `v`.
  api.config = Object.assign(function config_(k, v) {
    this.config[k] = v; // because functions are objects too :)
  }, {
    baseuri: '//otherpeoplespixels.com/log/locke',
    filename: 'clear.gif'
  });

  // init params
  api.params = {};

  // Set the param value for `k` to be `v`.
  api.set = function set_(k, v) {
    this.params[k] = v;
  };

  // Set the value of `k` to be `top,right,bottom,left,width,height` of `child`
  // relative to `parent` or viewport if not provided`api.getRect(id)`
  api.rect = function rect_(k, child, parent) {
    this.set(k, this.getRect(child, parent) || 'not-found');
  };

  function geometry(w, h, x, y) {
    return [
      /* size */
      Math.round(w), 'x',
      Math.round(h),
      /* offset */
      (x < 0 ? '' : '+'),
      Math.round(x),
      (y < 0 ? '' : '+'),
      Math.round(y)
    ].join('');
  }

  // Return the size and offset of the element matching `id` relative to the
  // viewport or the element matching the provided opt_id. In either case the
  // returned offsets are adjusted to accomodate the current scroll position.
  api.getRect = function getRect_(id, opt_id) {
    var el = d.getElementById(id);
    // get css bounding box for element to be measured
    var rect = el && el.getBoundingClientRect();
    // get scroll offset for viewport or opt_id element
    var scroll = rect && opt_id ? (function () {
      var el = d.getElementById(opt_id);
      return el && { x: el.scrollLeft, y: el.scrollTop };
    }()) : { x: w.pageXOffset, y: w.pageYOffset };

    if (rect && scroll) {
      return geometry(
        rect.width || el.offsetWidth,
        rect.height || el.offsetHeight,
        rect.left + scroll.x,
        rect.top + scroll.y
      );
    }
  };

  api.uri = function uri_() {
    var config = this.config;
    var params = this.params
    var uri = [config.baseuri, config.pathname, config.filename]
        .filter(Boolean).join('/');
    return Object.keys(params).reduce(function (uri, k) {
      return uri + '&' + k + '=' + encodeURIComponent(params[k]);
    }, uri + '?');
  };

  api.send = function send_() {
    var img = d.createElement('img');
    img.onload = img.onerror = function () { img.parentNode.removeChild(img) };
    img.src = this.uri();
    img.width = img.height = 1;
    img.style.display = 'none';
    d.body.appendChild(img);
  };

  function viewportSize() {
    var el = d.documentElement || d.body;
    return el.clientWidth + 'x' + el.clientHeight;
  }

  (function () {
    // get command queue from oppa global
    var q = (w.oppa && w.oppa.q) || [];
    // override global
    w.oppa = api;

    // run after a short delay
    setTimeout(function () {
      var params = api.params;
      params.x = window.devicePixelRatio || 1;
      params.v = viewportSize();
      !!q && q.forEach(function (cmd) {
        api.dispatch(cmd);
      });
    }, 10);
  })();
}(window,document));
