
function initSideNav() {
  var leftNavToggle = document.getElementById('sidenav-left-toggle');
  var leftDrawer = document.querySelector('.sidebar-offcanvas-left');
  var overlay = document.getElementById('overlay-under-drawer');

  function toggleBoth() {
    if (leftDrawer) {
      leftDrawer.classList.toggle('active');
    }

    if (overlay) {
      overlay.classList.toggle('active');
    }
  }

  if (overlay) {
    overlay.addEventListener('click', function(e) {
      toggleBoth();
    });
  }

  if (leftNavToggle) {
    leftNavToggle.addEventListener('click', function(e) {
      toggleBoth();
    });
  }
}

function initSearch(name) {
  var searchIndex;  // the JSON data

  var weights = {
    'library' : 2,
    'class' : 2,
    'typedef' : 3,
    'method' : 4,
    'accessor' : 4,
    'operator' : 4,
    'property' : 4,
    'constructor' : 4
  };

  function findMatches(q) {
    var allMatches = []; // list of matches

    function score(element, num) {
      num -= element.overriddenDepth * 10;
      var weightFactor = weights[element.type] || 4;
      return {e: element, score: (num / weightFactor) >> 0};
    }

    $.each(searchIndex, function(i, element) {
      // TODO: prefer matches in the current library
      // TODO: help prefer a named constructor

      var lowerName = element.name.toLowerCase();
      var lowerQualifiedName = element.qualifiedName.toLowerCase();
      var lowerQ = q.toLowerCase();
      var previousMatchCount = allMatches.length;

      if (element.name === q || element.qualifiedName === q) {
        // exact match, maximum score
        allMatches.push(score(element, 2000));
      } else if (element.name === 'dart:'+q) {
        // exact match for a dart: library
        allMatches.push(score(element, 2000));
      } else if (lowerName === 'dart:'+lowerQ) {
        // case-insensitive match for a dart: library
        allMatches.push(score(element, 1800));
      } else if (lowerName === lowerQ || lowerQualifiedName === lowerQ) {
        // case-insensitive exact match
        allMatches.push(score(element, 1700));
      }

      // only care about exact matches if length is 2 or less
      // and only continue if we didn't find a match above
      if (q.length <= 2 || previousMatchCount < allMatches.length) return;

      if (element.name.indexOf(q) === 0 || element.qualifiedName.indexOf(q) === 0) {
        // starts with
        allMatches.push(score(element, 750));
      } else if (lowerName.indexOf(lowerQ) === 0 || lowerQualifiedName.indexOf(lowerQ) === 0) {
        // case-insensitive starts with
        allMatches.push(score(element, 650));
      } else if (element.name.indexOf(q) >= 0 || element.qualifiedName.indexOf(q) >= 0) {
        // contains
        allMatches.push(score(element, 500));
      } else if (lowerName.indexOf(lowerQ) >= 0 || lowerQualifiedName.indexOf(lowerQ) >= 0) {
        // case insensitive contains
        allMatches.push(score(element, 400));
      }
    });

    allMatches.sort(function(a, b) {
      var x = b.score - a.score;
      if (x === 0) {
        // tie-breaker: shorter name wins
        return a.e.name.length - b.e.name.length;
      } else {
        return x;
      }
    });

    var sortedMatches = [];
    for (var i = 0; i < allMatches.length; i++) {
      sortedMatches.push(allMatches[i].e);
    }

    return sortedMatches;
  };

  function initTypeahead() {
    var search = new URI().query(true)["search"];
    if (search) {
      var matches = findMatches(search);
      if (matches.length != 0) {
        window.location = matches[0].href;
        return;
      }
    }

    $('#' + name).prop('disabled', false);
    $('#' + name).prop('placeholder', 'Search');
    $(document).keypress(function(event) {
      if (event.which == 47 /* / */) {
        event.preventDefault();
        $('#' + name).focus();
      }
    });

    $('#' + name + '.typeahead').typeahead({
      hint: true,
      highlight: true,
      minLength: 1
    },
    {
      name: 'elements',
      limit: 10,
      source: function(q, cb) { cb(findMatches(q)); },
      display: function(element) { return element.name; },
      templates: {
        suggestion: function(match) {
          return [
            '<div data-href="' + match.href + '">',
              match.name,
              ' ',
              match.type.toLowerCase(),
              (match.enclosedBy ? [
              '<div class="search-from-lib">from ',
              match.enclosedBy.name,
              '</div>'].join('') : ''),
            '</div>'
          ].join('');
        }
      }
    });

    var typeaheadElement = $('#' + name + '.typeahead');
    var typeaheadElementParent = typeaheadElement.parent();
    var selectedSuggestion;

    typeaheadElement.on("keydown", function (e) {
      if (e.keyCode === 13) { // Enter
        if (selectedSuggestion == null) {
          var suggestion = typeaheadElementParent.find(".tt-suggestion.tt-selectable:eq(0)");
          if (suggestion.length > 0) {
            var href = suggestion.data("href");
            if (href != null) {
              window.location = href;
            }
          }
        }
      }
    });

    typeaheadElement.bind('typeahead:select', function(ev, suggestion) {
        selectedSuggestion = suggestion;
        window.location = suggestion.href;
    });
  }

  var jsonReq = new XMLHttpRequest();
  jsonReq.open('GET', 'index.json', true);
  jsonReq.addEventListener('load', function() {
    searchIndex = JSON.parse(jsonReq.responseText);
    initTypeahead();
  });
  jsonReq.addEventListener('error', function() {
    $('#' + name).prop('placeholder', 'Error loading search index');
  });
  jsonReq.send();
}

document.addEventListener("DOMContentLoaded", function() {
  hljs.initHighlightingOnLoad();
  initSideNav();
  initSearch("search-box");
  initSearch("search-body");
});
