(function() {

var $clicked = $(null);
var searchTimeout = null;
var searchCache = [];
var caseSensitiveMatch = false;
var ignoreKeyCodeMin = 8;
var ignoreKeyCodeMax = 46;
var commandKey = 91;

RegExp.escape = function(text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}

function escapeShortcut() {
  $(document).keydown(function(evt) {
    if (evt.which == 27) {
      window.parent.postMessage('navEscape', '*');
    }
  });
}

function navResizer() {
  $(window).mousemove(function(e) {
    window.parent.postMessage({
      action: 'mousemove', event: {pageX: e.pageX, which: e.which}
    }, '*');
  }).mouseup(function(e) {
    window.parent.postMessage({action: 'mouseup'}, '*');
  });
  window.parent.postMessage("navReady", "*");
}

function clearSearchTimeout() {
  clearTimeout(searchTimeout);
  searchTimeout = null;
}

function enableLinks() {
  // load the target page in the parent window
  $('#full_list li').on('click', function(evt) {
    $('#full_list li').removeClass('clicked');
    $clicked = $(this);
    $clicked.addClass('clicked');
    evt.stopPropagation();

    if (evt.target.tagName === 'A') return true;

    var elem = $clicked.find('> .item .object_link a')[0];
    var e = evt.originalEvent;
    var newEvent = new MouseEvent(evt.originalEvent.type);
    newEvent.initMouseEvent(e.type, e.canBubble, e.cancelable, e.view, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
    elem.dispatchEvent(newEvent);
    evt.preventDefault();
    return false;
  });
}

function enableToggles() {
  // show/hide nested classes on toggle click
  $('#full_list a.toggle').on('click', function(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    $(this).parent().parent().toggleClass('collapsed');
    $(this).attr('aria-expanded', function (i, attr) {
        return attr == 'true' ? 'false' : 'true'
    });
    highlight();
  });

  // navigation of nested classes using keyboard
  $('#full_list a.toggle').on('keypress',function(evt) {
    // enter key is pressed
    if (evt.which == 13) {
      evt.stopPropagation();
      evt.preventDefault();
      $(this).parent().parent().toggleClass('collapsed');
      $(this).attr('aria-expanded', function (i, attr) {
          return attr == 'true' ? 'false' : 'true'
      });
      highlight();
    }
  });
}

function populateSearchCache() {
  $('#full_list li .item').each(function() {
    var $node = $(this);
    var $link = $node.find('.object_link a');
    if ($link.length > 0) {
      searchCache.push({
        node: $node,
        link: $link,
        name: $link.text(),
        fullName: $link.attr('title').split(' ')[0]
      });
    }
  });
}

function enableSearch() {
  $('#search input').keyup(function(event) {
    if (ignoredKeyPress(event)) return;
    if (this.value === "") {
      clearSearch();
    } else {
      performSearch(this.value);
    }
  });

  $('#full_list').after("<div id='noresults' role='status' style='display: none'></div>");
}

function ignoredKeyPress(event) {
  if (
    (event.keyCode > ignoreKeyCodeMin && event.keyCode < ignoreKeyCodeMax) ||
    (event.keyCode == commandKey)
  ) {
    return true;
  } else {
    return false;
  }
}

function clearSearch() {
  clearSearchTimeout();
  $('#full_list .found').removeClass('found').each(function() {
    var $link = $(this).find('.object_link a');
    $link.text($link.text());
  });
  $('#full_list, #content').removeClass('insearch');
  $clicked.parents().removeClass('collapsed');
  highlight();
}

function performSearch(searchString) {
  clearSearchTimeout();
  $('#full_list, #content').addClass('insearch');
  $('#noresults').text('').hide();
  partialSearch(searchString, 0);
}

function partialSearch(searchString, offset) {
  var lastRowClass = '';
  var i = null;
  for (i = offset; i < Math.min(offset + 50, searchCache.length); i++) {
    var item = searchCache[i];
    var searchName = (searchString.indexOf('::') != -1 ? item.fullName : item.name);
    var matchString = buildMatchString(searchString);
    var matchRegexp = new RegExp(matchString, caseSensitiveMatch ? "" : "i");
    if (searchName.match(matchRegexp) == null) {
      item.node.removeClass('found');
      item.link.text(item.link.text());
    }
    else {
      item.node.addClass('found');
      item.node.removeClass(lastRowClass).addClass(lastRowClass == 'r1' ? 'r2' : 'r1');
      lastRowClass = item.node.hasClass('r1') ? 'r1' : 'r2';
      item.link.html(item.name.replace(matchRegexp, "<strong>$&</strong>"));
    }
  }
  if(i == searchCache.length) {
    searchDone();
  } else {
    searchTimeout = setTimeout(function() {
      partialSearch(searchString, i);
    }, 0);
  }
}

function searchDone() {
  searchTimeout = null;
  highlight();
  var found = $('#full_list li:visible').size();
  if (found === 0) {
    $('#noresults').text('No results were found.');
  } else {
    // This is read out to screen readers
    $('#noresults').text('There are ' + found + ' results.');
  }
  $('#noresults').show();
  $('#content').removeClass('insearch');
}

function buildMatchString(searchString, event) {
  caseSensitiveMatch = searchString.match(/[A-Z]/) != null;
  var regexSearchString = RegExp.escape(searchString);
  if (caseSensitiveMatch) {
    regexSearchString += "|" +
      $.map(searchString.split(''), function(e) { return RegExp.escape(e); }).
      join('.+?');
  }
  return regexSearchString;
}

function highlight() {
  $('#full_list li:visible').each(function(n) {
    $(this).removeClass('even odd').addClass(n % 2 == 0 ? 'odd' : 'even');
  });
}

/**
 * Expands the tree to the target element and its immediate
 * children.
 */
function expandTo(path) {
  var $target = $(document.getElementById('object_' + path));
  $target.addClass('clicked');
  $target.removeClass('collapsed');
  $target.parentsUntil('#full_list', 'li').removeClass('collapsed');

  $target.find('a.toggle').attr('aria-expanded', 'true')
  $target.parentsUntil('#full_list', 'li').each(function(i, el) {
    $(el).find('> div > a.toggle').attr('aria-expanded', 'true');
  });

  if($target[0]) {
    window.scrollTo(window.scrollX, $target.offset().top - 250);
    highlight();
  }
}

function windowEvents(event) {
  var msg = event.data;
  if (msg.action === "expand") {
    expandTo(msg.path);
  }
  return false;
}

window.addEventListener("message", windowEvents, false);

$(document).ready(function() {
  escapeShortcut();
  navResizer();
  enableLinks();
  enableToggles();
  populateSearchCache();
  enableSearch();
});

})();
