/**
 * Javelin core; installs Javelin and Stratcom event delegation.
 *
 * @provides javelin-magical-init
 *
 * @javelin-installs JX.__rawEventQueue
 * @javelin-installs JX.__simulate
 * @javelin-installs JX.__allowedEvents
 * @javelin-installs JX.enableDispatch
 * @javelin-installs JX.onload
 * @javelin-installs JX.flushHoldingQueue
 *
 * @javelin
 */
(function() {

  if (window.JX) {
    return;
  }

  window.JX = {};

  // The holding queues hold calls to functions (JX.install() and JX.behavior())
  // before they load, so if you're async-loading them later in the document
  // the page will execute correctly regardless of the order resources arrive
  // in.

  var holding_queues = {};

  function makeHoldingQueue(name) {
    if (JX[name]) {
      return;
    }
    holding_queues[name] = [];
    JX[name] = function() { holding_queues[name].push(arguments); };
  }

  JX.flushHoldingQueue = function(name, fn) {
    for (var ii = 0; ii < holding_queues[name].length; ii++) {
      fn.apply(null, holding_queues[name][ii]);
    }
    holding_queues[name] = {};
  };

  makeHoldingQueue('install');
  makeHoldingQueue('behavior');
  makeHoldingQueue('install-init');

  window['__DEV__'] = window['__DEV__'] || 0;

  var loaded = false;
  var onload = [];
  var master_event_queue = [];
  var root = document.documentElement;
  var has_add_event_listener = !!root.addEventListener;

  JX.__rawEventQueue = function(what) {
    master_event_queue.push(what);

    // Evade static analysis - JX.Stratcom
    var Stratcom = JX['Stratcom'];
    if (Stratcom && Stratcom.ready) {
      //  Empty the queue now so that exceptions don't cause us to repeatedly
      //  try to handle events.
      var local_queue = master_event_queue;
      master_event_queue = [];
      for (var ii = 0; ii < local_queue.length; ++ii) {
        var evt = local_queue[ii];

        // Sometimes IE gives us events which throw when ".type" is accessed;
        // just ignore them since we can't meaningfully dispatch them. TODO:
        // figure out where these are coming from.
        try { var test = evt.type; } catch (x) { continue; }

        if (!loaded && evt.type == 'domready') {
          // NOTE: Firefox interprets "document.body.id = null" as the string
          // literal "null".
          document.body && (document.body.id = '');
          loaded = true;
          for (var jj = 0; jj < onload.length; jj++) {
            onload[jj]();
          }
        }

        Stratcom.dispatch(evt);
      }
    } else {
      var target = what.srcElement || what.target;
      if (target &&
          (what.type in {click: 1, submit: 1}) &&
          target.getAttribute &&
          target.getAttribute('data-mustcapture') === '1') {
        what.returnValue = false;
        what.preventDefault && what.preventDefault();
        document.body.id = 'event_capture';

        // For versions of IE that use attachEvent, the event object is somehow
        // stored globally by reference, and all the references we push to the
        // master_event_queue will always refer to the most recent event. We
        // work around this by popping the useless global event off the queue,
        // and pushing a clone of the event that was just fired using the IE's
        // proprietary createEventObject function.
        // see: http://msdn.microsoft.com/en-us/library/ms536390(v=vs.85).aspx
        if (!add_event_listener && document.createEventObject) {
          master_event_queue.pop();
          master_event_queue.push(document.createEventObject(what));
        }

        return false;
      }
    }
  };

  JX.enableDispatch = function(target, type) {
    if (__DEV__) {
      JX.__allowedEvents[type] = true;
    }

    if (target.addEventListener) {
      target.addEventListener(type, JX.__rawEventQueue, true);
    } else if (target.attachEvent) {
      target.attachEvent('on' + type, JX.__rawEventQueue);
    }
  };

  var document_events = [
    'click',
    'dblclick',
    'change',
    'submit',
    'keypress',
    'mousedown',
    'mouseover',
    'mouseout',
    'mouseup',
    'keyup',
    'keydown',
    'input',
    'drop',
    'dragenter',
    'dragleave',
    'dragover',
    'paste',
    'touchstart',
    'touchmove',
    'touchend',
    'touchcancel'
  ];

  //  Simulate focus and blur in old versions of IE using focusin and focusout
  //  TODO: Document the gigantic IE mess here with focus/blur.
  //  TODO: beforeactivate/beforedeactivate?
  //  http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
  if (!has_add_event_listener) {
    document_events.push('focusin', 'focusout');
  }

  //  Opera is multilol: it propagates focus / blur oddly
  if (window.opera) {
    document_events.push('focus', 'blur');
  }

  if (__DEV__) {
    JX.__allowedEvents = {};
    if ('onpagehide' in window) {
      JX.__allowedEvents.unload = true;
    }
  }

  var ii;
  for (ii = 0; ii < document_events.length; ++ii) {
    JX.enableDispatch(root, document_events[ii]);
  }

  //  In particular, we're interested in capturing window focus/blur here so
  //  long polls can abort when the window is not focused.
  var window_events = [
    ('onpagehide' in window) ? 'pagehide' : 'unload',
    'resize',
    'scroll',
    'focus',
    'blur',
    'popstate',
    'hashchange'
  ];


  for (ii = 0; ii < window_events.length; ++ii) {
    JX.enableDispatch(window, window_events[ii]);
  }

  JX.__simulate = function(node, event) {
    if (!has_add_event_listener) {
      var e = {target: node, type: event};
      JX.__rawEventQueue(e);
      if (e.returnValue === false) {
        return false;
      }
    }
  };

  if (has_add_event_listener) {
    document.addEventListener('DOMContentLoaded', function() {
      JX.__rawEventQueue({type: 'domready'});
    }, true);
  } else {
    var ready =
      "if (this.readyState == 'complete') {" +
        "JX.__rawEventQueue({type: 'domready'});" +
      "}";

    // NOTE: Don't write a 'src' attribute, because "javascript:void(0)" causes
    // a mixed content warning in IE8 if the page is served over SSL.
    document.write(
      '<script' +
      ' defer="defer"' +
      ' onreadystatechange="' + ready + '"' +
      '><\/sc' + 'ript' + '>');
  }

  JX.onload = function(func) {
    if (loaded) {
      func();
    } else {
      onload.push(func);
    }
  };

})();
