/*global HTMLDocument, Window, console */
this.Costanza = (function() {
  "use strict";

  function defaultReporter(info, err) {
    /*jshint eqnull:true */
    console.error('Costanza error:'
          + '\n  type: ' + info.type
          + '\n  section: ' + info.section
          + (info.url ? '\n  url: ' + info.url : '')
          + (info.line != null ? '\n  line: ' + info.line : '')
          + ((info.stack && info.stack.indexOf(info.msg) < 0) ? '\n\n' + info.msg : ''),
          + '\n\n' + (info.stack || info.msg));
  }

  var reportCallback = defaultReporter,
      currentSection = 'global',
      _onError,
      _setTimeout,
      _setInterval;

  function init(_reportCallback, options) {
    reportCallback = _reportCallback || defaultReporter;

    if (!window.onerror || !window.onerror._costanza) {
      _onError = _onError || window.onerror;
      window.onerror = onErrorRoot;

      window.addEventListener('error', onError, true);
    }

    // Allow users to opt out of the native prototype augmentation.
    if (options && options.safeMode) {
      return;
    }

    if (!setTimeout._costanza) {
      _setTimeout = setTimeout;
      window.setTimeout = function(callback, duration) {
        return _setTimeout(bind(callback), duration);
      };
      setTimeout._costanza = true;
    }

    if (!setInterval._costanza) {
      _setInterval = setInterval;
      window.setInterval = function(callback, interval) {
        return _setInterval(bind(callback), interval);
      };
      setInterval._costanza = true;
    }

    if (window.Element && Element.prototype.addEventListener && !Element.prototype.addEventListener._costanza) {

      wrapListener(Element.prototype);
      if (window.HTMLDocument) {
        wrapListener(HTMLDocument.prototype);
      }
      if (window.Window) {
        wrapListener(Window.prototype);
      }
    }

    function wrapListener(proto) {
      // We must pair the native implementation with the proper proto object as ios7 will throw
      // if they are not.
      proto._addEventListener = proto.addEventListener;
      proto.addEventListener = function(type, callback, useCapture) {
        callback._section = callback._section || bind(callback);
        this._addEventListener(type, callback._section, useCapture);
      };
      proto.addEventListener._costanza = true;

      proto._removeEventListener = proto.removeEventListener;
      proto.removeEventListener = function(type, callback, useCapture) {
        this._removeEventListener(type, callback._section || callback, useCapture);
      };
      proto.removeEventListener._costanza = true;
    }
  }
  function cleanup() {
    function cleanupListener(proto) {
      proto.addEventListener = proto._addEventListener;
      proto.removeEventListener = proto._removeEventListener;
    }

    reportCallback = defaultReporter;
    if (setTimeout._costanza) {
      window.setTimeout = _setTimeout;
    }
    if (setInterval._costanza) {
      window.setInterval = _setInterval;
    }
    if (window.Element && Element.prototype.addEventListener && Element.prototype.addEventListener._costanza) {
      cleanupListener(Element.prototype);
      if (window.HTMLDocument) {
        cleanupListener(HTMLDocument.prototype);
      }
      if (window.Window) {
        cleanupListener(Window.prototype);
      }
    }

    if (window.onerror && window.onerror._costanza) {
      window.onerror = _onError;

      window.removeEventListener('error', onError, true);
    }
  }

  function bind(name, callback) {
    if (!callback) {
      callback = name;
      name = currentSection;
    }

    if (callback._costanza) {
      return callback;
    }

    var ret = function() {
      var priorSite = currentSection;

      try {
        currentSection = name;

        callback.apply(this, arguments);
      } catch (err) {
        reportCallback({
          type: 'javascript',
          section: currentSection,
          msg: err.message,
          stack: err.stack
        }, err);
      } finally {
        currentSection = priorSite;
      }
    };
    ret._costanza = true;
    return ret;
  }

  function onError(errorMsg, url, lineNumber, error) {
    var type = 'javascript';

    // Handle ErrorEvent if we receieve it
    if (errorMsg && errorMsg.message) {
      url = errorMsg.filename;
      lineNumber = errorMsg.lineno;
      error = errorMsg.error;
      errorMsg = errorMsg.message;
    }

    if (errorMsg === 'Script error.' && !url) {
      // Ignore untrackable external errors locally
      return;
    }

    // This is a real event handler vs. the onerror special case.
    // Since some browsers decide to treat window.onerror as the error event handler,
    // we have to be prepared for either case
    if (errorMsg && errorMsg.srcElement) {
      // Don't submit duplciate events (and if we weren't already tracking
      // it, it probably wasn't that important)
      if (errorMsg.defaultPrevented || errorMsg._costanzaHandled) {
        return;
      }

      errorMsg._costanzaHandled = true;
      url = url || errorMsg.srcElement.src || errorMsg.srcElement.href;
      type = errorMsg.srcElement.tagName.toLowerCase();
      errorMsg = 'load-failed';
    }

    // Error argument added to the spec. Adding support so we can catch this as it rolls out
    // http://html5.org/tools/web-apps-tracker?from=8085&to=8086
    reportCallback({
      section: currentSection,
      url: url,
      line: lineNumber,
      type: type,

      // Cast error message to string as it sometimes can come through with objects, particularly DOM objects
      // containing circular references.
      msg: errorMsg+'',
      stack: error && error.stack
    }, error);
  }

  function onErrorRoot(errorMsg, url, lineNumber, error) {
    _onError && _onError(errorMsg, url, lineNumber, error);
    onError(errorMsg, url, lineNumber, error);
  }
  onErrorRoot._costanza = true;

  return {
    init: init,
    cleanup: cleanup,
    current: function() {
      return currentSection;
    },
    emit: function(info, error) {
      reportCallback(info, error);
    },
    bind: bind,
    run: function(name, callback) {
      bind(name, callback)();
    },
    onError: onError
  };
})();

@@ -1718,35 +1718,105 @@
 dow.HTMLDocument
-) %7B
+ %7C%7C window.Document) %7B%0A        // IE10: Document, others are HTMLDocument
 %0A        wrapLis
@@ -1813,32 +1813,40 @@
    wrapListener(
+(window.
 HTMLDocument.pro
@@ -1833,32 +1833,52 @@
 dow.HTMLDocument
+ %7C%7C window.Document)
 .prototype);%0A   

Fix IE10 document listener binds