Updated default.css and docs.js for DoD transition fallback.

Change-Id: I0a978667b2ead38ba8dad8c90b173e9e91abd6f2
diff --git a/tools/droiddoc/templates-sdk-dev/assets/js/android_3p-bundle.js b/tools/droiddoc/templates-sdk-dev/assets/js/android_3p-bundle.js
index 70d6c2f..a67b5b0 100644
--- a/tools/droiddoc/templates-sdk-dev/assets/js/android_3p-bundle.js
+++ b/tools/droiddoc/templates-sdk-dev/assets/js/android_3p-bundle.js
@@ -2763,10 +2763,4 @@
 * https://github.com/jquery/jquery-ui
 * Includes: jquery.effects.transfer.js
 * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
-(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);;
-/*! (c) 2012 Airbnb, Inc.
-*
-* polyglot.js 0.4.3 may be freely distributed under the terms of the BSD
-* license. For all licensing information, details, and documention:
-* http://airbnb.github.com/polyglot.js */
-(function(e,t){typeof define=="function"&&define.amd?define([],function(){return t(e)}):typeof exports=="object"?module.exports=t(e):e.Polyglot=t(e)})(this,function(e){"use strict";function t(e){e=e||{},this.phrases={},this.extend(e.phrases||{}),this.currentLocale=e.locale||"en",this.allowMissing=!!e.allowMissing,this.warn=e.warn||c}function s(e){var t,n,r,i={};for(t in e)if(e.hasOwnProperty(t)){n=e[t];for(r in n)i[n[r]]=t}return i}function o(e){var t=/^\s+|\s+$/g;return e.replace(t,"")}function u(e,t,r){var i,s,u;return r!=null&&e?(s=e.split(n),u=s[f(t,r)]||s[0],i=o(u)):i=e,i}function a(e){var t=s(i);return t[e]||t.en}function f(e,t){return r[a(e)](t)}function l(e,t){for(var n in t)n!=="_"&&t.hasOwnProperty(n)&&(e=e.replace(new RegExp("%\\{"+n+"\\}","g"),t[n]));return e}function c(t){e.console&&e.console.warn&&e.console.warn("WARNING: "+t)}function h(e){var t={};for(var n in e)t[n]=e[n];return t}t.VERSION="0.4.3",t.prototype.locale=function(e){return e&&(this.currentLocale=e),this.currentLocale},t.prototype.extend=function(e,t){var n;for(var r in e)e.hasOwnProperty(r)&&(n=e[r],t&&(r=t+"."+r),typeof n=="object"?this.extend(n,r):this.phrases[r]=n)},t.prototype.clear=function(){this.phrases={}},t.prototype.replace=function(e){this.clear(),this.extend(e)},t.prototype.t=function(e,t){var n,r;return t=t==null?{}:t,typeof t=="number"&&(t={smart_count:t}),typeof this.phrases[e]=="string"?n=this.phrases[e]:typeof t._=="string"?n=t._:this.allowMissing?n=e:(this.warn('Missing translation for key: "'+e+'"'),r=e),typeof n=="string"&&(t=h(t),r=u(n,this.currentLocale,t.smart_count),r=l(r,t)),r},t.prototype.has=function(e){return e in this.phrases};var n="||||",r={chinese:function(e){return 0},german:function(e){return e!==1?1:0},french:function(e){return e>1?1:0},russian:function(e){return e%10===1&&e%100!==11?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2},czech:function(e){return e===1?0:e>=2&&e<=4?1:2},polish:function(e){return e===1?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2},icelandic:function(e){return e%10!==1||e%100===11?1:0}},i={chinese:["fa","id","ja","ko","lo","ms","th","tr","zh"],german:["da","de","en","es","fi","el","he","hu","it","nl","no","pt","sv"],french:["fr","tl","pt-br"],russian:["hr","ru"],czech:["cs"],polish:["pl"],icelandic:["is"]};return t});
+(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);;
\ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk-dev/assets/js/docs.js b/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
index 9afed71..ee3ebee 100644
--- a/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
+++ b/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
@@ -1,9 +1,16 @@
+var classesNav;
+var devdocNav;
+var sidenav;
 var cookie_namespace = 'android_developer';
+var NAV_PREF_TREE = "tree";
+var NAV_PREF_PANELS = "panels";
+var nav_pref;
 var isMobile = false; // true if mobile, so we can adjust some layout
 var mPagePath; // initialized in ready() function
 
 var basePath = getBaseUri(location.pathname);
-var SITE_ROOT = toRoot + basePath.substring(1, basePath.indexOf("/", 1));
+var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
+var GOOGLE_DATA; // combined data for google service apis, used for search suggest
 
 // Ensure that all ajax getScript() requests allow caching
 $.ajaxSetup({
@@ -14,12 +21,102 @@
 
 $(document).ready(function() {
 
+  // show lang dialog if the URL includes /intl/
+  //if (location.pathname.substring(0,6) == "/intl/") {
+  //  var lang = location.pathname.split('/')[2];
+   // if (lang != getLangPref()) {
+   //   $("#langMessage a.yes").attr("onclick","changeLangPref('" + lang
+   //       + "', true); $('#langMessage').hide(); return false;");
+  //    $("#langMessage .lang." + lang).show();
+   //   $("#langMessage").show();
+   // }
+  //}
+
+  // load json file for JD doc search suggestions
+  $.getScript(toRoot + 'jd_lists_unified.js');
+  // load json file for Android API search suggestions
+  $.getScript(toRoot + 'reference/lists.js');
+  // load json files for Google services API suggestions
+  $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
+      // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
+      if(jqxhr.status === 200) {
+          $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
+              if(jqxhr.status === 200) {
+                  // combine GCM and GMS data
+                  GOOGLE_DATA = GMS_DATA;
+                  var start = GOOGLE_DATA.length;
+                  for (var i=0; i<GCM_DATA.length; i++) {
+                      GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
+                              link:GCM_DATA[i].link, type:GCM_DATA[i].type});
+                  }
+              }
+          });
+      }
+  });
+
+  // setup keyboard listener for search shortcut
+  $('body').keyup(function(event) {
+    if (event.which == 191 && $(event.target).is(':not(:input)')) {
+      $('#search_autocomplete').focus();
+    }
+  });
+
+  // init the fullscreen toggle click event
+  $('#nav-swap .fullscreen').click(function(){
+    if ($(this).hasClass('disabled')) {
+      toggleFullscreen(true);
+    } else {
+      toggleFullscreen(false);
+    }
+  });
+
+  // initialize the divs with custom scrollbars
+  if (window.innerWidth >= 720) {
+    $('.scroll-pane').jScrollPane({verticalGutter: 0});
+  }
+
+  // set up the search close button
+  $('#search-close').on('click touchend', function() {
+    $searchInput = $('#search_autocomplete');
+    $searchInput.attr('value', '');
+    $(this).addClass("hide");
+    $("#search-container").removeClass('active');
+    $("#search_autocomplete").blur();
+    search_focus_changed($searchInput.get(), false);
+    hideResults();
+  });
+
+
+  //Set up search
+  $("#search_autocomplete").focus(function() {
+    $("#search-container").addClass('active');
+  })
+  $("#search-container").on('mouseover touchend', function(e) {
+    if ($(e.target).is('#search-close')) { return; }
+    $("#search-container").addClass('active');
+    $("#search_autocomplete").focus();
+  })
+  $("#search-container").mouseout(function() {
+    if ($("#search_autocomplete").is(":focus")) return;
+    if ($("#search_autocomplete").val() == '') {
+      setTimeout(function(){
+        $("#search-container").removeClass('active');
+        $("#search_autocomplete").blur();
+      },250);
+    }
+  })
+  $("#search_autocomplete").blur(function() {
+    if ($("#search_autocomplete").val() == '') {
+      $("#search-container").removeClass('active');
+    }
+  })
+
+
   // prep nav expandos
-  var pagePath = devsite ?
-      location.href.replace(location.hash, '') : document.location.pathname;
+  var pagePath = document.location.pathname;
   // account for intl docs by removing the intl/*/ path
   if (pagePath.indexOf("/intl/") == 0) {
-    pagePath = pagePath.substr(pagePath.indexOf("/", 6)); // start after intl/ to get last /
+    pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
   }
 
   if (pagePath.indexOf(SITE_ROOT) == 0) {
@@ -50,10 +147,97 @@
     // Otherwise the page path is already an absolute URL
   }
 
+  // Highlight the header tabs...
+  // highlight Design tab
+  var urlSegments = pagePathOriginal.split('/');
+  var navEl = $(".dac-nav-list");
+  var subNavEl = navEl.find(".dac-nav-secondary");
+  var parentNavEl;
+
+  if ($("body").hasClass("design")) {
+    navEl.find("> li.design > a").addClass("selected");
+  // highlight About tabs
+  } else if ($("body").hasClass("about")) {
+    if (urlSegments[1] == "about" || urlSegments[1] == "wear" || urlSegments[1] == "tv" || urlSegments[1] == "auto") {
+      navEl.find("> li.home > a").addClass('has-subnav');
+      subNavEl.find("li." + urlSegments[1] + " > a").addClass("selected");
+    } else {
+      navEl.find("> li.home > a").addClass('selected');
+    }
+
+// highlight NDK tabs
+  } else if ($("body").hasClass("ndk")) {
+    parentNavEl = navEl.find("> li.ndk > a");
+    parentNavEl.addClass('has-subnav');
+    if ($("body").hasClass("guide")) {
+      navEl.find("> li.guides > a").addClass("selected ndk");
+    } else if ($("body").hasClass("reference")) {
+      navEl.find("> li.reference > a").addClass("selected ndk");
+    } else if ($("body").hasClass("samples")) {
+      navEl.find("> li.samples > a").addClass("selected ndk");
+    } else if ($("body").hasClass("downloads")) {
+      navEl.find("> li.downloads > a").addClass("selected ndk");
+    }
+
+  // highlight Develop tab
+  } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
+    parentNavEl = navEl.find("> li.develop > a");
+    parentNavEl.addClass('has-subnav');
+
+    // In Develop docs, also highlight appropriate sub-tab
+    if (urlSegments[1] == "training") {
+      subNavEl.find("li.training > a").addClass("selected");
+    } else if (urlSegments[1] == "guide") {
+      subNavEl.find("li.guide > a").addClass("selected");
+    } else if (urlSegments[1] == "reference") {
+      // If the root is reference, but page is also part of Google Services, select Google
+      if ($("body").hasClass("google")) {
+        subNavEl.find("li.google > a").addClass("selected");
+      } else {
+        subNavEl.find("li.reference > a").addClass("selected");
+      }
+    } else if ((urlSegments[1] == "tools") || (urlSegments[1] == "sdk")) {
+      subNavEl.find("li.tools > a").addClass("selected");
+    } else if ($("body").hasClass("google")) {
+      subNavEl.find("li.google > a").addClass("selected");
+    } else if ($("body").hasClass("samples")) {
+      subNavEl.find("li.samples > a").addClass("selected");
+    } else if ($("body").hasClass("preview")) {
+      subNavEl.find("li.preview > a").addClass("selected");
+    } else {
+      parentNavEl.removeClass('has-subnav').addClass("selected");
+    }
+  // highlight Distribute tab
+  } else if ($("body").hasClass("distribute")) {
+    parentNavEl = navEl.find("> li.distribute > a");
+    parentNavEl.addClass('has-subnav');
+
+    if (urlSegments[2] == "users") {
+      subNavEl.find("li.users > a").addClass("selected");
+    } else if (urlSegments[2] == "engage") {
+      subNavEl.find("li.engage > a").addClass("selected");
+    } else if (urlSegments[2] == "monetize") {
+      subNavEl.find("li.monetize > a").addClass("selected");
+    } else if (urlSegments[2] == "analyze") {
+      subNavEl.find("li.analyze > a").addClass("selected");
+    } else if (urlSegments[2] == "tools") {
+      subNavEl.find("li.essentials > a").addClass("selected");
+    } else if (urlSegments[2] == "stories") {
+      subNavEl.find("li.stories > a").addClass("selected");
+    } else if (urlSegments[2] == "essentials") {
+      subNavEl.find("li.essentials > a").addClass("selected");
+    } else if (urlSegments[2] == "googleplay") {
+      subNavEl.find("li.googleplay > a").addClass("selected");
+    } else {
+      parentNavEl.removeClass('has-subnav').addClass("selected");
+    }
+  }
+
   // set global variable so we can highlight the sidenav a bit later (such as for google reference)
   // and highlight the sidenav
   mPagePath = pagePath;
   highlightSidenav();
+  buildBreadcrumbs();
 
   // set up prev/next links if they exist
   var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
@@ -82,8 +266,8 @@
 
       // except if cross boundaries aren't allowed, and we're at the top of a section already
       // (and there's another parent)
-      if (!crossBoundaries && $parentListItem.hasClass('nav-section') &&
-                           $selListItem.hasClass('nav-section')) {
+      if (!crossBoundaries && $parentListItem.hasClass('nav-section')
+                           && $selListItem.hasClass('nav-section')) {
         $prevLink = [];
       }
     }
@@ -98,7 +282,7 @@
       $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
 
       // if there aren't any children, go to the next section (required for About pages)
-      if ($nextLink.length == 0) {
+      if($nextLink.length == 0) {
         $nextLink = $selListItem.next('li').find('a');
       } else if ($('.topic-start-link').length) {
         // as long as there's a child link and there is a "topic start link" (we're on a landing)
@@ -122,7 +306,7 @@
           $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
           if ($nextLink.length == 0) {
             // if that doesn't work, we're at the end of the list, so disable NEXT link
-            $('.next-page-link').attr('href', '').addClass("disabled")
+            $('.next-page-link').attr('href','').addClass("disabled")
                                 .click(function() { return false; });
             // and completely hide the one in the footer
             $('.content-footer .next-page-link').hide();
@@ -141,27 +325,22 @@
       }
     } else if (isCrossingBoundary && !$('body.design').length) {  // Design always crosses boundaries
       $('.content-footer.next-class').show();
-      $('.next-page-link').attr('href', '')
+      $('.next-page-link').attr('href','')
                           .removeClass("hide").addClass("disabled")
                           .click(function() { return false; });
       // and completely hide the one in the footer
       $('.content-footer .next-page-link').hide();
-      $('.content-footer .prev-page-link').hide();
-
       if ($nextLink.length) {
-        $('.next-class-link').attr('href', $nextLink.attr('href'))
-                             .removeClass("hide");
-
-        $('.content-footer .next-class-link').append($nextLink.html());
-
+        $('.next-class-link').attr('href',$nextLink.attr('href'))
+                             .removeClass("hide")
+                             .append(": " + $nextLink.html());
         $('.next-class-link').find('.new').empty();
       }
     } else {
       $('.next-page-link').attr('href', $nextLink.attr('href'))
                           .removeClass("hide");
-      // for the footer link, also add the previous and next page titles
-      $('.content-footer .prev-page-link').append($prevLink.html());
-      $('.content-footer .next-page-link').append($nextLink.html());
+      // for the footer link, also add the next page title
+      $('.content-footer .next-page-link').append(": " + $nextLink.html());
     }
 
     if (!startClass && $prevLink.length) {
@@ -175,6 +354,8 @@
 
   }
 
+
+
   // Set up the course landing pages for Training with class names and descriptions
   if ($('body.trainingcourse').length) {
     var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
@@ -201,7 +382,7 @@
     var $liLesson;
     $classLinks.each(function(index) {
       $liClass  = $('<li class="clearfix"></li>');
-      $h2Title  = $('<a class="title" href="' + $(this).attr('href') + '"><h2 class="norule">' + $(this).html() + '</h2><span></span></a>');
+      $h2Title  = $('<a class="title" href="'+$(this).attr('href')+'"><h2 class="norule">' + $(this).html()+'</h2><span></span></a>');
       $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
 
       $olLessons  = $('<ol class="lesson-list"></ol>');
@@ -210,7 +391,7 @@
 
       if ($lessons.length) {
         $lessons.each(function(index) {
-          $olLessons.append('<li><a href="' + $(this).attr('href') + '">' + $(this).html() + '</a></li>');
+          $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
         });
       } else {
         $pSummary.addClass('article');
@@ -219,14 +400,35 @@
       $liClass.append($h2Title).append($pSummary).append($olLessons);
       $olClasses.append($liClass);
     });
-    $('#classes').append($olClasses);
+    $('.jd-descr').append($olClasses);
   }
 
   // Set up expand/collapse behavior
   initExpandableNavItems("#nav");
 
+
+  $(".scroll-pane").scroll(function(event) {
+      event.preventDefault();
+      return false;
+  });
+
+  /* Resize nav height when window height changes */
+  $(window).resize(function() {
+    if ($('#side-nav').length == 0) return;
+    setNavBarDimensions(); // do this even if sidenav isn't fixed because it could become fixed
+    // make sidenav behave when resizing the window and side-scolling is a concern
+    updateSideNavDimensions();
+    checkSticky();
+    resizeNav(250);
+  });
+
+  if ($('#devdoc-nav').length) {
+    setNavBarDimensions();
+  }
+
+
   // Set up play-on-hover <video> tags.
-  $('video.play-on-hover').bind('click', function() {
+  $('video.play-on-hover').bind('click', function(){
     $(this).get(0).load(); // in case the video isn't seekable
     $(this).get(0).play();
   });
@@ -274,31 +476,220 @@
   });
 
   //Loads the +1 button
-  //var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
-  //po.src = 'https://apis.google.com/js/plusone.js';
-  //var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
+  var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
+  po.src = 'https://apis.google.com/js/plusone.js';
+  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
+
+  $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
+
+  if ($(".scroll-pane").length > 1) {
+    // Check if there's a user preference for the panel heights
+    var cookieHeight = readCookie("reference_height");
+    if (cookieHeight) {
+      restoreHeight(cookieHeight);
+    }
+  }
+
+  // Resize once loading is finished
+  resizeNav();
+  // Check if there's an anchor that we need to scroll into view.
+  // A delay is needed, because some browsers do not immediately scroll down to the anchor
+  window.setTimeout(offsetScrollForSticky, 100);
+
+  /* init the language selector based on user cookie for lang */
+  loadLangPref();
+  changeNavLang(getLangPref());
+
+  /* setup event handlers to ensure the overflow menu is visible while picking lang */
+  $("#language select")
+      .mousedown(function() {
+        $("div.morehover").addClass("hover"); })
+      .blur(function() {
+        $("div.morehover").removeClass("hover"); });
+
+  /* some global variable setup */
+  resizePackagesNav = $("#resize-packages-nav");
+  classesNav = $("#classes-nav");
+  devdocNav = $("#devdoc-nav");
+
+  var cookiePath = "";
+  if (location.href.indexOf("/reference/") != -1) {
+    cookiePath = "reference_";
+  } else if (location.href.indexOf("/guide/") != -1) {
+    cookiePath = "guide_";
+  } else if (location.href.indexOf("/tools/") != -1) {
+    cookiePath = "tools_";
+  } else if (location.href.indexOf("/training/") != -1) {
+    cookiePath = "training_";
+  } else if (location.href.indexOf("/design/") != -1) {
+    cookiePath = "design_";
+  } else if (location.href.indexOf("/distribute/") != -1) {
+    cookiePath = "distribute_";
+  }
+
+
+  /* setup shadowbox for any videos that want it */
+  var $videoLinks = $("a.video-shadowbox-button, a.notice-developers-video");
+  if ($videoLinks.length) {
+    // if there's at least one, add the shadowbox HTML to the body
+    $('body').prepend(
+'<div id="video-container">'+
+  '<div id="video-frame">'+
+    '<div class="video-close">'+
+      '<span id="icon-video-close" onclick="closeVideo()">&nbsp;</span>'+
+    '</div>'+
+    '<div id="youTubePlayer"></div>'+
+  '</div>'+
+'</div>');
+
+    // loads the IFrame Player API code asynchronously.
+    $.getScript("https://www.youtube.com/iframe_api");
+
+    $videoLinks.each(function() {
+      var videoId = $(this).attr('href').split('?v=')[1];
+      $(this).click(function(event) {
+        event.preventDefault();
+        startYouTubePlayer(videoId);
+      });
+    });
+  }
 });
 // END of the onload event
 
+
+var youTubePlayer;
+function onYouTubeIframeAPIReady() {
+}
+
+/* Returns the height the shadowbox video should be. It's based on the current
+   height of the "video-frame" element, which is 100% height for the window.
+   Then minus the margin so the video isn't actually the full window height. */
+function getVideoHeight() {
+  var frameHeight = $("#video-frame").height();
+  var marginTop = $("#video-frame").css('margin-top').split('px')[0];
+  return frameHeight - (marginTop * 2);
+}
+
+var mPlayerPaused = false;
+
+function startYouTubePlayer(videoId) {
+  $("#video-container").show();
+  $("#video-frame").show();
+  mPlayerPaused = false;
+
+  // compute the size of the player so it's centered in window
+  var maxWidth = 940;  // the width of the web site content
+  var videoAspect = .5625; // based on 1280x720 resolution
+  var maxHeight = maxWidth * videoAspect;
+  var videoHeight = getVideoHeight();
+  var videoWidth = videoHeight / videoAspect;
+  if (videoWidth > maxWidth) {
+    videoWidth = maxWidth;
+    videoHeight = maxHeight;
+  }
+  $("#video-frame").css('width', videoWidth);
+
+  // check if we've already created this player
+  if (youTubePlayer == null) {
+    // check if there's a start time specified
+    var idAndHash = videoId.split("#");
+    var startTime = 0;
+    if (idAndHash.length > 1) {
+      startTime = idAndHash[1].split("t=")[1] != undefined ? idAndHash[1].split("t=")[1] : 0;
+    }
+    // enable localized player
+    var lang = getLangPref();
+    var captionsOn = lang == 'en' ? 0 : 1;
+
+    youTubePlayer = new YT.Player('youTubePlayer', {
+      height: videoHeight,
+      width: videoWidth,
+      videoId: idAndHash[0],
+      playerVars: {start: startTime, hl: lang, cc_load_policy: captionsOn},
+      events: {
+        'onReady': onPlayerReady,
+        'onStateChange': onPlayerStateChange
+      }
+    });
+  } else {
+    // reset the size in case the user adjusted the window since last play
+    youTubePlayer.setSize(videoWidth, videoHeight);
+    // if a video different from the one already playing was requested, cue it up
+    if (videoId != youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0]) {
+      youTubePlayer.cueVideoById(videoId);
+    }
+    youTubePlayer.playVideo();
+  }
+}
+
+function onPlayerReady(event) {
+  event.target.playVideo();
+  mPlayerPaused = false;
+}
+
+function closeVideo() {
+  try {
+    youTubePlayer.pauseVideo();
+  } catch(e) {
+  }
+  $("#video-container").fadeOut(200);
+}
+
+/* Track youtube playback for analytics */
+function onPlayerStateChange(event) {
+    // Video starts, send the video ID
+    if (event.data == YT.PlayerState.PLAYING) {
+      if (mPlayerPaused) {
+        ga('send', 'event', 'Videos', 'Resume',
+            youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0]);
+      } else {
+        // track the start playing event so we know from which page the video was selected
+        ga('send', 'event', 'Videos', 'Start: ' +
+            youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
+            'on: ' + document.location.href);
+      }
+      mPlayerPaused = false;
+    }
+    // Video paused, send video ID and video elapsed time
+    if (event.data == YT.PlayerState.PAUSED) {
+      ga('send', 'event', 'Videos', 'Paused',
+            youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
+            youTubePlayer.getCurrentTime());
+      mPlayerPaused = true;
+    }
+    // Video finished, send video ID and video elapsed time
+    if (event.data == YT.PlayerState.ENDED) {
+      ga('send', 'event', 'Videos', 'Finished',
+            youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
+            youTubePlayer.getCurrentTime());
+      mPlayerPaused = true;
+    }
+}
+
+
+
 function initExpandableNavItems(rootTag) {
   $(rootTag + ' li.nav-section .nav-section-header').click(function() {
     var section = $(this).closest('li.nav-section');
     if (section.hasClass('expanded')) {
-      /* hide me and descendants */
+    /* hide me and descendants */
       section.find('ul').slideUp(250, function() {
         // remove 'expanded' class from my section and any children
         section.closest('li').removeClass('expanded');
         $('li.nav-section', section).removeClass('expanded');
+        resizeNav();
       });
     } else {
-      /* show me */
+    /* show me */
       // first hide all other siblings
       var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
       $others.removeClass('expanded').children('ul').slideUp(250);
 
       // now expand me
       section.closest('li').addClass('expanded');
-      section.children('ul').slideDown(250);
+      section.children('ul').slideDown(250, function() {
+        resizeNav();
+      });
     }
   });
 
@@ -311,6 +702,35 @@
   });
 }
 
+
+/** Create the list of breadcrumb links in the sticky header */
+function buildBreadcrumbs() {
+  var $breadcrumbUl =  $(".dac-header-crumbs");
+  var primaryNavLink = ".dac-nav-list > .dac-nav-item > .dac-nav-link";
+
+  // Add the secondary horizontal nav item, if provided
+  var $selectedSecondNav = $(".dac-nav-secondary .dac-nav-link.selected").clone()
+    .attr('class', 'dac-header-crumbs-link');
+
+  if ($selectedSecondNav.length) {
+    $breadcrumbUl.prepend($('<li class="dac-header-crumbs-item">').append($selectedSecondNav));
+  }
+
+  // Add the primary horizontal nav
+  var $selectedFirstNav = $(primaryNavLink + ".selected, " + primaryNavLink + ".has-subnav").clone()
+    .attr('class', 'dac-header-crumbs-link');
+
+  // If there's no header nav item, use the logo link and title from alt text
+  if ($selectedFirstNav.length < 1) {
+    $selectedFirstNav = $('<a class="dac-header-crumbs-link">')
+        .attr('href', $("div#header .logo a").attr('href'))
+        .text($("div#header .logo img").attr('alt'));
+  }
+  $breadcrumbUl.prepend($('<li class="dac-header-crumbs-item">').append($selectedFirstNav));
+}
+
+
+
 /** Highlight the current page in sidenav, expanding children as appropriate */
 function highlightSidenav() {
   // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
@@ -326,8 +746,6 @@
   }
 
   var $selListItem;
-  var breadcrumb = [];
-
   if ($selNavLink.length) {
     // Find this page's <li> in sidenav and set selected
     $selListItem = $selNavLink.closest('li');
@@ -337,20 +755,8 @@
     $selNavLink.parents('li.nav-section').each(function() {
       $(this).addClass('expanded');
       $(this).children('ul').show();
-
-      var link = $(this).find('a').first();
-
-      if (!$(this).is($selListItem)) {
-        breadcrumb.unshift(link)
-      }
     });
-
-    $('#nav').scrollIntoView($selNavLink);
   }
-
-  breadcrumb.forEach(function(link) {
-    link.dacCrumbs();
-  });
 }
 
 function unHighlightSidenav() {
@@ -358,6 +764,59 @@
   $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
 }
 
+function toggleFullscreen(enable) {
+  var delay = 20;
+  var enabled = true;
+  var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
+  if (enable) {
+    // Currently NOT USING fullscreen; enable fullscreen
+    stylesheet.removeAttr('disabled');
+    $('#nav-swap .fullscreen').removeClass('disabled');
+    $('#devdoc-nav').css({left:''});
+    setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
+    enabled = true;
+  } else {
+    // Currently USING fullscreen; disable fullscreen
+    stylesheet.attr('disabled', 'disabled');
+    $('#nav-swap .fullscreen').addClass('disabled');
+    setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
+    enabled = false;
+  }
+  writeCookie("fullscreen", enabled, null);
+  setNavBarDimensions();
+  resizeNav(delay);
+  updateSideNavDimensions();
+  setTimeout(initSidenavHeightResize,delay);
+}
+
+// TODO: Refactor into a closure.
+var navBarLeftPos;
+var navBarWidth;
+function setNavBarDimensions() {
+  navBarLeftPos = $('#body-content').offset().left;
+  navBarWidth = $('#side-nav').width();
+}
+
+
+function updateSideNavDimensions() {
+  var newLeft = $(window).scrollLeft() - navBarLeftPos;
+  $('#devdoc-nav').css({left: -newLeft, width: navBarWidth});
+  $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('padding-left')))});
+}
+
+// TODO: use $(document).ready instead
+function addLoadEvent(newfun) {
+  var current = window.onload;
+  if (typeof window.onload != 'function') {
+    window.onload = newfun;
+  } else {
+    window.onload = function() {
+      current();
+      newfun();
+    }
+  }
+}
+
 var agent = navigator['userAgent'].toLowerCase();
 // If a mobile phone, set flag and do mobile setup
 if ((agent.indexOf("mobile") != -1) ||      // android, iphone, ipod
@@ -367,23 +826,195 @@
   isMobile = true;
 }
 
+
 $(document).ready(function() {
   $("pre:not(.no-pretty-print)").addClass("prettyprint");
   prettyPrint();
 });
 
+
+
+
+/* ######### RESIZE THE SIDENAV ########## */
+
+function resizeNav(delay) {
+  var $nav = $("#devdoc-nav");
+  var $window = $(window);
+  var navHeight;
+
+  // Get the height of entire window and the total header height.
+  // Then figure out based on scroll position whether the header is visible
+  var windowHeight = $window.height();
+  var scrollTop = $window.scrollTop();
+  var headerHeight = $('#header-wrapper').outerHeight();
+  var headerVisible = scrollTop < stickyTop;
+
+  // get the height of space between nav and top of window.
+  // Could be either margin or top position, depending on whether the nav is fixed.
+  var topMargin = (parseInt($nav.css('top')) || 20) + 1;
+  // add 1 for the #side-nav bottom margin
+
+  // Depending on whether the header is visible, set the side nav's height.
+  if (headerVisible) {
+    // The sidenav height grows as the header goes off screen
+    navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
+  } else {
+    // Once header is off screen, the nav height is almost full window height
+    navHeight = windowHeight - topMargin;
+  }
+
+
+
+  $scrollPanes = $(".scroll-pane");
+  if ($window.width() < 720) {
+    $nav.css('height', '');
+  } else if ($scrollPanes.length > 1) {
+    // subtract the height of the api level widget and nav swapper from the available nav height
+    navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
+
+    $("#swapper").css({height:navHeight + "px"});
+    if ($("#nav-tree").is(":visible")) {
+      $("#nav-tree").css({height:navHeight});
+    }
+
+    var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
+    //subtract 10px to account for drag bar
+
+    // if the window becomes small enough to make the class panel height 0,
+    // then the package panel should begin to shrink
+    if (parseInt(classesHeight) <= 0) {
+      $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
+      $("#packages-nav").css({height:navHeight - 10});
+    }
+
+    $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
+    $("#classes-nav .jspContainer").css({height:classesHeight});
+
+
+  } else {
+    $nav.height(navHeight);
+  }
+
+  if (delay) {
+    updateFromResize = true;
+    delayedReInitScrollbars(delay);
+  } else {
+    reInitScrollbars();
+  }
+
+}
+
+var updateScrollbars = false;
+var updateFromResize = false;
+
+/* Re-initialize the scrollbars to account for changed nav size.
+ * This method postpones the actual update by a 1/4 second in order to optimize the
+ * scroll performance while the header is still visible, because re-initializing the
+ * scroll panes is an intensive process.
+ */
+function delayedReInitScrollbars(delay) {
+  // If we're scheduled for an update, but have received another resize request
+  // before the scheduled resize has occured, just ignore the new request
+  // (and wait for the scheduled one).
+  if (updateScrollbars && updateFromResize) {
+    updateFromResize = false;
+    return;
+  }
+
+  // We're scheduled for an update and the update request came from this method's setTimeout
+  if (updateScrollbars && !updateFromResize) {
+    reInitScrollbars();
+    updateScrollbars = false;
+  } else {
+    updateScrollbars = true;
+    updateFromResize = false;
+    setTimeout('delayedReInitScrollbars()',delay);
+  }
+}
+
+/* Re-initialize the scrollbars to account for changed nav size. */
+function reInitScrollbars() {
+  var pane = $(".scroll-pane").each(function(){
+    var api = $(this).data('jsp');
+    if (!api) {return;}
+    api.reinitialise( {verticalGutter:0} );
+  });
+  $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
+}
+
+
+/* Resize the height of the nav panels in the reference,
+ * and save the new size to a cookie */
+function saveNavPanels() {
+  var basePath = getBaseUri(location.pathname);
+  var section = basePath.substring(1,basePath.indexOf("/",1));
+  writeCookie("height", resizePackagesNav.css("height"), section);
+}
+
+
+
+function restoreHeight(packageHeight) {
+    $("#resize-packages-nav").height(packageHeight);
+    $("#packages-nav").height(packageHeight);
+  //  var classesHeight = navHeight - packageHeight;
+ //   $("#classes-nav").css({height:classesHeight});
+  //  $("#classes-nav .jspContainer").css({height:classesHeight});
+}
+
+
+
+/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
+
+
+
+
+
+/** Scroll the jScrollPane to make the currently selected item visible
+    This is called when the page finished loading. */
+function scrollIntoView(nav) {
+  return;
+  var $nav = $("#"+nav);
+  var element = $nav.jScrollPane({/* ...settings... */});
+  var api = element.data('jsp');
+
+  if ($nav.is(':visible')) {
+    var $selected = $(".selected", $nav);
+    if ($selected.length == 0) {
+      // If no selected item found, exit
+      return;
+    }
+    // get the selected item's offset from its container nav by measuring the item's offset
+    // relative to the document then subtract the container nav's offset relative to the document
+    var selectedOffset = $selected.offset().top - $nav.offset().top + 60;
+    if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
+                                               // if it's more than 80% down the nav
+      // scroll the item up by an amount equal to 80% the container nav's height
+      api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
+    }
+  }
+}
+
+
+
+
+
+
 /* Show popup dialogs */
 function showDialog(id) {
-  $dialog = $("#" + id);
+  $dialog = $("#"+id);
   $dialog.prepend('<div class="box-border"><div class="top"> <div class="left"></div> <div class="right"></div></div><div class="bottom"> <div class="left"></div> <div class="right"></div> </div> </div>');
   $dialog.wrapInner('<div/>');
   $dialog.removeClass("hide");
 }
 
+
+
+
+
 /* #########    COOKIES!     ########## */
 
 function readCookie(cookie) {
-  var myCookie = cookie_namespace + "_" + cookie + "=";
+  var myCookie = cookie_namespace+"_"+cookie+"=";
   if (document.cookie) {
     var index = document.cookie.indexOf(myCookie);
     if (index != -1) {
@@ -400,16 +1031,98 @@
 }
 
 function writeCookie(cookie, val, section) {
-  if (val == undefined) return;
-  section = section == null ? "_" : "_" + section + "_";
-  var age = 2 * 365 * 24 * 60 * 60; // set max-age to 2 years
-  var cookieValue = cookie_namespace + section + cookie + "=" + val +
-                    "; max-age=" + age + "; path=/";
+  if (val==undefined) return;
+  section = section == null ? "_" : "_"+section+"_";
+  var age = 2*365*24*60*60; // set max-age to 2 years
+  var cookieValue = cookie_namespace + section + cookie + "=" + val
+                    + "; max-age=" + age +"; path=/";
   document.cookie = cookieValue;
 }
 
 /* #########     END COOKIES!     ########## */
 
+
+var sticky = false;
+var stickyTop;
+var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
+/* Sets the vertical scoll position at which the sticky bar should appear.
+   This method is called to reset the position when search results appear or hide */
+function setStickyTop() {
+  stickyTop = $('#header-wrapper').outerHeight() - $('#header > .dac-header-inner').outerHeight();
+}
+
+/*
+ * Displays sticky nav bar on pages when dac header scrolls out of view
+ */
+$(window).scroll(function(event) {
+  // Exit if the mouse target is a DIV, because that means the event is coming
+  // from a scrollable div and so there's no need to make adjustments to our layout
+  if ($(event.target).nodeName == "DIV") {
+    return;
+  }
+
+  checkSticky();
+});
+
+function checkSticky() {
+  setStickyTop();
+  var $headerEl = $('#header');
+  // Exit if there's no sidenav
+  if ($('#side-nav').length == 0) return;
+
+  var top = $(window).scrollTop();
+  // we set the navbar fixed when the scroll position is beyond the height of the site header...
+  var shouldBeSticky = top > stickyTop;
+  // ... except if the document content is shorter than the sidenav height.
+  // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
+  if ($("#doc-col").height() < $("#side-nav").height()) {
+    shouldBeSticky = false;
+  }
+  // Nor on mobile
+  if (window.innerWidth < 720) {
+    shouldBeSticky = false;
+  }
+  // Account for horizontal scroll
+  var scrollLeft = $(window).scrollLeft();
+  // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
+  if (sticky && (scrollLeft != prevScrollLeft)) {
+    updateSideNavDimensions();
+    prevScrollLeft = scrollLeft;
+  }
+
+  // Don't continue if the header is sufficently far away
+  // (to avoid intensive resizing that slows scrolling)
+  if (sticky == shouldBeSticky) {
+    return;
+  }
+
+  // If sticky header visible and position is now near top, hide sticky
+  if (sticky && !shouldBeSticky) {
+    sticky = false;
+    // make the sidenav static again
+    $('#devdoc-nav')
+      .removeClass('fixed')
+      .css({'width':'auto','margin':''});
+    // delay hide the sticky
+    $headerEl.removeClass('is-sticky');
+
+    // update the sidenaav position for side scrolling
+    updateSideNavDimensions();
+  } else if (!sticky && shouldBeSticky) {
+    sticky = true;
+    $headerEl.addClass('is-sticky');
+
+    // make the sidenav fixed
+    $('#devdoc-nav')
+      .addClass('fixed');
+
+    // update the sidenaav position for side scrolling
+    updateSideNavDimensions();
+
+  }
+  resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
+}
+
 /*
  * Manages secion card states and nav resize to conclude loading
  */
@@ -419,7 +1132,7 @@
     // Stack hover states
     $('.section-card-menu').each(function(index, el) {
       var height = $(el).height();
-      $(el).css({height:height + 'px', position:'relative'});
+      $(el).css({height:height+'px', position:'relative'});
       var $cardInfo = $(el).find('.card-info');
 
       $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
@@ -429,8 +1142,25 @@
 
 })();
 
+
+
+
+
+
+
+
+
+
+
+
+
+
 /*      MISC LIBRARY FUNCTIONS     */
 
+
+
+
+
 function toggle(obj, slide) {
   var ul = $("ul:first", obj);
   var li = ul.parent();
@@ -451,6 +1181,7 @@
   }
 }
 
+
 function buildToggleLists() {
   $(".toggle-list").each(
     function(i) {
@@ -459,10 +1190,12 @@
     });
 }
 
+
+
 function hideNestedItems(list, toggle) {
   $list = $(list);
   // hide nested lists
-  if ($list.hasClass('showing')) {
+  if($list.hasClass('showing')) {
     $("li ol", $list).hide('fast');
     $list.removeClass('showing');
   // show nested lists
@@ -470,47 +1203,207 @@
     $("li ol", $list).show('fast');
     $list.addClass('showing');
   }
-  $(".more,.less", $(toggle)).toggle();
+  $(".more,.less",$(toggle)).toggle();
 }
 
+
 /* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
 function setupIdeDocToggle() {
-  $("select.ide").change(function() {
+  $( "select.ide" ).change(function() {
     var selected = $(this).find("option:selected").attr("value");
     $(".select-ide").hide();
-    $(".select-ide." + selected).show();
+    $(".select-ide."+selected).show();
 
     $("select.ide").val(selected);
   });
 }
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*      REFERENCE NAV SWAP     */
+
+
+function getNavPref() {
+  var v = readCookie('reference_nav');
+  if (v != NAV_PREF_TREE) {
+    v = NAV_PREF_PANELS;
+  }
+  return v;
+}
+
+function chooseDefaultNav() {
+  nav_pref = getNavPref();
+  if (nav_pref == NAV_PREF_TREE) {
+    $("#nav-panels").toggle();
+    $("#panel-link").toggle();
+    $("#nav-tree").toggle();
+    $("#tree-link").toggle();
+  }
+}
+
+function swapNav() {
+  if (nav_pref == NAV_PREF_TREE) {
+    nav_pref = NAV_PREF_PANELS;
+  } else {
+    nav_pref = NAV_PREF_TREE;
+    init_default_navtree(toRoot);
+  }
+  writeCookie("nav", nav_pref, "reference");
+
+  $("#nav-panels").toggle();
+  $("#panel-link").toggle();
+  $("#nav-tree").toggle();
+  $("#tree-link").toggle();
+
+  resizeNav();
+
+  // Gross nasty hack to make tree view show up upon first swap by setting height manually
+  $("#nav-tree .jspContainer:visible")
+      .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
+  // Another nasty hack to make the scrollbar appear now that we have height
+  resizeNav();
+
+  if ($("#nav-tree").is(':visible')) {
+    scrollIntoView("nav-tree");
+  } else {
+    scrollIntoView("packages-nav");
+    scrollIntoView("classes-nav");
+  }
+}
+
+
+
+/* ############################################ */
+/* ##########     LOCALIZATION     ############ */
+/* ############################################ */
+
+function getBaseUri(uri) {
+  var intlUrl = (uri.substring(0,6) == "/intl/");
+  if (intlUrl) {
+    base = uri.substring(uri.indexOf('intl/')+5,uri.length);
+    base = base.substring(base.indexOf('/')+1, base.length);
+      //alert("intl, returning base url: /" + base);
+    return ("/" + base);
+  } else {
+      //alert("not intl, returning uri as found.");
+    return uri;
+  }
+}
+
+function requestAppendHL(uri) {
+//append "?hl=<lang> to an outgoing request (such as to blog)
+  var lang = getLangPref();
+  if (lang) {
+    var q = 'hl=' + lang;
+    uri += '?' + q;
+    window.location = uri;
+    return false;
+  } else {
+    return true;
+  }
+}
+
+
+function changeNavLang(lang) {
+  if (lang === 'en') { return; }
+
+  var $links = $("a[" + lang + "-lang],p[" + lang + "-lang]");
+  $links.each(function(){ // for each link with a translation
+    var $link = $(this);
+    // put the desired language from the attribute as the text
+    $link.text($link.attr(lang + '-lang'))
+  });
+}
+
+function changeLangPref(lang, submit) {
+  writeCookie("pref_lang", lang, null);
+
+  //  #######  TODO:  Remove this condition once we're stable on devsite #######
+  //  This condition is only needed if we still need to support legacy GAE server
+  if (devsite) {
+    // Switch language when on Devsite server
+    if (submit) {
+      $("#setlang").submit();
+    }
+  } else {
+    // Switch language when on legacy GAE server
+    if (submit) {
+      window.location = getBaseUri(location.pathname);
+    }
+  }
+}
+
+function loadLangPref() {
+  var lang = readCookie("pref_lang");
+  if (lang != 0) {
+    $("#language").find("option[value='"+lang+"']").attr("selected",true);
+  }
+}
+
+function getLangPref() {
+  var lang = $("#language").find(":selected").attr("value");
+  if (!lang) {
+    lang = readCookie("pref_lang");
+  }
+  return (lang != 0) ? lang : 'en';
+}
+
+/* ##########     END LOCALIZATION     ############ */
+
+
+
+
+
+
 /* Used to hide and reveal supplemental content, such as long code samples.
    See the companion CSS in android-developer-docs.css */
 function toggleContent(obj) {
   var div = $(obj).closest(".toggle-content");
-  var toggleMe = $(".toggle-content-toggleme:eq(0)", div);
+  var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
   if (div.hasClass("closed")) { // if it's closed, open it
     toggleMe.slideDown();
     $(".toggle-content-text:eq(0)", obj).toggle();
     div.removeClass("closed").addClass("open");
-    $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot +
-                  "assets/images/triangle-opened.png");
+    $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
+                  + "assets/images/styles/disclosure_up.png");
   } else { // if it's open, close it
     toggleMe.slideUp('fast', function() {  // Wait until the animation is done before closing arrow
       $(".toggle-content-text:eq(0)", obj).toggle();
       div.removeClass("open").addClass("closed");
       div.find(".toggle-content").removeClass("open").addClass("closed")
               .find(".toggle-content-toggleme").hide();
-      $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot +
-                  "assets/images/triangle-closed.png");
+      $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
+                  + "assets/images/styles/disclosure_down.png");
     });
   }
   return false;
 }
 
+
 /* New version of expandable content */
-function toggleExpandable(link, id) {
-  if ($(id).is(':visible')) {
+function toggleExpandable(link,id) {
+  if($(id).is(':visible')) {
     $(id).slideUp();
     $(link).removeClass('expanded');
   } else {
@@ -524,6 +1417,10 @@
   $(ids).prev('h4').find('a.expandable').removeClass('expanded');
 }
 
+
+
+
+
 /*
  *  Slideshow 1.0
  *  Used on /index.html and /develop/index.html for carousel
@@ -563,164 +1460,169 @@
  *
  */
 
-(function($) {
-  $.fn.dacSlideshow = function(o) {
+ (function($) {
+ $.fn.dacSlideshow = function(o) {
 
-    //Options - see above
-    o = $.extend({
-      btnPrev:   null,
-      btnNext:   null,
-      btnPause:  null,
-      auto:      true,
-      speed:     500,
-      autoTime:  12000,
-      easing:    null,
-      start:     0,
-      scroll:    1,
-      pagination: true
+     //Options - see above
+     o = $.extend({
+         btnPrev:   null,
+         btnNext:   null,
+         btnPause:  null,
+         auto:      true,
+         speed:     500,
+         autoTime:  12000,
+         easing:    null,
+         start:     0,
+         scroll:    1,
+         pagination: true
 
-    }, o || {});
+     }, o || {});
 
-    //Set up a carousel for each
-    return this.each(function() {
+     //Set up a carousel for each
+     return this.each(function() {
 
-      var running = false;
-      var animCss = o.vertical ? "top" : "left";
-      var sizeCss = o.vertical ? "height" : "width";
-      var div = $(this);
-      var ul = $("ul", div);
-      var tLi = $("li", ul);
-      var tl = tLi.size();
-      var timer = null;
+         var running = false;
+         var animCss = o.vertical ? "top" : "left";
+         var sizeCss = o.vertical ? "height" : "width";
+         var div = $(this);
+         var ul = $("ul", div);
+         var tLi = $("li", ul);
+         var tl = tLi.size();
+         var timer = null;
 
-      var li = $("li", ul);
-      var itemLength = li.size();
-      var curr = o.start;
+         var li = $("li", ul);
+         var itemLength = li.size();
+         var curr = o.start;
 
-      li.css({float: o.vertical ? "none" : "left"});
-      ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
-      div.css({position: "relative", "z-index": "2", left: "0px"});
+         li.css({float: o.vertical ? "none" : "left"});
+         ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
+         div.css({position: "relative", "z-index": "2", left: "0px"});
 
-      var liSize = o.vertical ? height(li) : width(li);
-      var ulSize = liSize * itemLength;
-      var divSize = liSize;
+         var liSize = o.vertical ? height(li) : width(li);
+         var ulSize = liSize * itemLength;
+         var divSize = liSize;
 
-      li.css({width: li.width(), height: li.height()});
-      ul.css(sizeCss, ulSize + "px").css(animCss, -(curr * liSize));
+         li.css({width: li.width(), height: li.height()});
+         ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
 
-      div.css(sizeCss, divSize + "px");
+         div.css(sizeCss, divSize+"px");
 
-      //Pagination
-      if (o.pagination) {
-        var pagination = $("<div class='pagination'></div>");
-        var pag_ul = $("<ul></ul>");
-        if (tl > 1) {
-          for (var i = 0; i < tl; i++) {
-            var li = $("<li>" + i + "</li>");
-            pag_ul.append(li);
-            if (i == o.start) li.addClass('active');
-            li.click(function() {
-              go(parseInt($(this).text()));
-            })
-          }
-          pagination.append(pag_ul);
-          div.append(pagination);
-        }
-      }
+         //Pagination
+         if (o.pagination) {
+             var pagination = $("<div class='pagination'></div>");
+             var pag_ul = $("<ul></ul>");
+             if (tl > 1) {
+               for (var i=0;i<tl;i++) {
+                    var li = $("<li>"+i+"</li>");
+                    pag_ul.append(li);
+                    if (i==o.start) li.addClass('active');
+                        li.click(function() {
+                        go(parseInt($(this).text()));
+                    })
+                }
+                pagination.append(pag_ul);
+                div.append(pagination);
+             }
+         }
 
-      //Previous button
-      if (o.btnPrev)
+         //Previous button
+         if(o.btnPrev)
              $(o.btnPrev).click(function(e) {
-               e.preventDefault();
-               return go(curr - o.scroll);
+                 e.preventDefault();
+                 return go(curr-o.scroll);
              });
 
-      //Next button
-      if (o.btnNext)
+         //Next button
+         if(o.btnNext)
              $(o.btnNext).click(function(e) {
-               e.preventDefault();
-               return go(curr + o.scroll);
+                 e.preventDefault();
+                 return go(curr+o.scroll);
              });
 
-      //Pause button
-      if (o.btnPause)
+         //Pause button
+         if(o.btnPause)
              $(o.btnPause).click(function(e) {
-               e.preventDefault();
-               if ($(this).hasClass('paused')) {
-                 startRotateTimer();
-               } else {
-                 pauseRotateTimer();
-               }
+                 e.preventDefault();
+                 if ($(this).hasClass('paused')) {
+                     startRotateTimer();
+                 } else {
+                     pauseRotateTimer();
+                 }
              });
 
-      //Auto rotation
-      if (o.auto) startRotateTimer();
+         //Auto rotation
+         if(o.auto) startRotateTimer();
 
-      function startRotateTimer() {
-        clearInterval(timer);
-        timer = setInterval(function() {
-          if (curr == tl - 1) {
-            go(0);
-          } else {
-            go(curr + o.scroll);
-          }
-        }, o.autoTime);
-        $(o.btnPause).removeClass('paused');
-      }
+         function startRotateTimer() {
+             clearInterval(timer);
+             timer = setInterval(function() {
+                  if (curr == tl-1) {
+                    go(0);
+                  } else {
+                    go(curr+o.scroll);
+                  }
+              }, o.autoTime);
+             $(o.btnPause).removeClass('paused');
+         }
 
-      function pauseRotateTimer() {
-        clearInterval(timer);
-        $(o.btnPause).addClass('paused');
-      }
+         function pauseRotateTimer() {
+             clearInterval(timer);
+             $(o.btnPause).addClass('paused');
+         }
 
-      //Go to an item
-      function go(to) {
-        if (!running) {
+         //Go to an item
+         function go(to) {
+             if(!running) {
 
-          if (to < 0) {
-            to = itemLength - 1;
-          } else if (to > itemLength - 1) {
-            to = 0;
-          }
-          curr = to;
+                 if(to<0) {
+                    to = itemLength-1;
+                 } else if (to>itemLength-1) {
+                    to = 0;
+                 }
+                 curr = to;
 
-          running = true;
+                 running = true;
 
-          ul.animate(
-              animCss == "left" ? {left: -(curr * liSize)} : {top: -(curr * liSize)} , o.speed, o.easing,
+                 ul.animate(
+                     animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
                      function() {
-                       running = false;
+                         running = false;
                      }
                  );
 
-          $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
-          $((curr - o.scroll < 0 && o.btnPrev)              ||
-             (curr + o.scroll > itemLength && o.btnNext)              ||
-             []
-           ).addClass("disabled");
+                 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
+                 $( (curr-o.scroll<0 && o.btnPrev)
+                     ||
+                    (curr+o.scroll > itemLength && o.btnNext)
+                     ||
+                    []
+                  ).addClass("disabled");
 
-          var nav_items = $('li', pagination);
-          nav_items.removeClass('active');
-          nav_items.eq(to).addClass('active');
 
-        }
-        if (o.auto) startRotateTimer();
-        return false;
-      };
-    });
-  };
+                 var nav_items = $('li', pagination);
+                 nav_items.removeClass('active');
+                 nav_items.eq(to).addClass('active');
 
-  function css(el, prop) {
-    return parseInt($.css(el[0], prop)) || 0;
-  };
-  function width(el) {
-    return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
-  };
-  function height(el) {
-    return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
-  };
 
-})(jQuery);
+             }
+             if(o.auto) startRotateTimer();
+             return false;
+         };
+     });
+ };
+
+ function css(el, prop) {
+     return parseInt($.css(el[0], prop)) || 0;
+ };
+ function width(el) {
+     return  el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
+ };
+ function height(el) {
+     return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
+ };
+
+ })(jQuery);
+
 
 /*
  *  dacSlideshow 1.0
@@ -759,68 +1661,1204 @@
  *  pagination: whether or not to include dotted pagination
  *
  */
-(function($) {
-  $.fn.dacTabbedList = function(o) {
+ (function($) {
+ $.fn.dacTabbedList = function(o) {
 
-    //Options - see above
-    o = $.extend({
-      speed : 250,
-      easing: null,
-      nav_id: null,
-      frame_id: null
-    }, o || {});
+     //Options - see above
+     o = $.extend({
+         speed : 250,
+         easing: null,
+         nav_id: null,
+         frame_id: null
+     }, o || {});
 
-    //Set up a carousel for each
-    return this.each(function() {
+     //Set up a carousel for each
+     return this.each(function() {
 
-      var curr = 0;
-      var running = false;
-      var animCss = "margin-left";
-      var sizeCss = "width";
-      var div = $(this);
+         var curr = 0;
+         var running = false;
+         var animCss = "margin-left";
+         var sizeCss = "width";
+         var div = $(this);
 
-      var nav = $(o.nav_id, div);
-      var nav_li = $("li", nav);
-      var nav_size = nav_li.size();
-      var frame = div.find(o.frame_id);
-      var content_width = $(frame).find('ul').width();
-      //Buttons
-      $(nav_li).click(function(e) {
+         var nav = $(o.nav_id, div);
+         var nav_li = $("li", nav);
+         var nav_size = nav_li.size();
+         var frame = div.find(o.frame_id);
+         var content_width = $(frame).find('ul').width();
+         //Buttons
+         $(nav_li).click(function(e) {
            go($(nav_li).index($(this)));
          })
 
-      //Go to an item
-      function go(to) {
-        if (!running) {
-          curr = to;
-          running = true;
+         //Go to an item
+         function go(to) {
+             if(!running) {
+                 curr = to;
+                 running = true;
 
-          frame.animate({'margin-left' : -(curr * content_width)}, o.speed, o.easing,
+                 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
                      function() {
-                       running = false;
+                         running = false;
                      }
                  );
 
-          nav_li.removeClass('active');
-          nav_li.eq(to).addClass('active');
 
+                 nav_li.removeClass('active');
+                 nav_li.eq(to).addClass('active');
+
+
+             }
+             return false;
+         };
+     });
+ };
+
+ function css(el, prop) {
+     return parseInt($.css(el[0], prop)) || 0;
+ };
+ function width(el) {
+     return  el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
+ };
+ function height(el) {
+     return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
+ };
+
+ })(jQuery);
+
+
+
+
+
+/* ######################################################## */
+/* ################  SEARCH SUGGESTIONS  ################## */
+/* ######################################################## */
+
+
+
+var gSelectedIndex = -1;  // the index position of currently highlighted suggestion
+var gSelectedColumn = -1;  // which column of suggestion lists is currently focused
+
+var gMatches = new Array();
+var gLastText = "";
+var gInitialized = false;
+var ROW_COUNT_FRAMEWORK = 20;       // max number of results in list
+var gListLength = 0;
+
+
+var gGoogleMatches = new Array();
+var ROW_COUNT_GOOGLE = 15;          // max number of results in list
+var gGoogleListLength = 0;
+
+var gDocsMatches = new Array();
+var ROW_COUNT_DOCS = 100;          // max number of results in list
+var gDocsListLength = 0;
+
+function onSuggestionClick(link) {
+  // When user clicks a suggested document, track it
+  ga('send', 'event', 'Suggestion Click', 'clicked: ' + $(link).attr('href'),
+                'query: ' + $("#search_autocomplete").val().toLowerCase());
+}
+
+function set_item_selected($li, selected)
+{
+    if (selected) {
+        $li.attr('class','jd-autocomplete jd-selected');
+    } else {
+        $li.attr('class','jd-autocomplete');
+    }
+}
+
+function set_item_values(toroot, $li, match)
+{
+    var $link = $('a',$li);
+    $link.html(match.__hilabel || match.label);
+    $link.attr('href',toroot + match.link);
+}
+
+function set_item_values_jd(toroot, $li, match)
+{
+    var $link = $('a',$li);
+    $link.html(match.title);
+    $link.attr('href',toroot + match.url);
+}
+
+function new_suggestion($list) {
+    var $li = $("<li class='jd-autocomplete'></li>");
+    $list.append($li);
+
+    $li.mousedown(function() {
+        window.location = this.firstChild.getAttribute("href");
+    });
+    $li.mouseover(function() {
+        $('.search_filtered_wrapper li').removeClass('jd-selected');
+        $(this).addClass('jd-selected');
+        gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
+        gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
+    });
+    $li.append("<a onclick='onSuggestionClick(this)'></a>");
+    $li.attr('class','show-item');
+    return $li;
+}
+
+function sync_selection_table(toroot)
+{
+    var $li; //list item jquery object
+    var i; //list item iterator
+
+    // if there are NO results at all, hide all columns
+    if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
+        $('.suggest-card').hide(300);
+        return;
+    }
+
+    // if there are api results
+    if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
+      // reveal suggestion list
+      $('.suggest-card.reference').show();
+      var listIndex = 0; // list index position
+
+      // reset the lists
+      $(".suggest-card.reference li").remove();
+
+      // ########### ANDROID RESULTS #############
+      if (gMatches.length > 0) {
+
+          // determine android results to show
+          gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
+                        gMatches.length : ROW_COUNT_FRAMEWORK;
+          for (i=0; i<gListLength; i++) {
+              var $li = new_suggestion($(".suggest-card.reference ul"));
+              set_item_values(toroot, $li, gMatches[i]);
+              set_item_selected($li, i == gSelectedIndex);
+          }
+      }
+
+      // ########### GOOGLE RESULTS #############
+      if (gGoogleMatches.length > 0) {
+          // show header for list
+          $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
+
+          // determine google results to show
+          gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
+          for (i=0; i<gGoogleListLength; i++) {
+              var $li = new_suggestion($(".suggest-card.reference ul"));
+              set_item_values(toroot, $li, gGoogleMatches[i]);
+              set_item_selected($li, i == gSelectedIndex);
+          }
+      }
+    } else {
+      $('.suggest-card.reference').hide();
+    }
+
+    // ########### JD DOC RESULTS #############
+    if (gDocsMatches.length > 0) {
+        // reset the lists
+        $(".suggest-card:not(.reference) li").remove();
+
+        // determine google results to show
+        // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
+        // The order must match the reverse order that each section appears as a card in
+        // the suggestion UI... this may be only for the "develop" grouped items though.
+        gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
+        for (i=0; i<gDocsListLength; i++) {
+            var sugg = gDocsMatches[i];
+            var $li;
+            if (sugg.type == "design") {
+                $li = new_suggestion($(".suggest-card.design ul"));
+            } else
+            if (sugg.type == "distribute") {
+                $li = new_suggestion($(".suggest-card.distribute ul"));
+            } else
+            if (sugg.type == "samples") {
+                $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
+            } else
+            if (sugg.type == "training") {
+                $li = new_suggestion($(".suggest-card.develop .child-card.training"));
+            } else
+            if (sugg.type == "about"||"guide"||"tools"||"google") {
+                $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
+            } else {
+              continue;
+            }
+
+            set_item_values_jd(toroot, $li, sugg);
+            set_item_selected($li, i == gSelectedIndex);
+        }
+
+        // add heading and show or hide card
+        if ($(".suggest-card.design li").length > 0) {
+          $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
+          $(".suggest-card.design").show(300);
+        } else {
+          $('.suggest-card.design').hide(300);
+        }
+        if ($(".suggest-card.distribute li").length > 0) {
+          $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
+          $(".suggest-card.distribute").show(300);
+        } else {
+          $('.suggest-card.distribute').hide(300);
+        }
+        if ($(".child-card.guides li").length > 0) {
+          $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
+          $(".child-card.guides li").appendTo(".suggest-card.develop ul");
+        }
+        if ($(".child-card.training li").length > 0) {
+          $(".child-card.training").prepend("<li class='header'>Training:</li>");
+          $(".child-card.training li").appendTo(".suggest-card.develop ul");
+        }
+        if ($(".child-card.samples li").length > 0) {
+          $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
+          $(".child-card.samples li").appendTo(".suggest-card.develop ul");
+        }
+
+        if ($(".suggest-card.develop li").length > 0) {
+          $(".suggest-card.develop").show(300);
+        } else {
+          $('.suggest-card.develop').hide(300);
+        }
+
+    } else {
+      $('.suggest-card:not(.reference)').hide(300);
+    }
+}
+
+/** Called by the search input's onkeydown and onkeyup events.
+  * Handles navigation with keyboard arrows, Enter key to invoke search,
+  * otherwise invokes search suggestions on key-up event.
+  * @param e       The JS event
+  * @param kd      True if the event is key-down
+  * @param toroot  A string for the site's root path
+  * @returns       True if the event should bubble up
+  */
+function search_changed(e, kd, toroot)
+{
+    var currentLang = getLangPref();
+    var search = document.getElementById("search_autocomplete");
+    var text = search.value.replace(/(^ +)|( +$)/g, '');
+    // get the ul hosting the currently selected item
+    gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn :  0;
+    var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
+    var $selectedUl = $columns[gSelectedColumn];
+
+    // show/hide the close button
+    if (text != '') {
+        $("#search-close").removeClass("hide");
+    } else {
+        $("#search-close").addClass("hide");
+    }
+    // 27 = esc
+    if (e.keyCode == 27) {
+        // close all search results
+        if (kd) $('#search-close').trigger('click');
+        return true;
+    }
+    // 13 = enter
+    else if (e.keyCode == 13) {
+        if (gSelectedIndex < 0) {
+            $('.suggest-card').hide();
+            if ($("#searchResults").is(":hidden") && (search.value != "")) {
+              // if results aren't showing (and text not empty), return true to allow search to execute
+              $('body,html').animate({scrollTop:0}, '500', 'swing');
+              return true;
+            } else {
+              // otherwise, results are already showing, so allow ajax to auto refresh the results
+              // and ignore this Enter press to avoid the reload.
+              return false;
+            }
+        } else if (kd && gSelectedIndex >= 0) {
+            // click the link corresponding to selected item
+            $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
+            return false;
+        }
+    }
+    // If Google results are showing, return true to allow ajax search to execute
+    else if ($("#searchResults").is(":visible")) {
+        // Also, if search_results is scrolled out of view, scroll to top to make results visible
+        if ((sticky ) && (search.value != "")) {
+          $('body,html').animate({scrollTop:0}, '500', 'swing');
+        }
+        return true;
+    }
+    // 38 UP ARROW
+    else if (kd && (e.keyCode == 38)) {
+        // if the next item is a header, skip it
+        if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
+            gSelectedIndex--;
+        }
+        if (gSelectedIndex >= 0) {
+            $('li', $selectedUl).removeClass('jd-selected');
+            gSelectedIndex--;
+            $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
+            // If user reaches top, reset selected column
+            if (gSelectedIndex < 0) {
+              gSelectedColumn = -1;
+            }
         }
         return false;
-      };
+    }
+    // 40 DOWN ARROW
+    else if (kd && (e.keyCode == 40)) {
+        // if the next item is a header, skip it
+        if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
+            gSelectedIndex++;
+        }
+        if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
+                        ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
+            $('li', $selectedUl).removeClass('jd-selected');
+            gSelectedIndex++;
+            $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
+        }
+        return false;
+    }
+    // Consider left/right arrow navigation
+    // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
+    else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
+      // 37 LEFT ARROW
+      // go left only if current column is not left-most column (last column)
+      if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
+        $('li', $selectedUl).removeClass('jd-selected');
+        gSelectedColumn++;
+        $selectedUl = $columns[gSelectedColumn];
+        // keep or reset the selected item to last item as appropriate
+        gSelectedIndex = gSelectedIndex >
+                $("li", $selectedUl).length-1 ?
+                $("li", $selectedUl).length-1 : gSelectedIndex;
+        // if the corresponding item is a header, move down
+        if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
+          gSelectedIndex++;
+        }
+        // set item selected
+        $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
+        return false;
+      }
+      // 39 RIGHT ARROW
+      // go right only if current column is not the right-most column (first column)
+      else if (e.keyCode == 39 && gSelectedColumn > 0) {
+        $('li', $selectedUl).removeClass('jd-selected');
+        gSelectedColumn--;
+        $selectedUl = $columns[gSelectedColumn];
+        // keep or reset the selected item to last item as appropriate
+        gSelectedIndex = gSelectedIndex >
+                $("li", $selectedUl).length-1 ?
+                $("li", $selectedUl).length-1 : gSelectedIndex;
+        // if the corresponding item is a header, move down
+        if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
+          gSelectedIndex++;
+        }
+        // set item selected
+        $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
+        return false;
+      }
+    }
+
+    // if key-up event and not arrow down/up/left/right,
+    // read the search query and add suggestions to gMatches
+    else if (!kd && (e.keyCode != 40)
+                 && (e.keyCode != 38)
+                 && (e.keyCode != 37)
+                 && (e.keyCode != 39)) {
+        gSelectedIndex = -1;
+        gMatches = new Array();
+        matchedCount = 0;
+        gGoogleMatches = new Array();
+        matchedCountGoogle = 0;
+        gDocsMatches = new Array();
+        matchedCountDocs = 0;
+
+        // Search for Android matches
+        for (var i=0; i<DATA.length; i++) {
+            var s = DATA[i];
+            if (text.length != 0 &&
+                  s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
+                gMatches[matchedCount] = s;
+                matchedCount++;
+            }
+        }
+        rank_autocomplete_api_results(text, gMatches);
+        for (var i=0; i<gMatches.length; i++) {
+            var s = gMatches[i];
+        }
+
+
+        // Search for Google matches
+        for (var i=0; i<GOOGLE_DATA.length; i++) {
+            var s = GOOGLE_DATA[i];
+            if (text.length != 0 &&
+                  s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
+                gGoogleMatches[matchedCountGoogle] = s;
+                matchedCountGoogle++;
+            }
+        }
+        rank_autocomplete_api_results(text, gGoogleMatches);
+        for (var i=0; i<gGoogleMatches.length; i++) {
+            var s = gGoogleMatches[i];
+        }
+
+        highlight_autocomplete_result_labels(text);
+
+
+
+        // Search for matching JD docs
+        if (text.length >= 2) {
+          // match only the beginning of a word
+          var queryStr = text.toLowerCase();
+
+          // Search for Training classes
+          for (var i=0; i<TRAINING_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = TRAINING_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Don't consider doc title for lessons (only for class landing pages),
+            // unless the lesson has a tag that already matches
+            if ((s.lang == currentLang) &&
+                  (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
+              // it matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for API Guides
+          for (var i=0; i<GUIDE_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = GUIDE_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for Tools Guides
+          for (var i=0; i<TOOLS_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = TOOLS_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+                if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+                if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for About docs
+          for (var i=0; i<ABOUT_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = ABOUT_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for Design guides
+          for (var i=0; i<DESIGN_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = DESIGN_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for Distribute guides
+          for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = DISTRIBUTE_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for Google guides
+          for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = GOOGLE_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+
+          // Search for Samples
+          for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = SAMPLES_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title.t
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+          // Search for Preview Guides
+          for (var i=0; i<PREVIEW_RESOURCES.length; i++) {
+            // current search comparison, with counters for tag and title,
+            // used later to improve ranking
+            var s = PREVIEW_RESOURCES[i];
+            s.matched_tag = 0;
+            s.matched_title = 0;
+            var matched = false;
+
+            // Check if query matches any tags; work backwards toward 1 to assist ranking
+            for (var j = s.keywords.length - 1; j >= 0; j--) {
+              // it matches a tag
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_tag = j + 1; // add 1 to index position
+              }
+            }
+            // Check if query matches the doc title, but only for current language
+            if (s.lang == currentLang) {
+              // if query matches the doc title
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
+                matched = true;
+                s.matched_title = 1;
+              }
+            }
+            if (matched) {
+              gDocsMatches[matchedCountDocs] = s;
+              matchedCountDocs++;
+            }
+          }
+
+          // Rank/sort all the matched pages
+          rank_autocomplete_doc_results(text, gDocsMatches);
+        }
+
+        // draw the suggestions
+        sync_selection_table(toroot);
+        return true; // allow the event to bubble up to the search api
+    }
+}
+
+/* Order the jd doc result list based on match quality */
+function rank_autocomplete_doc_results(query, matches) {
+    query = query || '';
+    if (!matches || !matches.length)
+      return;
+
+    var _resultScoreFn = function(match) {
+        var score = 1.0;
+
+        // if the query matched a tag
+        if (match.matched_tag > 0) {
+          // multiply score by factor relative to position in tags list (max of 3)
+          score *= 3 / match.matched_tag;
+
+          // if it also matched the title
+          if (match.matched_title > 0) {
+            score *= 2;
+          }
+        } else if (match.matched_title > 0) {
+          score *= 3;
+        }
+
+        return score;
+    };
+
+    for (var i=0; i<matches.length; i++) {
+        matches[i].__resultScore = _resultScoreFn(matches[i]);
+    }
+
+    matches.sort(function(a,b){
+        var n = b.__resultScore - a.__resultScore;
+        if (n == 0) // lexicographical sort if scores are the same
+            n = (a.label < b.label) ? -1 : 1;
+        return n;
     });
+}
+
+/* Order the result list based on match quality */
+function rank_autocomplete_api_results(query, matches) {
+    query = query || '';
+    if (!matches || !matches.length)
+      return;
+
+    // helper function that gets the last occurence index of the given regex
+    // in the given string, or -1 if not found
+    var _lastSearch = function(s, re) {
+      if (s == '')
+        return -1;
+      var l = -1;
+      var tmp;
+      while ((tmp = s.search(re)) >= 0) {
+        if (l < 0) l = 0;
+        l += tmp;
+        s = s.substr(tmp + 1);
+      }
+      return l;
+    };
+
+    // helper function that counts the occurrences of a given character in
+    // a given string
+    var _countChar = function(s, c) {
+      var n = 0;
+      for (var i=0; i<s.length; i++)
+        if (s.charAt(i) == c) ++n;
+      return n;
+    };
+
+    var queryLower = query.toLowerCase();
+    var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
+    var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
+    var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
+
+    var _resultScoreFn = function(result) {
+        // scores are calculated based on exact and prefix matches,
+        // and then number of path separators (dots) from the last
+        // match (i.e. favoring classes and deep package names)
+        var score = 1.0;
+        var labelLower = result.label.toLowerCase();
+        var t;
+        t = _lastSearch(labelLower, partExactAlnumRE);
+        if (t >= 0) {
+            // exact part match
+            var partsAfter = _countChar(labelLower.substr(t + 1), '.');
+            score *= 200 / (partsAfter + 1);
+        } else {
+            t = _lastSearch(labelLower, partPrefixAlnumRE);
+            if (t >= 0) {
+                // part prefix match
+                var partsAfter = _countChar(labelLower.substr(t + 1), '.');
+                score *= 20 / (partsAfter + 1);
+            }
+        }
+
+        return score;
+    };
+
+    for (var i=0; i<matches.length; i++) {
+        // if the API is deprecated, default score is 0; otherwise, perform scoring
+        if (matches[i].deprecated == "true") {
+          matches[i].__resultScore = 0;
+        } else {
+          matches[i].__resultScore = _resultScoreFn(matches[i]);
+        }
+    }
+
+    matches.sort(function(a,b){
+        var n = b.__resultScore - a.__resultScore;
+        if (n == 0) // lexicographical sort if scores are the same
+            n = (a.label < b.label) ? -1 : 1;
+        return n;
+    });
+}
+
+/* Add emphasis to part of string that matches query */
+function highlight_autocomplete_result_labels(query) {
+    query = query || '';
+    if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
+      return;
+
+    var queryLower = query.toLowerCase();
+    var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
+    var queryRE = new RegExp(
+        '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
+    for (var i=0; i<gMatches.length; i++) {
+        gMatches[i].__hilabel = gMatches[i].label.replace(
+            queryRE, '<b>$1</b>');
+    }
+    for (var i=0; i<gGoogleMatches.length; i++) {
+        gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
+            queryRE, '<b>$1</b>');
+    }
+}
+
+function search_focus_changed(obj, focused)
+{
+    if (!focused) {
+        if(obj.value == ""){
+          $("#search-close").addClass("hide");
+        }
+        $(".suggest-card").hide();
+    }
+}
+
+function submit_search() {
+  var query = escapeHTML(document.getElementById('search_autocomplete').value);
+  location.hash = 'q=' + query;
+  searchControl.query = query;
+  searchControl.init();
+  searchControl.trackSearchRequest(query);
+  $("#searchResults").slideDown('slow', setStickyTop);
+  return false;
+}
+
+function hideResults() {
+  $("#searchResults").slideUp('fast', setStickyTop);
+  $("#search-close").addClass("hide");
+  location.hash = '';
+
+  $("#search_autocomplete").val("").blur();
+
+  // reset the ajax search callback to nothing, so results don't appear unless ENTER
+  searchControl.reset();
+
+  return false;
+}
+
+/* ########################################################## */
+/* ################  CUSTOM SEARCH ENGINE  ################## */
+/* ########################################################## */
+var searchControl = null;
+var dacsearch = dacsearch || {};
+
+/**
+ * The custom search engine API.
+ * @constructor
+ */
+dacsearch.CustomSearchEngine = function() {
+  /**
+   * The last response from Google CSE.
+   * @private {Object}
+   */
+  this.resultQuery_ = {};
+
+  /** @private {?Element} */
+  this.searchResultEl_ = null;
+
+  /** @private {?Element} */
+  this.searchInputEl_ = null;
+
+  /** @private {string} */
+  this.query = '';
+};
+
+/**
+ * Initializes DAC's Google custom search engine.
+ * @export
+ */
+dacsearch.CustomSearchEngine.prototype.init = function() {
+  this.searchResultEl_ = $('#leftSearchControl');
+  this.searchResultEl_.empty();
+  this.searchInputEl_ = $('#search_autocomplete');
+  this.searchInputEl_.focus().val(this.query);
+  this.getResults_();
+  this.bindEvents_();
+};
+
+
+/**
+ * Binds the keyup event to the search input.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.bindEvents_ = function() {
+  this.searchInputEl_.keyup(this.debounce_(function(e) {
+    var code = e.which;
+    if (code != 13) {
+      this.query = escapeHTML(this.searchInputEl_.val());
+      location.hash = 'q=' + encodeURI(this.query);
+      this.searchResultEl_.empty();
+      this.getResults_();
+    }
+  }.bind(this), 250));
+};
+
+
+/**
+ * Resets the search control.
+ */
+dacsearch.CustomSearchEngine.prototype.reset = function() {
+  this.query = '';
+  this.searchInputEl_.off('keyup');
+  this.searchResultEl_.empty();
+  this.updateResultTitle_();
+};
+
+
+/**
+ * Updates the search query text at the top of the results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.updateResultTitle_ = function() {
+  $("#searchTitle").html("Results for <em>" + this.query + "</em>");
+};
+
+
+/**
+ * Makes the CSE api call and gets the results.
+ * @param {number=} opt_start The optional start index.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.getResults_ = function(opt_start) {
+  var lang = getLangPref();
+  // Fix zh-cn to be zh-CN.
+  lang = lang.replace(/-\w+/, function(m) { return m.toUpperCase(); });
+  var cseUrl = 'https://content.googleapis.com/customsearch/v1?';
+  var searchParams = {
+    cx: '000521750095050289010:zpcpi1ea4s8',
+    key: 'AIzaSyCFhbGnjW06dYwvRCU8h_zjdpS4PYYbEe8',
+    q: this.query,
+    start: opt_start || 1,
+    num: 6,
+    hl: lang,
+    fields: 'queries,items(pagemap,link,title,htmlSnippet,formattedUrl)'
   };
 
-  function css(el, prop) {
-    return parseInt($.css(el[0], prop)) || 0;
-  };
-  function width(el) {
-    return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
-  };
-  function height(el) {
-    return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
-  };
+  $.get(cseUrl + $.param(searchParams), function(data) {
+    this.resultQuery_ = data;
+    this.renderResults_(data);
+    this.updateResultTitle_(this.query);
+  }.bind(this));
+};
 
-})(jQuery);
+
+/**
+ * Renders the results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.renderResults_ = function(results) {
+  var el = this.searchResultEl_;
+
+  if (!results.items) {
+    el.append($('<div>').text('No results'));
+    return;
+  }
+
+  for (var i = 0; i < results.items.length; i++) {
+    var item = results.items[i];
+    var hasImage = item.pagemap && item.pagemap.cse_thumbnail;
+    var sectionMatch = item.link.match(/developer\.android\.com\/(\w*)/);
+    var section = (sectionMatch && sectionMatch[1]) || 'blog';
+
+    var entry = $('<div>').addClass('dac-custom-search-entry cols');
+
+    if (hasImage) {
+      var image = item.pagemap.cse_thumbnail[0];
+      entry.append($('<div>').addClass('col-1of6')
+        .append($('<div>').addClass('dac-custom-search-image').css(
+        'background-image', 'url(' + image.src + ')')));
+    }
+
+    var linkTitleEl = $('<a>').text(item.title).attr('href', item.link);
+    linkTitleEl.click(function(e) {
+      ga('send', 'event', 'Google Custom Search',
+          'clicked: ' + linkTitleEl.attr('href'),
+          'query: ' + $("#search_autocomplete").val().toLowerCase());
+    });
+
+    var linkUrlEl = $('<a>').addClass('dac-custom-search-link').text(
+        item.formattedUrl).attr('href', item.link);
+    linkUrlEl.click(function(e) {
+      ga('send', 'event', 'Google Custom Search',
+          'clicked: ' + linkUrlEl.attr('href'),
+          'query: ' + $("#search_autocomplete").val().toLowerCase());
+    });
+
+
+    entry.append($('<div>').addClass(hasImage ? 'col-5of6' : 'col-6of6')
+      .append($('<p>').addClass('dac-custom-search-section').text(section))
+      .append(
+        linkTitleEl.wrap('<h2>').parent().addClass('dac-custom-search-title'))
+      .append($('<p>').addClass('dac-custom-search-snippet')
+      .html(item.htmlSnippet.replace(/<br>/g, ''))).append(linkUrlEl));
+
+    el.append(entry);
+  }
+
+  if ($('#dac-custom-search-load-more')) {
+    $('#dac-custom-search-load-more').remove();
+  }
+
+  if (results.queries.nextPage) {
+    var loadMoreButton = $('<button id="dac-custom-search-load-more">')
+      .addClass('dac-custom-search-load-more')
+      .text('Load more')
+      .click(function() {
+        this.loadMoreResults_();
+      }.bind(this));
+
+    el.append(loadMoreButton);
+  }
+};
+
+
+/**
+ * Loads more results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.loadMoreResults_ = function() {
+  this.query = this.resultQuery_.queries.request[0].searchTerms;
+  var start = this.resultQuery_.queries.nextPage[0].startIndex;
+  var loadMoreButton = this.searchResultEl_.find(
+      '#dac-custom-search-load-more');
+  loadMoreButton.text('Loading more...');
+  this.getResults_(start);
+  this.trackSearchRequest(this.query + ' startIndex = ' + start);
+};
+
+
+/**
+ * Tracks a search request.
+ * @param {string} query The query for the request,
+ *                       includes start index if loading more results.
+ */
+dacsearch.CustomSearchEngine.prototype.trackSearchRequest = function(query) {
+  ga('send', 'event', 'Google Custom Search Submit', 'submit search query',
+      'query: ' + query);
+};
+
+
+/**
+ * Returns a function, that, as long as it continues to be invoked, will not
+ * be triggered. The function will be called after it stops being called for
+ * N milliseconds.
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to wait before calling the function.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.debounce_ = function(func, wait) {
+  var timeout;
+  return function() {
+    var context = this, args = arguments;
+    var later = function() {
+      timeout = null;
+      func.apply(context, args);
+    };
+   clearTimeout(timeout);
+   timeout = setTimeout(later, wait);
+  };
+};
+
+
+google.setOnLoadCallback(function(){
+  searchControl = new dacsearch.CustomSearchEngine();
+  if (location.hash.indexOf("q=") == -1) {
+    // if there's no query in the url, don't search and make sure results are hidden
+    $('#searchResults').hide();
+    return;
+  } else {
+    // first time loading search results for this page
+    searchControl.query = escapeHTML(decodeURI(location.hash.split('q=')[1]));
+    searchControl.init();
+    searchControl.trackSearchRequest(searchControl.query);
+    $('#searchResults').slideDown('slow', setStickyTop);
+    $("#search-close").removeClass("hide");
+  }
+}, true);
+
+/* Adjust the scroll position to account for sticky header, only if the hash matches an id.
+   This does not handle <a name=""> tags. Some CSS fixes those, but only for reference docs. */
+function offsetScrollForSticky() {
+  // Ignore if there's no search bar (some special pages have no header)
+  if ($("#search-container").length < 1) return;
+
+  var hash = escape(location.hash.substr(1));
+  var $matchingElement = $("#"+hash);
+  // Sanity check that there's an element with that ID on the page
+  if ($matchingElement.length) {
+    // If the position of the target element is near the top of the page (<20px, where we expect it
+    // to be because we need to move it down 60px to become in view), then move it down 60px
+    if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
+      $(window).scrollTop($(window).scrollTop() - 60);
+    }
+  }
+}
+
+// when an event on the browser history occurs (back, forward, load) requery hash and do search
+$(window).hashchange( function(){
+  // Ignore if there's no search bar (some special pages have no header)
+  if ($("#search-container").length < 1) return;
+
+  // If the hash isn't a search query or there's an error in the query,
+  // then adjust the scroll position to account for sticky header, then exit.
+  if ((location.hash.indexOf("q=") == -1) || (searchControl.query == "undefined")) {
+    // If the results pane is open, close it.
+    if (!$("#searchResults").is(":hidden")) {
+      hideResults();
+    }
+    offsetScrollForSticky();
+    return;
+  }
+
+  $('#searchResults').slideDown('slow', setStickyTop);
+  $("#search_autocomplete").focus();
+  $("#search-close").removeClass("hide");
+});
+
+/* returns the given string with all HTML brackets converted to entities
+    TODO: move this to the site's JS library */
+function escapeHTML(string) {
+  return string.replace(/</g,"&lt;")
+                .replace(/>/g,"&gt;");
+}
+
+
+
+
+
+
 
 /* ######################################################## */
 /* #################  JAVADOC REFERENCE ################### */
@@ -828,13 +2866,14 @@
 
 /* Initialize some droiddoc stuff, but only if we're in the reference */
 if (location.pathname.indexOf("/reference") == 0) {
-  if (!(location.pathname.indexOf("/reference-gms/packages.html") == 0) &&
-    !(location.pathname.indexOf("/reference-gcm/packages.html") == 0) &&
-    !(location.pathname.indexOf("/reference/com/google") == 0)) {
+  if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
+    && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
+    && !(location.pathname.indexOf("/reference/com/google") == 0)) {
     $(document).ready(function() {
       // init available apis based on user pref
       changeApiLevel();
-    });
+      initSidenavHeightResize()
+      });
   }
 }
 
@@ -842,6 +2881,45 @@
 var minLevel = 1;
 var maxLevel = 1;
 
+/******* SIDENAV DIMENSIONS ************/
+
+  function initSidenavHeightResize() {
+    // Change the drag bar size to nicely fit the scrollbar positions
+    var $dragBar = $(".ui-resizable-s");
+    $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
+
+    $( "#resize-packages-nav" ).resizable({
+      containment: "#nav-panels",
+      handles: "s",
+      alsoResize: "#packages-nav",
+      resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
+      stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie  */
+      });
+
+  }
+
+function updateSidenavFixedWidth() {
+  if (!sticky) return;
+  $('#devdoc-nav').css({
+    'width' : $('#side-nav').css('width'),
+    'margin' : $('#side-nav').css('margin')
+  });
+  $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
+
+  initSidenavHeightResize();
+}
+
+function updateSidenavFullscreenWidth() {
+  if (!sticky) return;
+  $('#devdoc-nav').css({
+    'width' : $('#side-nav').css('width'),
+    'margin' : $('#side-nav').css('margin')
+  });
+  $('#devdoc-nav .totop').css({'left': 'inherit'});
+
+  initSidenavHeightResize();
+}
+
 function buildApiLevelSelector() {
   maxLevel = SINCE_DATA.length;
   var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
@@ -855,15 +2933,15 @@
     minLevel = maxLevel;
   }
   var select = $("#apiLevelSelector").html("").change(changeApiLevel);
-  for (var i = maxLevel - 1; i >= 0; i--) {
-    var option = $("<option />").attr("value", "" + SINCE_DATA[i]).append("" + SINCE_DATA[i]);
-    //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
+  for (var i = maxLevel-1; i >= 0; i--) {
+    var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
+  //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
     select.append(option);
   }
 
   // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
-  var selectedLevelItem = $("#apiLevelSelector option[value='" + userApiLevel + "']").get(0);
-  selectedLevelItem.setAttribute('selected', true);
+  var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
+  selectedLevelItem.setAttribute('selected',true);
 }
 
 function changeApiLevel() {
@@ -876,29 +2954,30 @@
   writeCookie(API_LEVEL_COOKIE, selectedLevel, null);
 
   if (selectedLevel < minLevel) {
-    $("#naMessage").show().html("<div><p><strong>This API" +
-              " requires API level " + minLevel + " or higher.</strong></p>" +
-              "<p>This document is hidden because your selected API level for the documentation is " +
-              selectedLevel + ". You can change the documentation API level with the selector " +
-              "above the left navigation.</p>" +
-              "<p>For more information about specifying the API level your app requires, " +
-              "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'" +
-              ">Supporting Different Platform Versions</a>.</p>" +
-              "<input type='button' value='OK, make this page visible' " +
-              "title='Change the API level to " + minLevel + "' " +
-              "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />" +
-              "</div>");
+    var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
+    $("#naMessage").show().html("<div><p><strong>This " + thing
+              + " requires API level " + minLevel + " or higher.</strong></p>"
+              + "<p>This document is hidden because your selected API level for the documentation is "
+              + selectedLevel + ". You can change the documentation API level with the selector "
+              + "above the left navigation.</p>"
+              + "<p>For more information about specifying the API level your app requires, "
+              + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
+              + ">Supporting Different Platform Versions</a>.</p>"
+              + "<input type='button' value='OK, make this page visible' "
+              + "title='Change the API level to " + minLevel + "' "
+              + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
+              + "</div>");
   } else {
     $("#naMessage").hide();
   }
 }
 
 function toggleVisisbleApis(selectedLevel, context) {
-  var apis = $(".api", context);
+  var apis = $(".api",context);
   apis.each(function(i) {
     var obj = $(this);
     var className = obj.attr("class");
-    var apiLevelIndex = className.lastIndexOf("-") + 1;
+    var apiLevelIndex = className.lastIndexOf("-")+1;
     var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
     apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
     var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
@@ -911,21 +2990,236 @@
     var selectedLevelNum = parseInt(selectedLevel)
     var apiLevelNum = parseInt(apiLevel);
     if (isNaN(apiLevelNum)) {
-      apiLevelNum = maxLevel;
+        apiLevelNum = maxLevel;
     }
 
     // Grey things out that aren't available and give a tooltip title
     if (apiLevelNum > selectedLevelNum) {
-      obj.addClass("absent").attr("title", "Requires API Level \"" +
-            apiLevel + "\" or higher. To reveal, change the target API level " +
-              "above the left navigation.");
-    } else obj.removeClass("absent").removeAttr("title");
+      obj.addClass("absent").attr("title","Requires API Level \""
+            + apiLevel + "\" or higher. To reveal, change the target API level "
+              + "above the left navigation.");
+    }
+    else obj.removeClass("absent").removeAttr("title");
   });
 }
 
+
+
+
 /* #################  SIDENAV TREE VIEW ################### */
+
+function new_node(me, mom, text, link, children_data, api_level)
+{
+  var node = new Object();
+  node.children = Array();
+  node.children_data = children_data;
+  node.depth = mom.depth + 1;
+
+  node.li = document.createElement("li");
+  mom.get_children_ul().appendChild(node.li);
+
+  node.label_div = document.createElement("div");
+  node.label_div.className = "label";
+  if (api_level != null) {
+    $(node.label_div).addClass("api");
+    $(node.label_div).addClass("api-level-"+api_level);
+  }
+  node.li.appendChild(node.label_div);
+
+  if (children_data != null) {
+    node.expand_toggle = document.createElement("a");
+    node.expand_toggle.href = "javascript:void(0)";
+    node.expand_toggle.onclick = function() {
+          if (node.expanded) {
+            $(node.get_children_ul()).slideUp("fast");
+            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+            node.expanded = false;
+          } else {
+            expand_node(me, node);
+          }
+       };
+    node.label_div.appendChild(node.expand_toggle);
+
+    node.plus_img = document.createElement("img");
+    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+    node.plus_img.className = "plus";
+    node.plus_img.width = "8";
+    node.plus_img.border = "0";
+    node.expand_toggle.appendChild(node.plus_img);
+
+    node.expanded = false;
+  }
+
+  var a = document.createElement("a");
+  node.label_div.appendChild(a);
+  node.label = document.createTextNode(text);
+  a.appendChild(node.label);
+  if (link) {
+    a.href = me.toroot + link;
+  } else {
+    if (children_data != null) {
+      a.className = "nolink";
+      a.href = "javascript:void(0)";
+      a.onclick = node.expand_toggle.onclick;
+      // This next line shouldn't be necessary.  I'll buy a beer for the first
+      // person who figures out how to remove this line and have the link
+      // toggle shut on the first try. --joeo@android.com
+      node.expanded = false;
+    }
+  }
+
+
+  node.children_ul = null;
+  node.get_children_ul = function() {
+      if (!node.children_ul) {
+        node.children_ul = document.createElement("ul");
+        node.children_ul.className = "children_ul";
+        node.children_ul.style.display = "none";
+        node.li.appendChild(node.children_ul);
+      }
+      return node.children_ul;
+    };
+
+  return node;
+}
+
+
+
+
+function expand_node(me, node)
+{
+  if (node.children_data && !node.expanded) {
+    if (node.children_visited) {
+      $(node.get_children_ul()).slideDown("fast");
+    } else {
+      get_node(me, node);
+      if ($(node.label_div).hasClass("absent")) {
+        $(node.get_children_ul()).addClass("absent");
+      }
+      $(node.get_children_ul()).slideDown("fast");
+    }
+    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
+    node.expanded = true;
+
+    // perform api level toggling because new nodes are new to the DOM
+    var selectedLevel = $("#apiLevelSelector option:selected").val();
+    toggleVisisbleApis(selectedLevel, "#side-nav");
+  }
+}
+
+function get_node(me, mom)
+{
+  mom.children_visited = true;
+  for (var i in mom.children_data) {
+    var node_data = mom.children_data[i];
+    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
+        node_data[2], node_data[3]);
+  }
+}
+
+function this_page_relative(toroot)
+{
+  var full = document.location.pathname;
+  var file = "";
+  if (toroot.substr(0, 1) == "/") {
+    if (full.substr(0, toroot.length) == toroot) {
+      return full.substr(toroot.length);
+    } else {
+      // the file isn't under toroot.  Fail.
+      return null;
+    }
+  } else {
+    if (toroot != "./") {
+      toroot = "./" + toroot;
+    }
+    do {
+      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
+        var pos = full.lastIndexOf("/");
+        file = full.substr(pos) + file;
+        full = full.substr(0, pos);
+        toroot = toroot.substr(0, toroot.length-3);
+      }
+    } while (toroot != "" && toroot != "/");
+    return file.substr(1);
+  }
+}
+
+function find_page(url, data)
+{
+  var nodes = data;
+  var result = null;
+  for (var i in nodes) {
+    var d = nodes[i];
+    if (d[1] == url) {
+      return new Array(i);
+    }
+    else if (d[2] != null) {
+      result = find_page(url, d[2]);
+      if (result != null) {
+        return (new Array(i).concat(result));
+      }
+    }
+  }
+  return null;
+}
+
+function init_default_navtree(toroot) {
+  // load json file for navtree data
+  $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
+      // when the file is loaded, initialize the tree
+      if(jqxhr.status === 200) {
+          init_navtree("tree-list", toroot, NAVTREE_DATA);
+      }
+  });
+
+  // perform api level toggling because because the whole tree is new to the DOM
+  var selectedLevel = $("#apiLevelSelector option:selected").val();
+  toggleVisisbleApis(selectedLevel, "#side-nav");
+}
+
+function init_navtree(navtree_id, toroot, root_nodes)
+{
+  var me = new Object();
+  me.toroot = toroot;
+  me.node = new Object();
+
+  me.node.li = document.getElementById(navtree_id);
+  me.node.children_data = root_nodes;
+  me.node.children = new Array();
+  me.node.children_ul = document.createElement("ul");
+  me.node.get_children_ul = function() { return me.node.children_ul; };
+  //me.node.children_ul.className = "children_ul";
+  me.node.li.appendChild(me.node.children_ul);
+  me.node.depth = 0;
+
+  get_node(me, me.node);
+
+  me.this_page = this_page_relative(toroot);
+  me.breadcrumbs = find_page(me.this_page, root_nodes);
+  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
+    var mom = me.node;
+    for (var i in me.breadcrumbs) {
+      var j = me.breadcrumbs[i];
+      mom = mom.children[j];
+      expand_node(me, mom);
+    }
+    mom.label_div.className = mom.label_div.className + " selected";
+    addLoadEvent(function() {
+      scrollIntoView("nav-tree");
+      });
+  }
+}
+
+
+
+
+
+
+
+
 /* TODO: eliminate redundancy with non-google functions */
-function init_google_navtree(navtree_id, toroot, root_nodes) {
+function init_google_navtree(navtree_id, toroot, root_nodes)
+{
   var me = new Object();
   me.toroot = toroot;
   me.node = new Object();
@@ -946,7 +3240,8 @@
   get_google_node(me, me.node);
 }
 
-function new_google_node(me, mom, text, link, children_data, api_level) {
+function new_google_node(me, mom, text, link, children_data, api_level)
+{
   var node = new Object();
   var child;
   node.children = Array();
@@ -964,25 +3259,28 @@
 
   mom.get_children_ul().appendChild(node.li);
 
-  if (link) {
+
+  if(link) {
     child = document.createElement("a");
 
-  } else {
+  }
+  else {
     child = document.createElement("span");
     child.className = "tree-list-subtitle";
 
   }
   if (children_data != null) {
-    node.li.className = "nav-section";
+    node.li.className="nav-section";
     node.label_div = document.createElement("div");
     node.label_div.className = "nav-section-header-ref";
     node.li.appendChild(node.label_div);
     get_google_node(me, node);
     node.label_div.appendChild(child);
-  } else {
+  }
+  else {
     node.li.appendChild(child);
   }
-  if (link) {
+  if(link) {
     child.href = me.toroot + link;
   }
   node.label = document.createTextNode(text);
@@ -993,21 +3291,27 @@
   return node;
 }
 
-function get_google_node(me, mom) {
+function get_google_node(me, mom)
+{
   mom.children_visited = true;
   var linkText;
   for (var i in mom.children_data) {
     var node_data = mom.children_data[i];
     linkText = node_data[0];
 
-    if (linkText.match("^" + "com.google.android") == "com.google.android") {
+    if(linkText.match("^"+"com.google.android")=="com.google.android"){
       linkText = linkText.substr(19, linkText.length);
     }
-    mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
-        node_data[2], node_data[3]);
+      mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
+          node_data[2], node_data[3]);
   }
 }
 
+
+
+
+
+
 /****** NEW version of script to build google and sample navs dynamically ******/
 // TODO: update Google reference docs to tolerate this new implementation
 
@@ -1017,8 +3321,9 @@
 var NODE_TAGS = 3;
 var NODE_CHILDREN = 4;
 
-function init_google_navtree2(navtree_id, data) {
-  var $containerUl = $("#" + navtree_id);
+function init_google_navtree2(navtree_id, data)
+{
+  var $containerUl = $("#"+navtree_id);
   for (var i in data) {
     var node_data = data[i];
     $containerUl.append(new_google_node2(node_data));
@@ -1027,22 +3332,23 @@
   // Make all third-generation list items 'sticky' to prevent them from collapsing
   $containerUl.find('li li li.nav-section').addClass('sticky');
 
-  initExpandableNavItems("#" + navtree_id);
+  initExpandableNavItems("#"+navtree_id);
 }
 
-function new_google_node2(node_data) {
+function new_google_node2(node_data)
+{
   var linkText = node_data[NODE_NAME];
-  if (linkText.match("^" + "com.google.android") == "com.google.android") {
+  if(linkText.match("^"+"com.google.android")=="com.google.android"){
     linkText = linkText.substr(19, linkText.length);
   }
   var $li = $('<li>');
   var $a;
   if (node_data[NODE_HREF] != null) {
-    $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >' +
-        linkText + '</a>');
+    $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
+        + linkText + '</a>');
   } else {
-    $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >' +
-        linkText + '/</a>');
+    $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
+        + linkText + '/</a>');
   }
   var $childUl = $('<ul>');
   if (node_data[NODE_CHILDREN] != null) {
@@ -1061,6 +3367,16 @@
   return $li;
 }
 
+
+
+
+
+
+
+
+
+
+
 function showGoogleRefTree() {
   init_default_google_navtree(toRoot);
   init_default_gcm_navtree(toRoot);
@@ -1069,22 +3385,49 @@
 function init_default_google_navtree(toroot) {
   // load json file for navtree data
   $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
-    // when the file is loaded, initialize the tree
-    if (jqxhr.status === 200) {
-      init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
-      highlightSidenav();
-    }
+      // when the file is loaded, initialize the tree
+      if(jqxhr.status === 200) {
+          init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
+          highlightSidenav();
+          resizeNav();
+      }
   });
 }
 
 function init_default_gcm_navtree(toroot) {
   // load json file for navtree data
   $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
-    // when the file is loaded, initialize the tree
-    if (jqxhr.status === 200) {
-      init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
-      highlightSidenav();
-    }
+      // when the file is loaded, initialize the tree
+      if(jqxhr.status === 200) {
+          init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
+          highlightSidenav();
+          resizeNav();
+      }
+  });
+}
+
+function showSamplesRefTree() {
+  init_default_samples_navtree(toRoot);
+}
+
+function init_default_samples_navtree(toroot) {
+  // load json file for navtree data
+  $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
+      // when the file is loaded, initialize the tree
+      if(jqxhr.status === 200) {
+          // hack to remove the "about the samples" link then put it back in
+          // after we nuke the list to remove the dummy static list of samples
+          var $firstLi = $("#nav.samples-nav > li:first-child").clone();
+          $("#nav.samples-nav").empty();
+          $("#nav.samples-nav").append($firstLi);
+
+          init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
+          highlightSidenav();
+          resizeNav();
+          if ($("#jd-content #samples").length) {
+            showSamples();
+          }
+      }
   });
 }
 
@@ -1096,25 +3439,25 @@
  *                'null' to simply toggle.
  */
 function toggleInherited(linkObj, expand) {
-  var base = linkObj.getAttribute("id");
-  var list = document.getElementById(base + "-list");
-  var summary = document.getElementById(base + "-summary");
-  var trigger = document.getElementById(base + "-trigger");
-  var a = $(linkObj);
-  if ((expand == null && a.hasClass("closed")) || expand) {
-    list.style.display = "none";
-    summary.style.display = "block";
-    trigger.src = toRoot + "assets/images/styles/disclosure_up.png";
-    a.removeClass("closed");
-    a.addClass("opened");
-  } else if ((expand == null && a.hasClass("opened")) || (expand == false)) {
-    list.style.display = "block";
-    summary.style.display = "none";
-    trigger.src = toRoot + "assets/images/styles/disclosure_down.png";
-    a.removeClass("opened");
-    a.addClass("closed");
-  }
-  return false;
+    var base = linkObj.getAttribute("id");
+    var list = document.getElementById(base + "-list");
+    var summary = document.getElementById(base + "-summary");
+    var trigger = document.getElementById(base + "-trigger");
+    var a = $(linkObj);
+    if ( (expand == null && a.hasClass("closed")) || expand ) {
+        list.style.display = "none";
+        summary.style.display = "block";
+        trigger.src = toRoot + "assets/images/styles/disclosure_up.png";
+        a.removeClass("closed");
+        a.addClass("opened");
+    } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
+        list.style.display = "block";
+        summary.style.display = "none";
+        trigger.src = toRoot + "assets/images/styles/disclosure_down.png";
+        a.removeClass("opened");
+        a.addClass("closed");
+    }
+    return false;
 }
 
 /* Toggle all inherited classes in a single table (e.g. all inherited methods)
@@ -1126,12 +3469,12 @@
   var a = $(linkObj);
   var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
   var expandos = $(".jd-expando-trigger", table);
-  if ((expand == null && a.text() == "[Expand]") || expand) {
+  if ( (expand == null && a.text() == "[Expand]") || expand ) {
     expandos.each(function(i) {
       toggleInherited(this, true);
     });
     a.text("[Collapse]");
-  } else if ((expand == null && a.text() == "[Collapse]") || (expand == false)) {
+  } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
     expandos.each(function(i) {
       toggleInherited(this, false);
     });
@@ -1168,19 +3511,25 @@
   $("#toggleAllClassInherited").text("[Collapse All]");
 }
 
+
 /* HANDLE KEY EVENTS
  * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
  */
 var agent = navigator['userAgent'].toLowerCase();
 var mac = agent.indexOf("macintosh") != -1;
 
-$(document).keydown(function(e) {
-  var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
+$(document).keydown( function(e) {
+var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
   if (control && e.which == 70) {  // 70 is "F"
     ensureAllInheritedExpanded();
   }
 });
 
+
+
+
+
+
 /* On-demand functions */
 
 /** Move sample code line numbers out of PRE block and into non-copyable column */
@@ -1204,39 +3553,40 @@
     // highlight the line when hovering on the number
     $("#codesample-line-numbers a.number").mouseover(function() {
       var id = $(this).attr('href');
-      $(id).css('background', '#e7e7e7');
+      $(id).css('background','#e7e7e7');
     });
     $("#codesample-line-numbers a.number").mouseout(function() {
       var id = $(this).attr('href');
-      $(id).css('background', 'none');
+      $(id).css('background','none');
     });
   });
 }
 
 // create SHIFT key binder to avoid the selectText method when selecting multiple lines
 var shifted = false;
-$(document).bind('keyup keydown', function(e) {
-  shifted = e.shiftKey; return true;
-});
+$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
 
 // courtesy of jasonedelman.com
 function selectText(element) {
-  var doc = document      ,
-        range, selection
-  ;
-  if (doc.body.createTextRange) { //ms
-    range = doc.body.createTextRange();
-    range.moveToElementText(element);
-    range.select();
-  } else if (window.getSelection) { //all others
-    selection = window.getSelection();
-    range = doc.createRange();
-    range.selectNodeContents(element);
-    selection.removeAllRanges();
-    selection.addRange(range);
-  }
+    var doc = document
+        , range, selection
+    ;
+    if (doc.body.createTextRange) { //ms
+        range = doc.body.createTextRange();
+        range.moveToElementText(element);
+        range.select();
+    } else if (window.getSelection) { //all others
+        selection = window.getSelection();
+        range = doc.createRange();
+        range.selectNodeContents(element);
+        selection.removeAllRanges();
+        selection.addRange(range);
+    }
 }
 
+
+
+
 /** Display links and other information about samples that match the
     group specified by the URL */
 function showSamples() {
@@ -1247,14 +3597,16 @@
   $selectedLi = $("#nav li.selected");
 
   $selectedLi.children("ul").children("li").each(function() {
-    var $li = $("<li>").append($(this).find("a").first().clone());
-    $ul.append($li);
+      var $li = $("<li>").append($(this).find("a").first().clone());
+      $ul.append($li);
   });
 
   $("#samples").append($ul);
 
 }
 
+
+
 /* ########################################################## */
 /* ###################  RESOURCE CARDS  ##################### */
 /* ########################################################## */
@@ -1263,75 +3615,78 @@
     jd_tag_helpers.js and the *_unified_data.js to be loaded. */
 
 (function() {
+  // Prevent the same resource from being loaded more than once per page.
+  var addedPageResources = {};
+
   $(document).ready(function() {
     // Need to initialize hero carousel before other sections for dedupe
     // to work correctly.
     $('[data-carousel-query]').dacCarouselQuery();
 
-    // Iterate over all instances and initialize a resource widget.
-    $('.resource-widget').resourceWidget();
+    $('.resource-widget').each(function() {
+      initResourceWidget(this);
+    });
+
+    /* Pass the line height to ellipsisfade() to adjust the height of the
+    text container to show the max number of lines possible, without
+    showing lines that are cut off. This works with the css ellipsis
+    classes to fade last text line and apply an ellipsis char. */
+
+    //card text currently uses 20px line height.
+    var lineHeight = 20;
+    $('.card-info .text').ellipsisfade(lineHeight);
   });
 
-  $.fn.widgetOptions = function() {
-    return {
-      cardSizes: (this.data('cardsizes') || '').split(','),
-      maxResults: parseInt(this.data('maxresults'), 10) || Infinity,
-      initialResults: this.data('initialResults'),
-      itemsPerPage: this.data('itemsPerPage'),
-      sortOrder: this.data('sortorder'),
-      query: this.data('query'),
-      section: this.data('section'),
-      /* Added by LFL 6/6/14 */
-      resourceStyle: this.data('resourcestyle') || 'card',
-      stackSort: this.data('stacksort') || 'true',
-      // For filter based resources
-      allowDuplicates: this.data('allow-duplicates') || 'false'
-    };
-  };
-
-  $.fn.deprecateOldGridStyles = function() {
-    var m = this.get(0).className.match(/\bcol-(\d+)\b/);
-    if (m && !this.is('.cols > *')) {
-      this.removeClass('col-' + m[1]);
-    }
-    return this;
-  }
-
   /*
-   * Three types of resource layouts:
-   * Flow - Uses a fixed row-height flow using float left style.
-   * Carousel - Single card slideshow all same dimension absolute.
-   * Stack - Uses fixed columns and flexible element height.
-   */
-  function initResourceWidget(widget, resources, opts) {
-    var $widget = $(widget).deprecateOldGridStyles();
-    var isFlow = $widget.hasClass('resource-flow-layout');
-    var isCarousel = $widget.hasClass('resource-carousel-layout');
-    var isStack = $widget.hasClass('resource-stack-layout');
+    Three types of resource layouts:
+    Flow - Uses a fixed row-height flow using float left style.
+    Carousel - Single card slideshow all same dimension absolute.
+    Stack - Uses fixed columns and flexible element height.
+  */
+  function initResourceWidget(widget) {
+    var $widget = $(widget);
+    var isFlow = $widget.hasClass('resource-flow-layout'),
+        isCarousel = $widget.hasClass('resource-carousel-layout'),
+        isStack = $widget.hasClass('resource-stack-layout');
 
-    opts = opts || $widget.widgetOptions();
-    resources = resources || metadata.query(opts);
-
-    if (opts.maxResults !== undefined) {
-      resources = resources.slice(0, opts.maxResults);
+    // remove illegal col-x class which is not relevant anymore thanks to responsive styles.
+    var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
+    if (m && !$widget.is('.cols > *')) {
+      $widget.removeClass('col-' + m[1]);
     }
 
+    var opts = {
+      cardSizes: ($widget.data('cardsizes') || '').split(','),
+      maxResults: parseInt($widget.data('maxresults') || '100', 10),
+      initialResults: $widget.data('initialResults'),
+      itemsPerPage: $widget.data('itemsperpage'),
+      sortOrder: $widget.data('sortorder'),
+      query: $widget.data('query'),
+      section: $widget.data('section'),
+      /* Added by LFL 6/6/14 */
+      resourceStyle: $widget.data('resourcestyle') || 'card',
+      stackSort: $widget.data('stacksort') || 'true'
+    };
+
+    // run the search for the set of resources to show
+
+    var resources = buildResourceList(opts);
+
     if (isFlow) {
       drawResourcesFlowWidget($widget, opts, resources);
     } else if (isCarousel) {
       drawResourcesCarouselWidget($widget, opts, resources);
     } else if (isStack) {
-      opts.numStacks = $widget.data('numstacks');
-      drawResourcesStackWidget($widget, opts, resources);
+      /* Looks like this got removed and is not used, so repurposing for the
+          homepage style layout.
+          Modified by LFL 6/6/14
+      */
+      //var sections = buildSectionList(opts);
+      opts['numStacks'] = $widget.data('numstacks');
+      drawResourcesStackWidget($widget, opts, resources/*, sections*/);
     }
   }
 
-  $.fn.resourceWidget = function(resources, options) {
-    return this.each(function() {
-      initResourceWidget(this, resources, options);
-    });
-  };
-
   /* Initializes a Resource Carousel Widget */
   function drawResourcesCarouselWidget($widget, opts, resources) {
     $widget.empty();
@@ -1341,15 +3696,15 @@
       .append($('<a>').addClass('slideshow-prev').text('Prev'))
       .append($('<a>').addClass('slideshow-next').text('Next'));
 
-    var css = {'width': $widget.width() + 'px',
-                'height': $widget.height() + 'px'};
+    var css = { 'width': $widget.width() + 'px',
+                'height': $widget.height() + 'px' };
 
     var $ul = $('<ul>');
 
     for (var i = 0; i < resources.length; ++i) {
       var $card = $('<a>')
         .attr('href', cleanUrl(resources[i].url))
-        .decorateResourceCard(resources[i], plusone);
+        .decorateResourceCard(resources[i],plusone);
 
       $('<li>').css(css)
           .append($card)
@@ -1365,7 +3720,7 @@
       btnPrev: '.slideshow-prev',
       btnNext: '.slideshow-next'
     });
-  }
+  };
 
   /* Initializes a Resource Card Stack Widget (column-based layout)
      Modified by LFL 6/6/14
@@ -1377,6 +3732,7 @@
     var cards = $widget.find('.resource-card').detach().toArray();
     var numStacks = opts.numStacks || 1;
     var $stacks = [];
+    var urlString;
 
     for (var i = 0; i < numStacks; ++i) {
       $stacks[i] = $('<div>').addClass('resource-card-stack')
@@ -1387,21 +3743,21 @@
 
     // Extract any subsections that are actually resource cards
     if (sections) {
-      for (i = 0; i < sections.length; ++i) {
+      for (var i = 0; i < sections.length; ++i) {
         if (!sections[i].sections || !sections[i].sections.length) {
           // Render it as a resource card
           sectionResources.push(
             $('<a>')
               .addClass('resource-card section-card')
               .attr('href', cleanUrl(sections[i].resource.url))
-              .decorateResourceCard(sections[i].resource, plusone)[0]
+              .decorateResourceCard(sections[i].resource,plusone)[0]
           );
 
         } else {
           cards.push(
             $('<div>')
               .addClass('resource-card section-card-menu')
-              .decorateResourceSection(sections[i], plusone)[0]
+              .decorateResourceSection(sections[i],plusone)[0]
           );
         }
       }
@@ -1409,7 +3765,7 @@
 
     cards = cards.concat(sectionResources);
 
-    for (i = 0; i < resources.length; ++i) {
+    for (var i = 0; i < resources.length; ++i) {
       var $card = createResourceElement(resources[i], opts);
 
       if (opts.resourceStyle.indexOf('related') > -1) {
@@ -1419,8 +3775,8 @@
       cards.push($card[0]);
     }
 
-    if (opts.stackSort !== 'false') {
-      for (i = 0; i < cards.length; ++i) {
+    if (opts.stackSort != 'false') {
+      for (var i = 0; i < cards.length; ++i) {
         // Find the stack with the shortest height, but give preference to
         // left to right order.
         var minHeight = $stacks[0].height();
@@ -1437,7 +3793,8 @@
         $stacks[minIndex].append($(cards[i]));
       }
     }
-  }
+
+  };
 
   /*
     Create a resource card using the given resource object and a list of html
@@ -1450,7 +3807,7 @@
     // so its a div instead of an a tag, also the generic one is not given
     // the resource-card class so it appears with a transparent background
     // and can be styled in whatever way the css setup.
-    if (opts.resourceStyle === 'generic') {
+    if (opts.resourceStyle == 'generic') {
       $el = $('<div>')
         .addClass('resource')
         .attr('href', cleanUrl(resource.url))
@@ -1476,54 +3833,39 @@
       column.addClass('col-tablet-1of1');
     }
     if (cardWidth < 18) {
-      column.addClass('col-mobile-1of1');
+      column.addClass('col-mobile-1of1')
     }
     return column;
   }
 
   /* Initializes a flow widget, see distribute.scss for generating accompanying css */
   function drawResourcesFlowWidget($widget, opts, resources) {
-    // We'll be doing our own modifications to opts.
-    opts = $.extend({}, opts);
-
     $widget.empty().addClass('cols');
-    if (opts.itemsPerPage) {
-      $('<div class="col-1of1 dac-section-links dac-text-center">')
-        .append(
-          $('<div class="dac-section-link dac-show-less" data-toggle="show-less">Less<i class="dac-sprite dac-auto-unfold-less"></i></div>'),
-          $('<div class="dac-section-link dac-show-more" data-toggle="show-more">More<i class="dac-sprite dac-auto-unfold-more"></i></div>')
-        )
-        .appendTo($widget);
-    }
-
-    $widget.data('options.resourceflow', opts);
-    $widget.data('resources.resourceflow', resources);
-
-    drawResourceFlowPage($widget, opts, resources);
-  }
-
-  function drawResourceFlowPage($widget, opts, resources) {
-    var cardSizes = opts.cardSizes || ['6x6']; // 2015-08-09: dynamic card sizes are deprecated
-    var i = opts.currentIndex || 0;
-    var j = 0;
+    var cardSizes = opts.cardSizes || ['6x6'];
+    var initialResults = opts.initialResults || resources.length;
+    var i = 0, j = 0;
     var plusone = false; // stop showing plusone buttons on cards
-    var firstPage = i === 0;
-    var initialResults = opts.initialResults || opts.itemsPerPage || resources.length;
-    var max = firstPage ? initialResults : i + opts.itemsPerPage;
-    max = Math.min(resources.length, max);
+    var cardParent = $widget;
 
-    var page = $('<div class="resource-flow-page">');
-    if (opts.itemsPerPage) {
-      $widget.find('.dac-section-links').before(page);
-    } else {
-      $widget.append(page);
-    }
+    while (i < resources.length) {
 
-    while (i < max) {
+      if (i === initialResults && initialResults < resources.length) {
+        // Toggle remaining cards
+        cardParent = $('<div class="dac-toggle-content clearfix">').appendTo($widget);
+        $widget.addClass('dac-toggle');
+        $('<div class="col-1of1 dac-section-links dac-text-center">')
+          .append(
+            $('<div class="dac-section-link" data-toggle="section">')
+              .append('<span class="dac-toggle-expand">More<i class="dac-sprite dac-auto-unfold-more"></i></span>')
+              .append('<span class="dac-toggle-collapse">Less<i class="dac-sprite dac-auto-unfold-less"></i></span>')
+          )
+          .appendTo($widget)
+      }
+
       var cardSize = cardSizes[j++ % cardSizes.length];
-      cardSize = cardSize.replace(/^\s+|\s+$/, '');
+      cardSize = cardSize.replace(/^\s+|\s+$/,'');
 
-      var column = createResponsiveFlowColumn(cardSize).appendTo(page);
+      var column = createResponsiveFlowColumn(cardSize).appendTo(cardParent);
 
       // A stack has a third dimension which is the number of stacked items
       var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
@@ -1533,8 +3875,8 @@
       if (isStack) {
         // Create a stack container which should have the dimensions defined
         // by the product of the items inside.
-        $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1] +
-          'x' + isStack[2] * isStack[3]) .appendTo(column);
+        $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
+            + 'x' + isStack[2] * isStack[3]) .appendTo(column);
       }
 
       // Build each stack item or just a single item
@@ -1544,11 +3886,11 @@
         var $card = createResourceElement(resources[i], opts, plusone);
 
         $card.addClass('resource-card-' + cardSize +
-          ' resource-card-' + resource.type.toLowerCase());
+          ' resource-card-' + resource.type);
 
         if (isStack) {
           $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
-          if (++stackCount === parseInt(isStack[3])) {
+          if (++stackCount == parseInt(isStack[3])) {
             $card.addClass('resource-card-row-stack-last');
             stackCount = 0;
           }
@@ -1558,47 +3900,8 @@
 
         $card.appendTo($stackDiv || column);
 
-      } while (++i < max && stackCount > 0);
-
-      // Record number of pages viewed in analytics.
-      if (!firstPage) {
-        var clicks = Math.ceil((i - initialResults) / opts.itemsPerPage);
-        ga('send', 'event', 'Cards', 'Click More', clicks);
-      }
+      } while (++i < resources.length && stackCount > 0);
     }
-
-    opts.currentIndex = i;
-    $widget.toggleClass('dac-has-more', i < resources.length);
-    $widget.toggleClass('dac-has-less', !firstPage);
-
-    $widget.trigger('dac:domchange');
-    if (opts.onRenderPage) {
-      opts.onRenderPage(page);
-    }
-  }
-
-  function drawResourceFlowReset($widget, opts, resources) {
-    $widget.find('.resource-flow-page')
-        .slice(1)
-        .remove();
-    $widget.toggleClass('dac-has-more', true);
-    $widget.toggleClass('dac-has-less', false);
-
-    opts.currentIndex = Math.min(opts.initialResults, resources.length);
-
-    ga('send', 'event', 'Cards', 'Click Less');
-  }
-
-  /* A decorator for event functions which finds the surrounding widget and it's options */
-  function wrapWithWidget(func) {
-    return function(e) {
-      if (e) e.preventDefault();
-
-      var $widget = $(this).closest('.resource-flow-layout');
-      var opts = $widget.data('options.resourceflow');
-      var resources = $widget.data('resources.resourceflow');
-      func($widget, opts, resources);
-    };
   }
 
   /* Build a site map of resources using a section as a root. */
@@ -1609,7 +3912,139 @@
     return [];
   }
 
-  function cleanUrl(url) {
+  function buildResourceList(opts) {
+    return $.queryResources(opts);
+  }
+
+  $.queryResources = function(opts) {
+    var maxResults = opts.maxResults || 100;
+
+    var query = opts.query || '';
+    var expressions = parseResourceQuery(query);
+    var addedResourceIndices = {};
+    var results = [];
+
+    for (var i = 0; i < expressions.length; i++) {
+      var clauses = expressions[i];
+
+      // build initial set of resources from first clause
+      var firstClause = clauses[0];
+      var resources = [];
+      switch (firstClause.attr) {
+        case 'type':
+          resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
+          break;
+        case 'lang':
+          resources = ALL_RESOURCES_BY_LANG[firstClause.value];
+          break;
+        case 'tag':
+          resources = ALL_RESOURCES_BY_TAG[firstClause.value];
+          break;
+        case 'collection':
+          var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
+          resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
+          break;
+        case 'section':
+          var urls = SITE_MAP[firstClause.value].sections || [];
+          resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
+          break;
+      }
+      // console.log(firstClause.attr + ':' + firstClause.value);
+      resources = resources || [];
+
+      // use additional clauses to filter corpus
+      if (clauses.length > 1) {
+        var otherClauses = clauses.slice(1);
+        resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
+      }
+
+      // filter out resources already added
+      if (i > 1) {
+        resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
+      }
+
+      // add to list of already added indices
+      for (var j = 0; j < resources.length; j++) {
+        if (resources[j]) {
+          addedResourceIndices[resources[j].index] = 1;
+        }
+      }
+
+      // concat to final results list
+      results = results.concat(resources);
+    }
+
+    if (opts.sortOrder && results.length) {
+      var attr = opts.sortOrder;
+
+      if (opts.sortOrder == 'random') {
+        var i = results.length, j, temp;
+        while (--i) {
+          j = Math.floor(Math.random() * (i + 1));
+          temp = results[i];
+          results[i] = results[j];
+          results[j] = temp;
+        }
+      } else {
+        var desc = attr.charAt(0) == '-';
+        if (desc) {
+          attr = attr.substring(1);
+        }
+        results = results.sort(function(x,y) {
+          return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
+        });
+      }
+    }
+
+    results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
+    results = results.slice(0, maxResults);
+
+    for (var j = 0; j < results.length; ++j) {
+      addedPageResources[results[j].index] = 1;
+    }
+
+    return results;
+  }
+
+
+  function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
+    return function(resource) {
+      return resource && !addedResourceIndices[resource.index];
+    };
+  }
+
+
+  function getResourceMatchesClausesFilter(clauses) {
+    return function(resource) {
+      return doesResourceMatchClauses(resource, clauses);
+    };
+  }
+
+
+  function doesResourceMatchClauses(resource, clauses) {
+    for (var i = 0; i < clauses.length; i++) {
+      var map;
+      switch (clauses[i].attr) {
+        case 'type':
+          map = IS_RESOURCE_OF_TYPE[clauses[i].value];
+          break;
+        case 'lang':
+          map = IS_RESOURCE_IN_LANG[clauses[i].value];
+          break;
+        case 'tag':
+          map = IS_RESOURCE_TAGGED[clauses[i].value];
+          break;
+      }
+
+      if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
+        return clauses[i].negative;
+      }
+    }
+    return true;
+  }
+
+  function cleanUrl(url)
+  {
     if (url && url.indexOf('//') === -1) {
       url = toRoot + url;
     }
@@ -1617,16 +4052,53 @@
     return url;
   }
 
-  // Delegated events for resources.
-  $(document).on('click', '.resource-flow-layout [data-toggle="show-more"]', wrapWithWidget(drawResourceFlowPage));
-  $(document).on('click', '.resource-flow-layout [data-toggle="show-less"]', wrapWithWidget(drawResourceFlowReset));
+
+  function parseResourceQuery(query) {
+    // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
+    var expressions = [];
+    var expressionStrs = query.split(',') || [];
+    for (var i = 0; i < expressionStrs.length; i++) {
+      var expr = expressionStrs[i] || '';
+
+      // Break expression into clauses (clause e.g. 'tag:foo')
+      var clauses = [];
+      var clauseStrs = expr.split(/(?=[\+\-])/);
+      for (var j = 0; j < clauseStrs.length; j++) {
+        var clauseStr = clauseStrs[j] || '';
+
+        // Get attribute and value from clause (e.g. attribute='tag', value='foo')
+        var parts = clauseStr.split(':');
+        var clause = {};
+
+        clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
+        if (clause.attr) {
+          if (clause.attr.charAt(0) == '+') {
+            clause.attr = clause.attr.substring(1);
+          } else if (clause.attr.charAt(0) == '-') {
+            clause.negative = true;
+            clause.attr = clause.attr.substring(1);
+          }
+        }
+
+        if (parts.length > 1) {
+          clause.value = parts[1].replace(/^\s+|\s+$/g,'');
+        }
+
+        clauses.push(clause);
+      }
+
+      if (!clauses.length) {
+        continue;
+      }
+
+      expressions.push(clauses);
+    }
+
+    return expressions;
+  }
 })();
 
 (function($) {
-  // A mapping from category and type values to new values or human presentable strings.
-  var SECTION_MAP = {
-    googleplay: 'google play'
-  };
 
   /*
     Utility method for creating dom for the description area of a card.
@@ -1655,10 +4127,10 @@
     return $description;
   }
 
+
   /* Simple jquery function to create dom for a standard resource card */
-  $.fn.decorateResourceCard = function(resource, plusone) {
-    var section = resource.category || resource.type;
-    section = (SECTION_MAP[section] || section).toLowerCase();
+  $.fn.decorateResourceCard = function(resource,plusone) {
+    var section = resource.group || resource.type;
     var imgUrl = resource.image ||
       'assets/images/resource-card-default-android.jpg';
 
@@ -1666,7 +4138,7 @@
       imgUrl = toRoot + imgUrl;
     }
 
-    if (resource.type === 'youtube' || resource.type === 'video') {
+    if (resource.type === 'youtube') {
       $('<div>').addClass('play-button')
         .append($('<i class="dac-sprite dac-play-white">'))
         .appendTo(this);
@@ -1679,8 +4151,7 @@
 
     $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
       .append($('<div>').addClass('section').text(section))
-      .append($('<div>').addClass('title' + (resource.title_highlighted ? ' highlighted' : ''))
-        .html(resource.title_highlighted || resource.title))
+      .append($('<div>').addClass('title').html(resource.title))
       .append(buildResourceCardDescription(resource, plusone))
       .appendTo(this);
 
@@ -1688,7 +4159,7 @@
   };
 
   /* Simple jquery function to create dom for a resource section card (menu) */
-  $.fn.decorateResourceSection = function(section, plusone) {
+  $.fn.decorateResourceSection = function(section,plusone) {
     var resource = section.resource;
     //keep url clean for matching and offline mode handling
     var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
@@ -1766,6 +4237,9 @@
     return this;
   };
 
+
+
+
   /* Render other types of resource styles that are not cards. */
   $.fn.decorateResource = function(resource, opts) {
     var imgUrl = resource.image ||
@@ -1784,7 +4258,7 @@
       $('<div>').addClass('image')
         .css('background-image', 'url(' + imgUrl + ')'),
       $('<div>').addClass('info').append(
-        $('<h4>').addClass('title').html(resource.title_highlighted || resource.title),
+        $('<h4>').addClass('title').html(resource.title),
         $('<p>').addClass('summary').html(resource.summary),
         $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
       )
@@ -1794,6 +4268,31 @@
   };
 })(jQuery);
 
+
+/* Calculate the vertical area remaining */
+(function($) {
+    $.fn.ellipsisfade= function(lineHeight) {
+        this.each(function() {
+            // get element text
+            var $this = $(this);
+            var remainingHeight = $this.parent().parent().height();
+            $this.parent().siblings().each(function ()
+            {
+              if ($(this).is(":visible")) {
+                var h = $(this).outerHeight(true);
+                remainingHeight = remainingHeight - h;
+              }
+            });
+
+            adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
+            $this.parent().css({'height': adjustedRemainingHeight});
+            $this.css({'height': "auto"});
+        });
+
+        return this;
+    };
+}) (jQuery);
+
 /*
   Fullscreen Carousel
 
@@ -1884,6 +4383,10 @@
   }
 })();
 
+
+
+
+
 /*
   Tab Carousel
 
@@ -1989,7 +4492,7 @@
     // add HRs below all H2s (except for a few other h2 variants)
     // Consider doing this with css instead.
     h2Titles = $('h2').not('#qv h2, #tb h2, .sidebox h2, #devdoc-nav h2, h2.norule');
-    h2Titles.css({paddingBottom:0}).after('<hr/>');
+    h2Titles.css({marginBottom:0}).after('<hr/>');
 
     // Exit early if on older browser.
     if (!window.matchMedia) {
@@ -2022,7 +4525,7 @@
       // Find all the relevant nodes.
       var $title = $(this);
       var $hr = $title.next();
-      var $contents = allNextUntil($hr[0], 'h2, .next-docs');
+      var $contents = $hr.nextUntil('h2, .next-docs');
       var $section = $($title)
         .add($hr)
         .add($title.prev('a[name]'))
@@ -2051,9 +4554,7 @@
 
       // Wrap in magic markup.
       $section = $section.wrapAll('<div class="dac-toggle dac-mobile">').parent();
-
-      // extra div used for max-height calculation.
-      $contents.wrapAll('<div class="dac-toggle-content dac-expand"><div>');
+      $contents.wrapAll('<div class="dac-toggle-content"><div>'); // extra div used for max-height calculation.
 
       // Pre-expand section if requested.
       if ($title.hasClass('is-expanded')) {
@@ -2070,511 +4571,11 @@
     });
   }
 
-  // Similar to $.fn.nextUntil() except we need all nodes, jQuery skips text nodes.
-  function allNextUntil(elem, until) {
-    var matched = [];
-
-    while ((elem = elem.nextSibling) && elem.nodeType !== 9) {
-      if (elem.nodeType === 1 && jQuery(elem).is(until)) {
-        break;
-      }
-      matched.push(elem);
-    }
-    return $(matched);
-  }
-
   $(function() {
     initWidget();
   });
 })(jQuery);
 
-(function($, window) {
-  'use strict';
-
-  // Blogger API info
-  var apiUrl = 'https://www.googleapis.com/blogger/v3';
-  var apiKey = 'AIzaSyCFhbGnjW06dYwvRCU8h_zjdpS4PYYbEe8';
-
-  // Blog IDs can be found in the markup of the blog posts
-  var blogs = {
-    'android-developers': {
-      id: '6755709643044947179',
-      title: 'Android Developers Blog'
-    }
-  };
-  var monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
-      'July', 'August', 'September', 'October', 'November', 'December'];
-
-  var BlogReader = (function() {
-    var reader;
-
-    function BlogReader() {
-      this.doneSetup = false;
-    }
-
-    /**
-     * Initialize the blog reader and modal.
-     */
-    BlogReader.prototype.setup = function() {
-      $('#jd-content').append(
-          '<div id="blog-reader" data-modal="blog-reader" class="dac-modal dac-has-small-header">' +
-            '<div class="dac-modal-container">' +
-              '<div class="dac-modal-window">' +
-                '<header class="dac-modal-header">' +
-                  '<div class="dac-modal-header-actions">' +
-                    '<a href="" class="dac-modal-header-open" target="_blank">' +
-                      '<i class="dac-sprite dac-open-in-new"></i>' +
-                    '</a>' +
-                    '<button class="dac-modal-header-close" data-modal-toggle>' +
-                    '</button>' +
-                  '</div>' +
-                  '<h2 class="norule dac-modal-header-title"></h2>' +
-                '</header>' +
-                '<div class="dac-modal-content dac-blog-reader">' +
-                  '<time class="dac-blog-reader-date" pubDate></time>' +
-                  '<h3 class="dac-blog-reader-title"></h3>' +
-                  '<div class="dac-blog-reader-text clearfix"></div>' +
-                '</div>' +
-              '</div>' +
-            '</div>' +
-          '</div>');
-
-      this.blogReader = $('#blog-reader').dacModal();
-
-      this.doneSetup = true;
-    };
-
-    BlogReader.prototype.openModal_ = function(blog, post) {
-      var published = new Date(post.published);
-      var formattedDate = monthNames[published.getMonth()] + ' ' + published.getDay() + ' ' + published.getFullYear();
-      this.blogReader.find('.dac-modal-header-open').attr('href', post.url);
-      this.blogReader.find('.dac-modal-header-title').text(blog.title);
-      this.blogReader.find('.dac-blog-reader-title').html(post.title);
-      this.blogReader.find('.dac-blog-reader-date').html(formattedDate);
-      this.blogReader.find('.dac-blog-reader-text').html(post.content);
-      this.blogReader.trigger('modal-open');
-    };
-
-    /**
-     * Show a blog post in a modal
-     * @param  {string} blogName - The name of the Blogspot blog.
-     * @param  {string} postPath - The path to the blog post.
-     * @param  {bool} secondTry - Has it failed once?
-     */
-    BlogReader.prototype.showPost = function(blogName, postPath, secondTry) {
-      var blog = blogs[blogName];
-      var postUrl = 'https://' + blogName + '.blogspot.com' + postPath;
-
-      var url = apiUrl + '/blogs/' + blog.id + '/posts/bypath?path=' + encodeURIComponent(postPath) + '&key=' + apiKey;
-      $.ajax(url, {timeout: 650}).done(this.openModal_.bind(this, blog)).fail(function(error) {
-        // Retry once if we get an error
-        if (error.status === 500 && !secondTry) {
-          this.showPost(blogName, postPath, true);
-        } else {
-          window.location.href = postUrl;
-        }
-      }.bind(this));
-    };
-
-    return {
-      getReader: function() {
-        if (!reader) {
-          reader = new BlogReader();
-        }
-        return reader;
-      }
-    };
-  })();
-
-  var blogReader = BlogReader.getReader();
-
-  function wrapLinkWithReader(e) {
-    var el = $(e.currentTarget);
-    if (el.hasClass('dac-modal-header-open')) {
-      return;
-    }
-
-    // Only catch links on blogspot.com
-    var matches = el.attr('href').match(/https?:\/\/([^\.]*).blogspot.com([^$]*)/);
-    if (matches && matches.length === 3) {
-      var blogName = matches[1];
-      var postPath = matches[2];
-
-      // Check if we have information about the blog
-      if (!blogs[blogName]) {
-        return;
-      }
-
-      // Setup the first time it's used
-      if (!blogReader.doneSetup) {
-        blogReader.setup();
-      }
-
-      e.preventDefault();
-      blogReader.showPost(blogName, postPath);
-    }
-  }
-
-  $(document).on('click.blog-reader', 'a[href*="blogspot.com/"]', wrapLinkWithReader);
-})(jQuery, window);
-
-(function($) {
-  $.fn.debounce = function(func, wait, immediate) {
-    var timeout;
-
-    return function() {
-      var context = this;
-      var args = arguments;
-
-      var later = function() {
-        timeout = null;
-        if (!immediate) {
-          func.apply(context, args);
-        }
-      };
-
-      var callNow = immediate && !timeout;
-      clearTimeout(timeout);
-      timeout = setTimeout(later, wait);
-
-      if (callNow) {
-        func.apply(context, args);
-      }
-    };
-  };
-})(jQuery);
-
-/* Calculate the vertical area remaining */
-(function($) {
-  $.fn.ellipsisfade = function() {
-    // Only fetch line-height of first element to avoid recalculate style.
-    // Will be NaN if no elements match, which is ok.
-    var lineHeight = parseInt(this.css('line-height'), 10);
-
-    this.each(function() {
-      // get element text
-      var $this = $(this);
-      var remainingHeight = $this.parent().parent().height();
-      $this.parent().siblings().each(function() {
-        var elHeight;
-        if ($(this).is(':visible')) {
-          elHeight = $(this).outerHeight(true);
-          remainingHeight = remainingHeight - elHeight;
-        }
-      });
-
-      var adjustedRemainingHeight = ((remainingHeight) / lineHeight >> 0) * lineHeight;
-      $this.parent().css({height: adjustedRemainingHeight});
-      $this.css({height: 'auto'});
-    });
-
-    return this;
-  };
-
-  /* Pass the line height to ellipsisfade() to adjust the height of the
-   text container to show the max number of lines possible, without
-   showing lines that are cut off. This works with the css ellipsis
-   classes to fade last text line and apply an ellipsis char. */
-  function updateEllipsis(context) {
-    if (!(context instanceof jQuery)) {
-      context = $('html');
-    }
-
-    context.find('.card-info .text').ellipsisfade();
-  }
-
-  $(window).on('resize', $.fn.debounce(updateEllipsis, 500));
-  $(updateEllipsis);
-  $('html').on('dac:domchange', function(e) { updateEllipsis($(e.target)); });
-})(jQuery);
-
-/* Filter */
-(function($) {
-  'use strict';
-
-  /**
-   * A single filter item content.
-   * @type {string} - Element template.
-   * @private
-   */
-  var ITEM_STR_ = '<input type="checkbox" value="{{value}}" class="dac-form-checkbox" id="{{id}}">' +
-      '<label for="{{id}}" class="dac-form-checkbox-button"></label>' +
-      '<label for="{{id}}" class="dac-form-label">{{name}}</label>';
-
-  /**
-   * Template for a chip element.
-   * @type {*|HTMLElement}
-   * @private
-   */
-  var CHIP_BASE_ = $('<li class="dac-filter-chip">' +
-    '<button class="dac-filter-chip-close">' +
-      '<i class="dac-sprite dac-close-black dac-filter-chip-close-icon"></i>' +
-    '</button>' +
-  '</li>');
-
-  /**
-   * Component to handle narrowing down resources.
-   * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
-   * @constructor
-   */
-  function Filter(el, options) {
-    this.el = $(el);
-    this.options = $.extend({}, Filter.DEFAULTS_, options);
-    this.init();
-  }
-
-  Filter.DEFAULTS_ = {
-    activeClass: 'dac-active',
-    chipsDataAttr: 'filter-chips',
-    nameDataAttr: 'filter-name',
-    countDataAttr: 'filter-count',
-    tabViewDataAttr: 'tab-view',
-    valueDataAttr: 'filter-value'
-  };
-
-  /**
-   * Draw resource cards.
-   * @param {Array} resources
-   * @private
-   */
-  Filter.prototype.draw_ = function(resources) {
-    var that = this;
-
-    if (resources.length === 0) {
-      this.containerEl_.html('<p class="dac-filter-message">Nothing matches selected filters.</p>');
-      return;
-    }
-
-    // Draw resources.
-    that.containerEl_.resourceWidget(resources, that.data_.options);
-  };
-
-  /**
-   * Initialize a Filter component.
-   */
-  Filter.prototype.init = function() {
-    this.containerEl_ = $(this.options.filter);
-
-    // Setup data settings
-    this.data_ = {};
-    this.data_.chips = {};
-    this.data_.options = this.containerEl_.widgetOptions();
-    this.data_.all = window.metadata.query(this.data_.options);
-
-    // Initialize filter UI
-    this.initUi();
-  };
-
-  /**
-   * Generate a chip for a given filter item.
-   * @param {Object} item - A single filter option (checkbox container).
-   * @returns {HTMLElement} A new Chip element.
-   */
-  Filter.prototype.chipForItem = function(item) {
-    var chip = CHIP_BASE_.clone();
-    chip.prepend(this.data_.chips[item.data('filter-value')]);
-    chip.data('item.dac-filter', item);
-    item.data('chip.dac-filter', chip);
-    this.addToItemValue(item, 1);
-    return chip[0];
-  };
-
-  /**
-   * Update count of checked filter items.
-   * @param {Object} item - A single filter option (checkbox container).
-   * @param {Number} value - Either -1 or 1.
-   */
-  Filter.prototype.addToItemValue = function(item, value) {
-    var tab = item.parent().data(this.options.tabViewDataAttr);
-    var countEl = this.countEl_.filter('[data-' + this.options.countDataAttr + '="' + tab + '"]');
-    var count = value + parseInt(countEl.text(), 10);
-    countEl.text(count);
-    countEl.toggleClass('dac-disabled', count === 0);
-  };
-
-  /**
-   * Set event listeners.
-   * @private
-   */
-  Filter.prototype.setEventListeners_ = function() {
-    this.chipsEl_.on('click.dac-filter', '.dac-filter-chip-close', this.closeChipHandler_.bind(this));
-    this.tabViewEl_.on('change.dac-filter', ':checkbox', this.toggleCheckboxHandler_.bind(this));
-  };
-
-  /**
-   * Check filter items that are active by default.
-   */
-  Filter.prototype.activateInitialFilters_ = function() {
-    var id = (new Date()).getTime();
-    var initiallyCheckedValues = this.data_.options.query.replace(/,\s*/g, '+').split('+');
-    var chips = document.createDocumentFragment();
-    var that = this;
-
-    this.items_.each(function(i) {
-      var item = $(this);
-      var opts = item.data();
-      that.data_.chips[opts.filterValue] = opts.filterName;
-
-      var checkbox = $(ITEM_STR_.replace(/\{\{name\}\}/g, opts.filterName)
-        .replace(/\{\{value\}\}/g, opts.filterValue)
-        .replace(/\{\{id\}\}/g, 'filter-' + id + '-' + (i + 1)));
-
-      if (initiallyCheckedValues.indexOf(opts.filterValue) > -1) {
-        checkbox[0].checked = true;
-        chips.appendChild(that.chipForItem(item));
-      }
-
-      item.append(checkbox);
-    });
-
-    this.chipsEl_.append(chips);
-  };
-
-  /**
-   * Initialize the Filter view
-   */
-  Filter.prototype.initUi = function() {
-    // Cache DOM elements
-    this.chipsEl_ = this.el.find('[data-' + this.options.chipsDataAttr + ']');
-    this.countEl_ = this.el.find('[data-' + this.options.countDataAttr + ']');
-    this.tabViewEl_ = this.el.find('[data-' + this.options.tabViewDataAttr + ']');
-    this.items_ = this.el.find('[data-' + this.options.nameDataAttr + ']');
-
-    // Setup UI
-    this.draw_(this.data_.all);
-    this.activateInitialFilters_();
-    this.setEventListeners_();
-  };
-
-  /**
-   * @returns {[types|Array, tags|Array, category|Array]}
-   */
-  Filter.prototype.getActiveClauses = function() {
-    var tags = [];
-    var types = [];
-    var categories = [];
-
-    this.items_.find(':checked').each(function(i, checkbox) {
-      // Currently, there is implicit business logic here that `tag` is AND'ed together
-      // while `type` is OR'ed. So , and + do the same thing here. It would be great to
-      // reuse the same query engine for filters, but it would need more powerful syntax.
-      // Probably parenthesis, to support "tag:dog + tag:cat + (type:video, type:blog)"
-      var expression = $(checkbox).val();
-      var regex = /(\w+):(\w+)/g;
-      var match;
-
-      while (match = regex.exec(expression)) {
-        switch (match[1]) {
-          case 'category':
-            categories.push(match[2]);
-            break;
-          case 'tag':
-            tags.push(match[2]);
-            break;
-          case 'type':
-            types.push(match[2]);
-            break;
-        }
-      }
-    });
-
-    return [types, tags, categories];
-  };
-
-  /**
-   * Actual filtering logic.
-   * @returns {Array}
-   */
-  Filter.prototype.filteredResources = function() {
-    var data = this.getActiveClauses();
-    var types = data[0];
-    var tags = data[1];
-    var categories = data[2];
-    var resources = [];
-    var resource = {};
-    var tag = '';
-    var shouldAddResource = true;
-
-    for (var resourceIndex = 0; resourceIndex < this.data_.all.length; resourceIndex++) {
-      resource = this.data_.all[resourceIndex];
-      shouldAddResource = types.indexOf(resource.type) > -1;
-
-      if (categories && categories.length > 0) {
-        shouldAddResource = shouldAddResource && categories.indexOf(resource.category) > -1;
-      }
-
-      for (var tagIndex = 0; shouldAddResource && tagIndex < tags.length; tagIndex++) {
-        tag = tags[tagIndex];
-        shouldAddResource = resource.tags.indexOf(tag) > -1;
-      }
-
-      if (shouldAddResource) {
-        resources.push(resource);
-      }
-    }
-
-    return resources;
-  };
-
-  /**
-   * Close Chip Handler
-   * @param {Event} event - Click event
-   * @private
-   */
-  Filter.prototype.closeChipHandler_ = function(event) {
-    var chip = $(event.currentTarget).parent();
-    var checkbox = chip.data('item.dac-filter').find(':first-child')[0];
-    checkbox.checked = false;
-    this.changeStateForCheckbox(checkbox);
-  };
-
-  /**
-   * Handle filter item state change.
-   * @param {Event} event - Change event
-   * @private
-   */
-  Filter.prototype.toggleCheckboxHandler_ = function(event) {
-    this.changeStateForCheckbox(event.currentTarget);
-  };
-
-  /**
-   * Redraw resource view based on new state.
-   * @param checkbox
-   */
-  Filter.prototype.changeStateForCheckbox = function(checkbox) {
-    var item = $(checkbox).parent();
-
-    if (checkbox.checked) {
-      this.chipsEl_.append(this.chipForItem(item));
-      ga('send', 'event', 'Filters', 'Check', $(checkbox).val());
-    } else {
-      item.data('chip.dac-filter').remove();
-      this.addToItemValue(item, -1);
-      ga('send', 'event', 'Filters', 'Uncheck', $(checkbox).val());
-    }
-
-    this.draw_(this.filteredResources());
-  };
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacFilter = function() {
-    return this.each(function() {
-      var el = $(this);
-      new Filter(el, el.data());
-    });
-  };
-
-  /**
-   * Data Attribute API
-   */
-  $(function() {
-    $('[data-filter]').dacFilter();
-  });
-})(jQuery);
-
 (function($) {
   'use strict';
 
@@ -2631,188 +4632,97 @@
   });
 })(jQuery);
 
+/* global toRoot, CAROUSEL_OVERRIDE */
 (function($) {
-  'use strict';
+  // Ordering matters
+  var TAG_MAP = [
+    {from: 'developerstory', to: 'Android Developer Story'},
+    {from: 'googleplay', to: 'Google Play'}
+  ];
 
-  /**
-   * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
-   * @constructor
-   */
-  function Crumbs(selected, options) {
-    this.options = $.extend({}, Crumbs.DEFAULTS_, options);
-    this.el = $(this.options.container);
-
-    // Do not build breadcrumbs for landing site.
-    if (!selected || location.pathname === '/index.html' || location.pathname === '/') {
-      return;
-    }
-
-    // Cache navigation resources
-    this.selected = $(selected);
-    this.selectedParent = this.selected.closest('.dac-nav-secondary').siblings('a');
-
-    // Build the breadcrumb list.
-    this.init();
-  }
-
-  Crumbs.DEFAULTS_ = {
-    container: '.dac-header-crumbs',
-    crumbItem: $('<li class="dac-header-crumbs-item">'),
-    linkClass: 'dac-header-crumbs-link'
-  };
-
-  Crumbs.prototype.init = function() {
-    Crumbs.buildCrumbForLink(this.selected.clone()).appendTo(this.el);
-
-    if (this.selectedParent.length) {
-      Crumbs.buildCrumbForLink(this.selectedParent.clone()).prependTo(this.el);
-    }
-
-    // Reveal the breadcrumbs
-    this.el.addClass('dac-has-content');
-  };
-
-  /**
-   * Build a HTML structure for a breadcrumb.
-   * @param {string} link
-   * @return {jQuery}
-   */
-  Crumbs.buildCrumbForLink = function(link) {
-    link.find('br').replaceWith(' ');
-
-    var crumbLink = $('<a>')
-      .attr('class', Crumbs.DEFAULTS_.linkClass)
-      .attr('href', link.attr('href'))
-      .text(link.text());
-
-    return Crumbs.DEFAULTS_.crumbItem.clone().append(crumbLink);
-  };
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacCrumbs = function(options) {
-    return this.each(function() {
-      new Crumbs(this, options);
-    });
-  };
-})(jQuery);
-
-(function($) {
-  'use strict';
-
-  /**
-   * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
-   * @constructor
-   */
-  function SearchInput(el, options) {
-    this.el = $(el);
-    this.options = $.extend({}, SearchInput.DEFAULTS_, options);
-    this.body = $('body');
-    this.input = this.el.find('input');
-    this.close = this.el.find(this.options.closeButton);
-    this.clear = this.el.find(this.options.clearButton);
-    this.icon = this.el.find('.' + this.options.iconClass);
-    this.init();
-  }
-
-  SearchInput.DEFAULTS_ = {
-    activeClass: 'dac-active',
-    activeIconClass: 'dac-search',
-    closeButton: '[data-search-close]',
-    clearButton: '[data-search-clear]',
-    hiddenClass: 'dac-hidden',
-    iconClass: 'dac-header-search-icon',
-    searchModeClass: 'dac-search-mode',
-    transitionDuration: 250
-  };
-
-  SearchInput.prototype.init = function() {
-    this.input.on('focus.dac-search', this.setActiveState.bind(this))
-              .on('input.dac-search', this.checkInputValue.bind(this));
-    this.close.on('click.dac-search', this.unsetActiveStateHandler_.bind(this));
-    this.clear.on('click.dac-search', this.clearInput.bind(this));
-  };
-
-  SearchInput.prototype.setActiveState = function() {
-    var that = this;
-
-    this.clear.addClass(this.options.hiddenClass);
-    this.body.addClass(this.options.searchModeClass);
-    this.checkInputValue();
-
-    // Set icon to black after background has faded to white.
-    setTimeout(function() {
-      that.icon.addClass(that.options.activeIconClass);
-    }, this.options.transitionDuration);
-  };
-
-  SearchInput.prototype.unsetActiveStateHandler_ = function(event) {
-    event.preventDefault();
-    this.unsetActiveState();
-  };
-
-  SearchInput.prototype.unsetActiveState = function() {
-    this.icon.removeClass(this.options.activeIconClass);
-    this.clear.addClass(this.options.hiddenClass);
-    this.body.removeClass(this.options.searchModeClass);
-  };
-
-  SearchInput.prototype.clearInput = function(event) {
-    event.preventDefault();
-    this.input.val('');
-    this.clear.addClass(this.options.hiddenClass);
-  };
-
-  SearchInput.prototype.checkInputValue = function() {
-    if (this.input.val().length) {
-      this.clear.removeClass(this.options.hiddenClass);
-    } else {
-      this.clear.addClass(this.options.hiddenClass);
-    }
-  };
-
-  /**
-   * jQuery plugin
-   * @param {object} options - Override default options.
-   */
-  $.fn.dacSearchInput = function() {
-    return this.each(function() {
-      var el = $(this);
-      el.data('search-input.dac', new SearchInput(el, el.data()));
-    });
-  };
-
-  /**
-   * Data Attribute API
-   */
-  $(function() {
-    $('[data-search]').dacSearchInput();
-  });
-})(jQuery);
-
-/* global METADATA */
-(function($) {
   function DacCarouselQuery(el) {
-    el = $(el);
+    this.el = $(el);
 
-    var opts = el.data();
+    var opts = this.el.data();
     opts.maxResults = parseInt(opts.maxResults || '100', 10);
     opts.query = opts.carouselQuery;
-    var resources = window.metadata.query(opts);
+    var resources = $.queryResources(opts);
 
-    el.empty();
-    $(resources).each(function() {
-      var resource = $.extend({}, this, METADATA.carousel[this.url]);
-      el.dacHero(resource);
-    });
+    this.el.empty();
+    $(resources).map(function() {
+      var resource = $.extend({}, this, CAROUSEL_OVERRIDE[this.url]);
+      var slide = $('<article class="dac-expand dac-hero">');
+      var image = cleanUrl(resource.heroImage || resource.image);
+      var fullBleed = image && !resource.heroColor;
+
+      // Configure background
+      slide.css({
+        backgroundImage: fullBleed ? 'url(' + image + ')' : '',
+        backgroundColor: resource.heroColor || ''
+      });
+
+      // Should copy be inverted
+      slide.toggleClass('dac-invert', resource.heroInvert || fullBleed);
+      slide.toggleClass('dac-darken', fullBleed);
+
+      // Should be clickable
+      slide.append($('<a class="dac-hero-carousel-action">').attr('href', cleanUrl(resource.url)));
+
+      var cols = $('<div class="cols dac-hero-content">');
+
+      // inline image column
+      var rightCol = $('<div class="col-1of2 col-push-1of2 dac-hero-figure">')
+        .appendTo(cols);
+
+      if (!fullBleed && image) {
+        rightCol.append($('<img>').attr('src', image));
+      }
+
+      // info column
+      $('<div class="col-1of2 col-pull-1of2">')
+        .append($('<div class="dac-hero-tag">').text(formatTag(resource)))
+        .append($('<h1 class="dac-hero-title">').text(formatTitle(resource)))
+        .append($('<p class="dac-hero-description">').text(resource.summary))
+        .append($('<a class="dac-hero-cta">')
+          .text(formatCTA(resource))
+          .attr('href', cleanUrl(resource.url))
+          .prepend($('<span class="dac-sprite dac-auto-chevron">'))
+        )
+        .appendTo(cols);
+
+      slide.append(cols.wrap('<div class="wrap">').parent());
+      return slide[0];
+    }).prependTo(this.el);
 
     // Pagination element.
-    el.append('<div class="dac-hero-carousel-pagination"><div class="wrap" data-carousel-pagination>');
+    this.el.append('<div class="dac-hero-carousel-pagination"><div class="wrap" data-carousel-pagination>');
 
-    el.dacCarousel();
+    this.el.dacCarousel();
+  }
+
+  function cleanUrl(url) {
+    if (url && url.indexOf('//') === -1) {
+      url = toRoot + url;
+    }
+    return url;
+  }
+
+  function formatTag(resource) {
+    // Hmm, need a better more scalable solution for this.
+    for (var i = 0, mapping; mapping = TAG_MAP[i]; i++) {
+      if (resource.tags.indexOf(mapping.from) > -1) {
+        return mapping.to;
+      }
+    }
+    return resource.type;
+  }
+
+  function formatTitle(resource) {
+    return resource.title.replace(/android developer story: /i, '');
+  }
+
+  function formatCTA(resource) {
+    return resource.type === 'youtube' ? 'Watch the video' : 'Learn more';
   }
 
   // jQuery plugin
@@ -3007,884 +4917,16 @@
   });
 })(jQuery);
 
-/* global toRoot */
-
-(function($) {
-  // Ordering matters
-  var TAG_MAP = [
-    {from: 'developerstory', to: 'Android Developer Story'},
-    {from: 'googleplay', to: 'Google Play'}
-  ];
-
-  function DacHero(el, resource, isSearch) {
-    var slide = $('<article>');
-    slide.addClass(isSearch ? 'dac-search-hero' : 'dac-expand dac-hero');
-    var image = cleanUrl(resource.heroImage || resource.image);
-    var fullBleed = image && !resource.heroColor;
-
-    if (!isSearch) {
-      // Configure background
-      slide.css({
-        backgroundImage: fullBleed ? 'url(' + image + ')' : '',
-        backgroundColor: resource.heroColor || ''
-      });
-
-      // Should copy be inverted
-      slide.toggleClass('dac-invert', resource.heroInvert || fullBleed);
-      slide.toggleClass('dac-darken', fullBleed);
-
-      // Should be clickable
-      slide.append($('<a class="dac-hero-carousel-action">').attr('href', cleanUrl(resource.url)));
-    }
-
-    var cols = $('<div class="cols dac-hero-content">');
-
-    // inline image column
-    var rightCol = $('<div class="col-1of2 col-push-1of2 dac-hero-figure">')
-      .appendTo(cols);
-
-    if ((!fullBleed || isSearch) && image) {
-      rightCol.append($('<img>').attr('src', image));
-    }
-
-    // info column
-    $('<div class="col-1of2 col-pull-1of2">')
-      .append($('<div class="dac-hero-tag">').text(formatTag(resource)))
-      .append($('<h1 class="dac-hero-title">').text(formatTitle(resource)))
-      .append($('<p class="dac-hero-description">').text(resource.summary))
-      .append($('<a class="dac-hero-cta">')
-        .text(formatCTA(resource))
-        .attr('href', cleanUrl(resource.url))
-        .prepend($('<span class="dac-sprite dac-auto-chevron">'))
-      )
-      .appendTo(cols);
-
-    slide.append(cols.wrap('<div class="wrap">').parent());
-    el.append(slide);
-  }
-
-  function cleanUrl(url) {
-    if (url && url.indexOf('//') === -1) {
-      url = toRoot + url;
-    }
-    return url;
-  }
-
-  function formatTag(resource) {
-    // Hmm, need a better more scalable solution for this.
-    for (var i = 0, mapping; mapping = TAG_MAP[i]; i++) {
-      if (resource.tags.indexOf(mapping.from) > -1) {
-        return mapping.to;
-      }
-    }
-    return resource.type;
-  }
-
-  function formatTitle(resource) {
-    return resource.title.replace(/android developer story: /i, '');
-  }
-
-  function formatCTA(resource) {
-    return resource.type === 'youtube' ? 'Watch the video' : 'Learn more';
-  }
-
-  // jQuery plugin
-  $.fn.dacHero = function(resource, isSearch) {
-    return this.each(function() {
-      var el = $(this);
-      return new DacHero(el, resource, isSearch);
-    });
-  };
-})(jQuery);
-
-(function($) {
-  'use strict';
-
-  function highlightString(label, query) {
-    query = query || '';
-    //query = query.replace('<wbr>', '').replace('.', '\\.');
-    var queryRE = new RegExp('(' + query + ')', 'ig');
-    return label.replace(queryRE, '<em>$1</em>');
-  }
-
-  $.fn.highlightMatches = function(query) {
-    return this.each(function() {
-      var el = $(this);
-      var label = el.html();
-      var highlighted = highlightString(label, query);
-      el.html(highlighted);
-      el.addClass('highlighted');
-    });
-  };
-})(jQuery);
-
-/**
- * History tracking.
- * Track visited urls in localStorage.
- */
-(function($) {
-  var PAGES_TO_STORE_ = 100;
-  var MIN_NUMBER_OF_PAGES_TO_DISPLAY_ = 6;
-  var CONTAINER_SELECTOR_ = '.dac-search-results-history-wrap';
-
-  /**
-   * Generate resource cards for visited pages.
-   * @param {HTMLElement} el
-   * @constructor
-   */
-  function HistoryQuery(el) {
-    this.el = $(el);
-
-    // Only show history component if enough pages have been visited.
-    if (getVisitedPages().length < MIN_NUMBER_OF_PAGES_TO_DISPLAY_) {
-      this.el.closest(CONTAINER_SELECTOR_).addClass('dac-hidden');
-      return;
-    }
-
-    // Rename query
-    this.el.data('query', this.el.data('history-query'));
-
-    // jQuery method to populate cards.
-    this.el.resourceWidget();
-  }
-
-  /**
-   * Fetch from localStorage an array of visted pages
-   * @returns {Array}
-   */
-  function getVisitedPages() {
-    var visited = localStorage.getItem('visited-pages');
-    return visited ? JSON.parse(visited) : [];
-  }
-
-  /**
-   * Return a page corresponding to cuurent pathname. If none exists, create one.
-   * @param {Array} pages
-   * @param {String} path
-   * @returns {Object} Page
-   */
-  function getPageForPath(pages, path) {
-    var page;
-
-    // Backwards lookup for current page, last pages most likely to be visited again.
-    for (var i = pages.length - 1; i >= 0; i--) {
-      if (pages[i].path === path) {
-        page = pages[i];
-
-        // Remove page object from pages list to ensure correct ordering.
-        pages.splice(i, 1);
-
-        return page;
-      }
-    }
-
-    // If storage limit is exceeded, remove last visited path.
-    if (pages.length >= PAGES_TO_STORE_) {
-      pages.shift();
-    }
-
-    return {path: path};
-  }
-
-  /**
-   * Add current page to back of visited array, increase hit count by 1.
-   */
-  function addCurrectPage() {
-    var path = location.pathname;
-
-    // Do not track frontpage visits.
-    if (path === '/' || path === '/index.html') {return;}
-
-    var pages = getVisitedPages();
-    var page = getPageForPath(pages, path);
-
-    // New page visits have no hit count.
-    page.hit = ~~page.hit + 1;
-
-    // Most recently visted pages are located at the end of the visited array.
-    pages.push(page);
-
-    localStorage.setItem('visited-pages', JSON.stringify(pages));
-  }
-
-  /**
-   * Hit count compare function.
-   * @param {Object} a - page
-   * @param {Object} b - page
-   * @returns {number}
-   */
-  function byHit(a, b) {
-    if (a.hit > b.hit) {
-      return -1;
-    } else if (a.hit < b.hit) {
-      return 1;
-    }
-
-    return 0;
-  }
-
-  /**
-   * Return a list of visited urls in a given order.
-   * @param {String} order - (recent|most-visited)
-   * @returns {Array}
-   */
-  $.dacGetVisitedUrls = function(order) {
-    var pages = getVisitedPages();
-
-    if (order === 'recent') {
-      pages.reverse();
-    } else {
-      pages.sort(byHit);
-    }
-
-    return pages.map(function(page) {
-      return page.path.replace(/^\//, '');
-    });
-  };
-
-  // jQuery plugin
-  $.fn.dacHistoryQuery = function() {
-    return this.each(function() {
-      var el = $(this);
-      var data = el.data('dac.recentlyVisited');
-
-      if (!data) {
-        el.data('dac.recentlyVisited', (data = new HistoryQuery(el)));
-      }
-    });
-  };
-
-  $(function() {
-    $('[data-history-query]').dacHistoryQuery();
-    // Do not block page rendering.
-    setTimeout(addCurrectPage, 0);
-  });
-})(jQuery);
-
-/* ############################################ */
-/* ##########     LOCALIZATION     ############ */
-/* ############################################ */
-/**
- * Global helpers.
- */
-function getBaseUri(uri) {
-  var intlUrl = (uri.substring(0, 6) === '/intl/');
-  if (intlUrl) {
-    var base = uri.substring(uri.indexOf('intl/') + 5, uri.length);
-    base = base.substring(base.indexOf('/') + 1, base.length);
-    return '/' + base;
-  } else {
-    return uri;
-  }
-}
-
-function changeLangPref(targetLang, submit) {
-  window.writeCookie('pref_lang', targetLang, null);
-//DD
-  $('#language').find('option[value="' + targetLang + '"]').attr('selected', true);
-  //  #######  TODO:  Remove this condition once we're stable on devsite #######
-  //  This condition is only needed if we still need to support legacy GAE server
-  if (window.devsite) {
-    // Switch language when on Devsite server
-    if (submit) {
-      $('#setlang').submit();
-    }
-  } else {
-    // Switch language when on legacy GAE server
-    if (submit) {
-      window.location = getBaseUri(location.pathname);
-    }
-  }
-}
-// Redundant usage to appease jshint.
-window.changeLangPref = changeLangPref;
-
-(function() {
-  /**
-   * Whitelisted locales. Should match choices in language dropdown. Repeated here
-   * as a lot of i18n logic happens before page load and dropdown is ready.
-   */
-  var LANGUAGES = [
-    'en',
-    'es',
-    'in',
-    'ja',
-    'ko',
-    'pt-br',
-    'ru',
-    'vi',
-    'zh-cn',
-    'zh-tw'
-  ];
-
-  /**
-   * Master list of translated strings for template files.
-   */
-  var PHRASES = {
-    'newsletter': {
-      'title': 'Get the latest Android developer news and tips that will help you find success on Google Play.',
-      'requiredHint': '* Required Fields',
-      'name': 'Full name',
-      'email': 'Email address',
-      'company': 'Company / developer name',
-      'appUrl': 'One of your Play Store app URLs',
-      'business': {
-        'label': 'Which best describes your business:',
-        'apps': 'Apps',
-        'games': 'Games',
-        'both': 'Apps & Games'
-      },
-      'confirmMailingList': 'Add me to the mailing list for the monthly newsletter and occasional emails about ' +
-                            'development and Google Play opportunities.',
-      'privacyPolicy': 'I acknowledge that the information provided in this form will be subject to Google\'s ' +
-                       '<a href="https://www.google.com/policies/privacy/" target="_blank">privacy policy</a>.',
-      'languageVal': 'English',
-      'successTitle': 'Hooray!',
-      'successDetails': 'You have successfully signed up for the latest Android developer news and tips.',
-      'languageValTarget': {
-        'en': 'English',
-        'ar': 'Arabic (العربيّة)',
-        'in': 'Indonesian (Bahasa)',
-        'fr': 'French (français)',
-        'de': 'German (Deutsch)',
-        'ja': 'Japanese (日本語)',
-        'ko': 'Korean (한국어)',
-        'ru': 'Russian (Русский)',
-        'es': 'Spanish (español)',
-        'th': 'Thai (ภาษาไทย)',
-        'tr': 'Turkish (Türkçe)',
-        'vi': 'Vietnamese (tiếng Việt)',
-        'pt-br': 'Brazilian Portuguese (Português Brasileiro)',
-        'zh-cn': 'Simplified Chinese (简体中文)',
-        'zh-tw': 'Traditional Chinese (繁體中文)',
-      },
-      'resetLangTitle': "Browse this site in %{targetLang}?",
-      'resetLangTextIntro': 'You requested a page in %{targetLang}, but your language preference for this site is %{lang}.',
-      'resetLangTextCta': 'Would you like to change your language preference and browse this site in %{targetLang}? ' +
-                          'If you want to change your language preference later, use the language menu at the bottom of each page.',
-      'resetLangButtonYes': 'Change Language',
-      'resetLangButtonNo': 'Not Now'
-    }
-  };
-
-  /**
-   * Current locale.
-   */
-  var locale = (function() {
-    var lang = window.readCookie('pref_lang');
-    if (lang === 0 || LANGUAGES.indexOf(lang) === -1) {
-      lang = 'en';
-    }
-    return lang;
-  })();
-  var localeTarget = (function() {
-    var localeTarget = locale;
-    if (window.devsite) {
-      if (getQueryVariable('hl')) {
-        var target = getQueryVariable('hl');
-        if (!(target === 0) || (LANGUAGES.indexOf(target) === -1)) {
-          localeTarget = target;
-        }
-      }
-    } else {
-      if (location.pathname.substring(0,6) == "/intl/") {
-        var target = location.pathname.split('/')[2];
-        if (!(target === 0) || (LANGUAGES.indexOf(target) === -1)) {
-          localeTarget = target;
-        }
-      }
-    }
-
-    return localeTarget;
-  })();
-
-  /**
-   * Global function shims for backwards compatibility
-   */
-  window.changeNavLang = function() {
-    // Already done.
-  };
-
-  window.loadLangPref = function() {
-    // Languages pref already loaded.
-  };
-
-  window.getLangPref = function() {
-    return locale;
-  };
-
-  window.getLangTarget = function() {
-    return localeTarget;
-  };
-
-  // Expose polyglot instance for advanced localization.
-  var polyglot = window.polyglot = new window.Polyglot({
-    locale: locale,
-    phrases: PHRASES
-  });
-
-  // When DOM is ready.
-  $(function() {
-    // Mark current locale in language picker.
-    $('#language').find('option[value="' + locale + '"]').attr('selected', true);
-
-    $('html').dacTranslate().on('dac:domchange', function(e) {
-      $(e.target).dacTranslate();
-    });
-  });
-
-  $.fn.dacTranslate = function() {
-    // Translate strings in template markup:
-
-    // OLD
-    // Having all translations in HTML does not scale well and bloats every page.
-    // Need to migrate this to data-l JS translations below.
-    if (locale !== 'en') {
-      var $links = this.find('a[' + locale + '-lang]');
-      $links.each(function() { // for each link with a translation
-        var $link = $(this);
-        // put the desired language from the attribute as the text
-        $link.text($link.attr(locale + '-lang'));
-      });
-    }
-
-    // NEW
-    // A simple declarative api for JS translations. Feel free to extend as appropriate.
-
-    // Miscellaneous string compilations
-    // Build full strings from localized substrings:
-    var myLocaleTarget = window.getLangTarget();
-    var myTargetLang = window.polyglot.t("newsletter.languageValTarget." + myLocaleTarget);
-    var myLang = window.polyglot.t("newsletter.languageVal");
-    var myTargetLangTitleString = window.polyglot.t("newsletter.resetLangTitle", {targetLang: myTargetLang});
-    var myResetLangTextIntro = window.polyglot.t("newsletter.resetLangTextIntro", {targetLang: myTargetLang, lang: myLang});
-    var myResetLangTextCta = window.polyglot.t("newsletter.resetLangTextCta", {targetLang: myTargetLang});
-    //var myResetLangButtonYes = window.polyglot.t("newsletter.resetLangButtonYes", {targetLang: myTargetLang});
-
-    // Inject strings as text values in dialog components:
-    $("#langform .dac-modal-header-title").text(myTargetLangTitleString);
-    $("#langform #resetLangText").text(myResetLangTextIntro);
-    $("#langform #resetLangCta").text(myResetLangTextCta);
-    //$("#resetLangButtonYes").attr("data-t", window.polyglot.t(myResetLangButtonYes));
-
-    // Text: <div data-t="nav.home"></div>
-    // HTML: <div data-t="privacy" data-t-html></html>
-    this.find('[data-t]').each(function() {
-      var el = $(this);
-      var data = el.data();
-      if (data.t) {
-        el[data.tHtml === '' ? 'html' : 'text'](polyglot.t(data.t));
-      }
-    });
-
-    return this;
-  };
-})();
-/* ##########     END LOCALIZATION     ############ */
-
-// Translations. These should eventually be moved into language-specific files and loaded on demand.
-// jshint nonbsp:false
-switch (window.getLangPref()) {
-  case 'ar':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Google Play. يمكنك الحصول على آخر الأخبار والنصائح من مطوّري تطبيقات Android، مما يساعدك ' +
-          'على تحقيق النجاح على',
-        'requiredHint': '* حقول مطلوبة',
-        'name': '. الاسم بالكامل ',
-        'email': '. عنوان البريد الإلكتروني ',
-        'company': '. اسم الشركة / اسم مطوّر البرامج',
-        'appUrl': '. أحد عناوين URL لتطبيقاتك في متجر Play',
-        'business': {
-          'label': '. ما العنصر الذي يوضح طبيعة نشاطك التجاري بدقة؟ ',
-          'apps': 'التطبيقات',
-          'games': 'الألعاب',
-          'both': 'التطبيقات والألعاب'
-        },
-        'confirmMailingList': 'إضافتي إلى القائمة البريدية للنشرة الإخبارية الشهرية والرسائل الإلكترونية التي يتم' +
-          ' إرسالها من حين لآخر بشأن التطوير وفرص Google Play.',
-        'privacyPolicy': 'أقر بأن المعلومات المقدَّمة في هذا النموذج تخضع لسياسة خصوصية ' +
-          '<a href="https://www.google.com/intl/ar/policies/privacy/" target="_blank">Google</a>.',
-        'languageVal': 'Arabic (العربيّة)',
-        'successTitle': 'رائع!',
-        'successDetails': 'لقد اشتركت بنجاح للحصول على آخر الأخبار والنصائح من مطوّري برامج Android.'
-      }
-    });
-    break;
-  case 'zh-cn':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': '获取最新的 Android 开发者资讯和提示,助您在 Google Play 上取得成功。',
-        'requiredHint': '* 必填字段',
-        'name': '全名',
-        'email': '电子邮件地址',
-        'company': '公司/开发者名称',
-        'appUrl': '您的某个 Play 商店应用网址',
-        'business': {
-          'label': '哪一项能够最准确地描述您的业务?',
-          'apps': '应用',
-          'games': '游戏',
-          'both': '应用和游戏'
-        },
-        'confirmMailingList': '将我添加到邮寄名单,以便接收每月简报以及不定期发送的关于开发和 Google Play 商机的电子邮件。',
-        'privacyPolicy': '我确认自己了解在此表单中提供的信息受 <a href="https://www.google.com/intl/zh-CN/' +
-        'policies/privacy/" target="_blank">Google</a> 隐私权政策的约束。',
-        'languageVal': 'Simplified Chinese (简体中文)',
-        'successTitle': '太棒了!',
-        'successDetails': '您已成功订阅最新的 Android 开发者资讯和提示。'
-      }
-    });
-    break;
-  case 'zh-tw':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': '獲得 Android 開發人員的最新消息和各項秘訣,讓您在 Google Play 上輕鬆邁向成功之路。',
-        'requiredHint': '* 必要欄位',
-        'name': '全名',
-        'email': '電子郵件地址',
-        'company': '公司/開發人員名稱',
-        'appUrl': '您其中一個 Play 商店應用程式的網址',
-        'business': {
-          'label': '為您的商家選取最合適的產品類別。',
-          'apps': '應用程式',
-          'games': '遊戲',
-          'both': '應用程式和遊戲'
-        },
-        'confirmMailingList': '我想加入 Google Play 的郵寄清單,以便接收每月電子報和 Google Play 不定期寄送的電子郵件,' +
-          '瞭解關於開發和 Google Play 商機的資訊。',
-        'privacyPolicy': '我瞭解,我在這張表單中提供的資訊將受到 <a href="' +
-        'https://www.google.com/intl/zh-TW/policies/privacy/" target="_blank">Google</a> 隱私權政策.',
-        'languageVal': 'Traditional Chinese (繁體中文)',
-        'successTitle': '太棒了!',
-        'successDetails': '您已經成功訂閱 Android 開發人員的最新消息和各項秘訣。'
-      }
-    });
-    break;
-  case 'fr':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Recevez les dernières actualités destinées aux développeurs Android, ainsi que des conseils qui ' +
-          'vous mèneront vers le succès sur Google Play.',
-        'requiredHint': '* Champs obligatoires',
-        'name': 'Nom complet',
-        'email': 'Adresse e-mail',
-        'company': 'Nom de la société ou du développeur',
-        'appUrl': 'Une de vos URL Play Store',
-        'business': {
-          'label': 'Quelle option décrit le mieux votre activité ?',
-          'apps': 'Applications',
-          'games': 'Jeux',
-          'both': 'Applications et jeux'
-        },
-        'confirmMailingList': 'Ajoutez-moi à la liste de diffusion de la newsletter mensuelle et tenez-moi informé ' +
-          'par des e-mails occasionnels de l\'évolution et des opportunités de Google Play.',
-        'privacyPolicy': 'Je comprends que les renseignements fournis dans ce formulaire seront soumis aux <a href="' +
-        'https://www.google.com/intl/fr/policies/privacy/" target="_blank">règles de confidentialité</a> de Google.',
-        'languageVal': 'French (français)',
-        'successTitle': 'Super !',
-        'successDetails': 'Vous êtes bien inscrit pour recevoir les actualités et les conseils destinés aux ' +
-          'développeurs Android.'
-      }
-    });
-    break;
-  case 'de':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Abonniere aktuelle Informationen und Tipps für Android-Entwickler und werde noch erfolgreicher ' +
-          'bei Google Play.',
-        'requiredHint': '* Pflichtfelder',
-        'name': 'Vollständiger Name',
-        'email': 'E-Mail-Adresse',
-        'company': 'Unternehmens-/Entwicklername',
-        'appUrl': 'Eine der URLs deiner Play Store App',
-        'business': {
-          'label': 'Welche der folgenden Kategorien beschreibt dein Unternehmen am besten?',
-          'apps': 'Apps',
-          'games': 'Spiele',
-          'both': 'Apps und Spiele'
-        },
-        'confirmMailingList': 'Meine E-Mail-Adresse soll zur Mailingliste hinzugefügt werden, damit ich den ' +
-          'monatlichen Newsletter sowie gelegentlich E-Mails zu Entwicklungen und Optionen bei Google Play erhalte.',
-        'privacyPolicy': 'Ich bestätige, dass die in diesem Formular bereitgestellten Informationen gemäß der ' +
-          '<a href="https://www.google.com/intl/de/policies/privacy/" target="_blank">Datenschutzerklärung</a> von ' +
-          'Google verwendet werden dürfen.',
-        'languageVal': 'German (Deutsch)',
-        'successTitle': 'Super!',
-        'successDetails': 'Du hast dich erfolgreich angemeldet und erhältst jetzt aktuelle Informationen und Tipps ' +
-          'für Android-Entwickler.'
-      }
-    });
-    break;
-  case 'in':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Receba as dicas e as notícias mais recentes para os desenvolvedores Android e seja bem-sucedido ' +
-        'no Google Play.',
-        'requiredHint': '* Bidang Wajib Diisi',
-        'name': 'Nama lengkap',
-        'email': 'Alamat email',
-        'company': 'Nama pengembang / perusahaan',
-        'appUrl': 'Salah satu URL aplikasi Play Store Anda',
-        'business': {
-          'label': 'Dari berikut ini, mana yang paling cocok dengan bisnis Anda?',
-          'apps': 'Aplikasi',
-          'games': 'Game',
-          'both': 'Aplikasi dan Game'
-        },
-        'confirmMailingList': 'Tambahkan saya ke milis untuk mendapatkan buletin bulanan dan email sesekali mengenai ' +
-          'perkembangan dan kesempatan yang ada di Google Play.',
-        'privacyPolicy': 'Saya memahami bahwa informasi yang diberikan dalam formulir ini tunduk pada <a href="' +
-        'https://www.google.com/intl/in/policies/privacy/" target="_blank">kebijakan privasi</a> Google.',
-        'languageVal': 'Indonesian (Bahasa)',
-        'successTitle': 'Hore!',
-        'successDetails': 'Anda berhasil mendaftar untuk kiat dan berita pengembang Android terbaru.'
-      }
-    });
-    break;
-  case 'it':
-    //window.polyglot.extend({
-    //  'newsletter': {
-    //    'title': 'Receba as dicas e as notícias mais recentes para os desenvolvedores Android e seja bem-sucedido ' +
-    //    'no Google Play.',
-    //    'requiredHint': '* Campos obrigatórios',
-    //    'name': 'Nome completo',
-    //    'email': 'Endereço de Email',
-    //    'company': 'Nome da empresa / do desenvolvedor',
-    //    'appUrl': 'URL de um dos seus apps da Play Store',
-    //    'business': {
-    //      'label': 'Qual das seguintes opções melhor descreve sua empresa?',
-    //      'apps': 'Apps',
-    //      'games': 'Jogos',
-    //      'both': 'Apps e Jogos'
-    //    },
-    //    'confirmMailingList': 'Inscreva-me na lista de e-mails para que eu receba o boletim informativo mensal, ' +
-    //    'bem como e-mails ocasionais sobre o desenvolvimento e as oportunidades do Google Play.',
-    //    'privacyPolicy': 'Reconheço que as informações fornecidas neste formulário estão sujeitas à <a href="' +
-    //    'https://www.google.com.br/policies/privacy/" target="_blank">Política de Privacidade</a> do Google.',
-    //    'languageVal': 'Italian (italiano)',
-    //    'successTitle': 'Uhu!',
-    //    'successDetails': 'Você se inscreveu para receber as notícias e as dicas mais recentes para os ' +
-    //    'desenvolvedores Android.',
-    //  }
-    //});
-    break;
-  case 'ja':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Google Play での成功に役立つ Android デベロッパー向けの最新ニュースやおすすめの情報をお届けします。',
-        'requiredHint': '* 必須',
-        'name': '氏名',
-        'email': 'メールアドレス',
-        'company': '会社名 / デベロッパー名',
-        'appUrl': 'Play ストア アプリの URL(いずれか 1 つ)',
-        'business': {
-          'label': 'お客様のビジネスに最もよく当てはまるものをお選びください。',
-          'apps': 'アプリ',
-          'games': 'ゲーム',
-          'both': 'アプリとゲーム'
-        },
-        'confirmMailingList': '開発や Google Play の最新情報に関する毎月発行のニュースレターや不定期発行のメールを受け取る',
-        'privacyPolicy': 'このフォームに入力した情報に <a href="https://www.google.com/intl/ja/policies/privacy/" ' +
-          'target="_blank">Google</a> のプライバシー ポリシーが適用',
-        'languageVal': 'Japanese (日本語)',
-        'successTitle': '完了です!',
-        'successDetails': 'Android デベロッパー向けの最新ニュースやおすすめの情報の配信登録が完了しました。'
-      }
-    });
-    break;
-  case 'ko':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Google Play에서 성공을 거두는 데 도움이 되는 최신 Android 개발자 소식 및 도움말을 받아 보세요.',
-        'requiredHint': '* 필수 입력란',
-        'name': '이름',
-        'email': '이메일 주소',
-        'company': '회사/개발자 이름',
-        'appUrl': 'Play 스토어 앱 URL 중 1개',
-        'business': {
-          'label': '다음 중 내 비즈니스를 가장 잘 설명하는 단어는 무엇인가요?',
-          'apps': '앱',
-          'games': '게임',
-          'both': '앱 및 게임'
-        },
-        'confirmMailingList': '개발 및 Google Play 관련 소식에 관한 월별 뉴스레터 및 비정기 이메일을 받아보겠습니다.',
-        'privacyPolicy': '이 양식에 제공한 정보는 <a href="https://www.google.com/intl/ko/policies/privacy/" ' +
-          'target="_blank">Google의</a> 개인정보취급방침에 따라 사용됨을',
-        'languageVal':'Korean (한국어)',
-        'successTitle': '축하합니다!',
-        'successDetails': '최신 Android 개발자 뉴스 및 도움말을 받아볼 수 있도록 가입을 완료했습니다.'
-      }
-    });
-    break;
-  case 'pt-br':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Receba as dicas e as notícias mais recentes para os desenvolvedores Android e seja bem-sucedido ' +
-        'no Google Play.',
-        'requiredHint': '* Campos obrigatórios',
-        'name': 'Nome completo',
-        'email': 'Endereço de Email',
-        'company': 'Nome da empresa / do desenvolvedor',
-        'appUrl': 'URL de um dos seus apps da Play Store',
-        'business': {
-          'label': 'Qual das seguintes opções melhor descreve sua empresa?',
-          'apps': 'Apps',
-          'games': 'Jogos',
-          'both': 'Apps e Jogos'
-        },
-        'confirmMailingList': 'Inscreva-me na lista de e-mails para que eu receba o boletim informativo mensal, ' +
-        'bem como e-mails ocasionais sobre o desenvolvimento e as oportunidades do Google Play.',
-        'privacyPolicy': 'Reconheço que as informações fornecidas neste formulário estão sujeitas à <a href="' +
-        'https://www.google.com.br/policies/privacy/" target="_blank">Política de Privacidade</a> do Google.',
-        'languageVal': 'Brazilian Portuguese (Português Brasileiro)',
-        'successTitle': 'Uhu!',
-        'successDetails': 'Você se inscreveu para receber as notícias e as dicas mais recentes para os ' +
-        'desenvolvedores Android.'
-      }
-    });
-    break;
-  case 'ru':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Хотите получать последние новости и советы для разработчиков Google Play? Заполните эту форму.',
-        'requiredHint': '* Обязательные поля',
-        'name': 'Полное имя',
-        'email': 'Адрес электронной почты',
-        'company': 'Название компании или имя разработчика',
-        'appUrl': 'Ссылка на любое ваше приложение в Google Play',
-        'business': {
-          'label': 'Что вы создаете?',
-          'apps': 'Приложения',
-          'games': 'Игры',
-          'both': 'Игры и приложения'
-        },
-        'confirmMailingList': 'Я хочу получать ежемесячную рассылку для разработчиков и другие полезные новости ' +
-          'Google Play.',
-        'privacyPolicy': 'Я предоставляю эти данные в соответствии с <a href="' +
-          'https://www.google.com/intl/ru/policies/privacy/" target="_blank">Политикой конфиденциальности</a> Google.',
-        'languageVal': 'Russian (Русский)',
-        'successTitle': 'Поздравляем!',
-        'successDetails': 'Теперь вы подписаны на последние новости и советы для разработчиков Android.'
-      }
-    });
-    break;
-  case 'es':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Recibe las últimas noticias y sugerencias para programadores de Android y logra tener éxito en ' +
-          'Google Play.',
-        'requiredHint': '* Campos obligatorios',
-        'name': 'Dirección de correo electrónico',
-        'email': 'Endereço de Email',
-        'company': 'Nombre de la empresa o del programador',
-        'appUrl': 'URL de una de tus aplicaciones de Play Store',
-        'business': {
-          'label': '¿Qué describe mejor a tu empresa?',
-          'apps': 'Aplicaciones',
-          'games': 'Juegos',
-          'both': 'Juegos y aplicaciones'
-        },
-        'confirmMailingList': 'Deseo unirme a la lista de distribución para recibir el boletín informativo mensual ' +
-          'y correos electrónicos ocasionales sobre desarrollo y oportunidades de Google Play.',
-        'privacyPolicy': 'Acepto que la información que proporcioné en este formulario cumple con la <a href="' +
-        'https://www.google.com/intl/es/policies/privacy/" target="_blank">política de privacidad</a> de Google.',
-        'languageVal': 'Spanish (español)',
-        'successTitle': '¡Felicitaciones!',
-        'successDetails': 'El registro para recibir las últimas noticias y sugerencias para programadores de Android ' +
-          'se realizó correctamente.'
-      }
-    });
-    break;
-  case 'th':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'รับข่าวสารล่าสุดสำหรับนักพัฒนาซอฟต์แวร์ Android ตลอดจนเคล็ดลับที่จะช่วยให้คุณประสบความสำเร็จบน ' +
-          'Google Play',
-        'requiredHint': '* ช่องที่ต้องกรอก',
-        'name': 'ชื่อและนามสกุล',
-        'email': 'ที่อยู่อีเมล',
-        'company': 'ชื่อบริษัท/นักพัฒนาซอฟต์แวร์',
-        'appUrl': 'URL แอปใดแอปหนึ่งของคุณใน Play สโตร์',
-        'business': {
-          'label': 'ข้อใดตรงกับธุรกิจของคุณมากที่สุด',
-          'apps': 'แอป',
-          'games': 'เกม',
-          'both': 'แอปและเกม'
-        },
-        'confirmMailingList': 'เพิ่มฉันลงในรายชื่ออีเมลเพื่อรับจดหมายข่าวรายเดือนและอีเมลเป็นครั้งคราวเกี่ยวกับก' +
-          'ารพัฒนาซอฟต์แวร์และโอกาสใน Google Play',
-        'privacyPolicy': 'ฉันรับทราบว่าข้อมูลที่ให้ไว้ในแบบฟอร์มนี้จะเป็นไปตามนโยบายส่วนบุคคลของ ' +
-          '<a href="https://www.google.com/intl/th/policies/privacy/" target="_blank">Google</a>',
-        'languageVal': 'Thai (ภาษาไทย)',
-        'successTitle': 'ไชโย!',
-        'successDetails': 'คุณลงชื่อสมัครรับข่าวสารและเคล็ดลับล่าสุดสำหรับนักพัฒนาซอฟต์แวร์ Android เสร็จเรียบร้อยแล้ว'
-      }
-    });
-    break;
-  case 'tr':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Google Play\'de başarılı olmanıza yardımcı olacak en son Android geliştirici haberleri ve ipuçları.',
-        'requiredHint': '* Zorunlu Alanlar',
-        'name': 'Tam ad',
-        'email': 'E-posta adresi',
-        'company': 'Şirket / geliştirici adı',
-        'appUrl': 'Play Store uygulama URL\'lerinizden biri',
-        'business': {
-          'label': 'İşletmenizi en iyi hangisi tanımlar?',
-          'apps': 'Uygulamalar',
-          'games': 'Oyunlar',
-          'both': 'Uygulamalar ve Oyunlar'
-        },
-        'confirmMailingList': 'Beni, geliştirme ve Google Play fırsatlarıyla ilgili ara sıra gönderilen e-posta ' +
-          'iletilerine ilişkin posta listesine ve aylık haber bültenine ekle.',
-        'privacyPolicy': 'Bu formda sağlanan bilgilerin Google\'ın ' +
-          '<a href="https://www.google.com/intl/tr/policies/privacy/" target="_blank">Gizlilik Politikası\'na</a> ' +
-          'tabi olacağını kabul ediyorum.',
-        'languageVal': 'Turkish (Türkçe)',
-        'successTitle': 'Yaşasın!',
-        'successDetails': 'En son Android geliştirici haberleri ve ipuçlarına başarıyla kaydoldunuz.'
-      }
-    });
-    break;
-  case 'vi':
-    window.polyglot.extend({
-      'newsletter': {
-        'title': 'Nhận tin tức và mẹo mới nhất dành cho nhà phát triển Android sẽ giúp bạn tìm thấy thành công trên ' +
-          'Google Play.',
-        'requiredHint': '* Các trường bắt buộc',
-        'name': 'Tên đầy đủ',
-        'email': 'Địa chỉ email',
-        'company': 'Tên công ty/nhà phát triển',
-        'appUrl': 'Một trong số các URL ứng dụng trên cửa hàng Play của bạn',
-        'business': {
-          'label': 'Lựa chọn nào sau đây mô tả chính xác nhất doanh nghiệp của bạn?',
-          'apps': 'Ứng dụng',
-          'games': 'Trò chơi',
-          'both': 'Ứng dụng và trò chơi'
-        },
-        'confirmMailingList': 'Thêm tôi vào danh sách gửi thư cho bản tin hàng tháng và email định kỳ về việc phát ' +
-          'triển và cơ hội của Google Play.',
-        'privacyPolicy': 'Tôi xác nhận rằng thông tin được cung cấp trong biểu mẫu này tuân thủ chính sách bảo mật ' +
-          'của <a href="https://www.google.com/intl/vi/policies/privacy/" target="_blank">Google</a>.',
-        'languageVal': 'Vietnamese (tiếng Việt)',
-        'successTitle': 'Thật tuyệt!',
-        'successDetails': 'Bạn đã đăng ký thành công nhận tin tức và mẹo mới nhất dành cho nhà phát triển của Android.'
-      }
-    });
-    break;
-}
-
 (function($) {
   'use strict';
 
   function Modal(el, options) {
     this.el = $(el);
-    this.options = $.extend({}, options);
+    this.options = $.extend({}, ToggleModal.DEFAULTS_, options);
     this.isOpen = false;
 
     this.el.on('click', function(event) {
-      if (!$.contains(this.el.find('.dac-modal-window')[0], event.target)) {
+      if (!$.contains($('.dac-modal-window')[0], event.target)) {
         return this.el.trigger('modal-close');
       }
     }.bind(this));
@@ -3915,15 +4957,20 @@
     this.isOpen = true;
   };
 
-  function onClickToggleModal(event) {
-    event.preventDefault();
-    var toggle = $(event.currentTarget);
-    var options = toggle.data();
-    var modal = options.modalToggle ? $('[data-modal="' + options.modalToggle + '"]') :
-      toggle.closest('[data-modal]');
-    modal.trigger('modal-toggle');
+  function ToggleModal(el, options) {
+    this.el = $(el);
+    this.options = $.extend({}, ToggleModal.DEFAULTS_, options);
+    this.modal = this.options.modalToggle ? $('[data-modal="' + this.options.modalToggle + '"]') :
+      this.el.closest('[data-modal]');
+
+    this.el.on('click', this.clickHandler_.bind(this));
   }
 
+  ToggleModal.prototype.clickHandler_ = function(event) {
+    event.preventDefault();
+    this.modal.trigger('modal-toggle');
+  };
+
   /**
    * jQuery plugin
    * @param  {object} options - Override default options.
@@ -3948,626 +4995,71 @@
       $(this).dacModal($(this).data());
     });
 
-    $('html').on('click.modal', '[data-modal-toggle]', onClickToggleModal);
-
-    // Check if url anchor is targetting a toggle to open the modal.
-    if (location.hash) {
-      $(location.hash + '[data-modal-toggle]').trigger('click');
-    }
-
-    if (window.getLangTarget() !== window.getLangPref()) {
-          $('#langform').trigger('modal-open');
-          $("#langform button.yes").attr("onclick","window.changeLangPref('" + window.getLangTarget() + "', true);  return false;");
-          $("#langform button.no").attr("onclick","window.changeLangPref('" + window.getLangPref() + "', true); return false;");
-    }
+    $('[data-modal-toggle]').each(function() {
+      $(this).dacToggleModal($(this).data());
+    });
   });
 })(jQuery);
 
-/* Fullscreen - Toggle fullscreen mode for reference pages */
-(function($) {
-  'use strict';
-
-  /**
-   * @param {HTMLElement} el - The DOM element.
-   * @constructor
-   */
-  function Fullscreen(el) {
-    this.el = $(el);
-    this.html = $('html');
-    this.icon = this.el.find('.dac-sprite');
-    this.isFullscreen = window.readCookie(Fullscreen.COOKIE_) === 'true';
-    this.activate_();
-    this.el.on('click.dac-fullscreen', this.toggleHandler_.bind(this));
-  }
-
-  /**
-   * Cookie name for storing the state
-   * @type {string}
-   * @private
-   */
-  Fullscreen.COOKIE_ = 'fullscreen';
-
-  /**
-   * Classes to modify the DOM
-   * @type {{mode: string, fullscreen: string, fullscreenExit: string}}
-   * @private
-   */
-  Fullscreen.CLASSES_ = {
-    mode: 'dac-fullscreen-mode',
-    fullscreen: 'dac-fullscreen',
-    fullscreenExit: 'dac-fullscreen-exit'
-  };
-
-  /**
-   * Event listener for toggling fullscreen mode
-   * @param {MouseEvent} event
-   * @private
-   */
-  Fullscreen.prototype.toggleHandler_ = function(event) {
-    event.stopPropagation();
-    this.toggle(!this.isFullscreen, true);
-  };
-
-  /**
-   * Change the DOM based on current state.
-   * @private
-   */
-  Fullscreen.prototype.activate_ = function() {
-    this.icon.toggleClass(Fullscreen.CLASSES_.fullscreen, !this.isFullscreen);
-    this.icon.toggleClass(Fullscreen.CLASSES_.fullscreenExit, this.isFullscreen);
-    this.html.toggleClass(Fullscreen.CLASSES_.mode, this.isFullscreen);
-  };
-
-  /**
-   * Toggle fullscreen mode and store the state in a cookie.
-   */
-  Fullscreen.prototype.toggle = function() {
-    this.isFullscreen = !this.isFullscreen;
-    window.writeCookie(Fullscreen.COOKIE_, this.isFullscreen, null);
-    this.activate_();
-  };
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacFullscreen = function() {
-    return this.each(function() {
-      new Fullscreen($(this));
-    });
-  };
-})(jQuery);
-
-(function($) {
-  'use strict';
-
-  /**
-   * @param {HTMLElement} selected - The link that is selected in the nav.
-   * @constructor
-   */
-  function HeaderTabs(selected) {
-
-    // Don't highlight any tabs on the index page
-    if (location.pathname === '/index.html' || location.pathname === '/') {
-      //return;
-    }
-
-    this.selected = $(selected);
-    this.selectedParent = this.selected.closest('.dac-nav-secondary').siblings('a');
-    this.links = $('.dac-header-tabs a');
-
-    this.selectActiveTab();
-  }
-
-  HeaderTabs.prototype.selectActiveTab = function() {
-    var section = null;
-
-    if (this.selectedParent.length) {
-      section = this.selectedParent.text();
-    } else {
-      section = this.selected.text();
-    }
-
-    if (section) {
-      this.links.removeClass('selected');
-
-      this.links.filter(function() {
-        return $(this).text() === $.trim(section);
-      }).addClass('selected');
-    }
-  };
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacHeaderTabs = function() {
-    return this.each(function() {
-      new HeaderTabs(this);
-    });
-  };
-})(jQuery);
-
-(function($) {
-  'use strict';
-  var icon = $('<i/>').addClass('dac-sprite dac-nav-forward');
-  var config = JSON.parse(window.localStorage.getItem('global-navigation') || '{}');
-  var forwardLink = $('<span/>')
-    .addClass('dac-nav-link-forward')
-    .html(icon)
-    .on('click', swap_);
-
-  /**
-   * @constructor
-   */
-  function Nav(navigation) {
-    $('.dac-nav-list').dacCurrentPage().dacHeaderTabs().dacSidebarToggle($('body'));
-
-    navigation.find('[data-reference-tree]').dacReferenceNav();
-
-    setupViews_(navigation.children().eq(0).children());
-
-    initCollapsedNavs(navigation.find('.dac-nav-sub-slider'));
-
-    $('#dac-main-navigation').scrollIntoView('.selected')
-  }
-
-  function updateStore(icon) {
-    var navClass = getCurrentLandingPage_(icon);
-    var isExpanded = icon.hasClass('dac-expand-less-black');
-    var expandedNavs = config.expanded || [];
-    if (isExpanded) {
-      expandedNavs.push(navClass);
-    } else {
-      expandedNavs = expandedNavs.filter(function(item) {
-        return item !== navClass;
-      });
-    }
-    config.expanded = expandedNavs;
-    window.localStorage.setItem('global-navigation', JSON.stringify(config));
-  }
-
-  function toggleSubNav_(icon) {
-    var isExpanded = icon.hasClass('dac-expand-less-black');
-    icon.toggleClass('dac-expand-less-black', !isExpanded);
-    icon.toggleClass('dac-expand-more-black', isExpanded);
-    icon.data('sub-navigation.dac').slideToggle(200);
-
-    updateStore(icon);
-  }
-
-  function handleSubNavToggle_(event) {
-    event.preventDefault();
-    var icon = $(event.target);
-    toggleSubNav_(icon);
-  }
-
-  function getCurrentLandingPage_(icon) {
-    return icon.closest('li')[0].className.replace('dac-nav-item ', '');
-  }
-
-  // Setup sub navigation collapse/expand
-  function initCollapsedNavs(toggleIcons) {
-    toggleIcons.each(setInitiallyActive_($('body')));
-    toggleIcons.on('click', handleSubNavToggle_);
-
-  }
-
-  function setInitiallyActive_(body) {
-    var expandedNavs = config.expanded || [];
-    return function(i, icon) {
-      icon = $(icon);
-      var subNav = icon.next();
-
-      if (!subNav.length) {
-        return;
-      }
-
-      var landingPageClass = getCurrentLandingPage_(icon);
-      var expanded = expandedNavs.indexOf(landingPageClass) >= 0;
-      landingPageClass = landingPageClass === 'home' ? 'about' : landingPageClass;
-
-      // TODO: Should read from localStorage
-      var visible = body.hasClass(landingPageClass) || expanded;
-
-      icon.data('sub-navigation.dac', subNav);
-      icon.toggleClass('dac-expand-less-black', visible);
-      icon.toggleClass('dac-expand-more-black', !visible);
-      subNav.toggle(visible);
-    };
-  }
-
-  function setupViews_(views) {
-    if (views.length === 1) {
-      // Active tier 1 nav.
-      views.addClass('dac-active');
-    } else {
-      // Activate back button and tier 2 nav.
-      views.slice(0, 2).addClass('dac-active');
-      var selectedNav = views.eq(2).find('.selected').after(forwardLink);
-      var langAttr = selectedNav.attr(window.getLangPref() + '-lang');
-      //form the label from locale attr if possible, else set to selectedNav text value
-      if ((typeof langAttr !== typeof undefined &&  langAttr !== false) && (langAttr !== '')) {
-        $('.dac-nav-back-title').text(langAttr);
-      } else {
-        $('.dac-nav-back-title').text(selectedNav.text());
-      }
-    }
-
-    // Navigation should animate.
-    setTimeout(function() {
-      views.removeClass('dac-no-anim');
-    }, 10);
-  }
-
-  function swap_(event) {
-    event.preventDefault();
-    $(event.currentTarget).trigger('swap-content');
-  }
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacNav = function() {
-    return this.each(function() {
-      new Nav($(this));
-    });
-  };
-})(jQuery);
-
-/* global NAVTREE_DATA */
-(function($) {
-  /**
-   * Build the reference navigation with namespace dropdowns.
-   * @param {jQuery} el - The DOM element.
-   */
-  function buildReferenceNav(el) {
-    var namespaceList = el.find('[data-reference-namespaces]');
-    var resources = el.find('[data-reference-resources]');
-    var selected = namespaceList.find('.selected');
-
-    // Links should be toggleable.
-    namespaceList.find('a').addClass('dac-reference-nav-toggle dac-closed');
-
-    // Load in all resources
-    $.getScript('/navtree_data.js', function(data, textStatus, xhr) {
-      if (xhr.status === 200) {
-        namespaceList.on('click', 'a.dac-reference-nav-toggle', toggleResourcesHandler);
-      }
-    });
-
-    // No setup required if no resources are present
-    if (!resources.length) {
-      return;
-    }
-
-    // The resources should be a part of selected namespace.
-    var overview = addResourcesToView(resources, selected);
-
-    // Currently viewing Overview
-    if (location.pathname === overview.attr('href')) {
-      overview.parent().addClass('selected');
-    }
-
-    // Open currently selected resource
-    var listsToOpen = selected.children().eq(1);
-    listsToOpen = listsToOpen.add(listsToOpen.find('.selected').parent()).show();
-
-    // Mark dropdowns as open
-    listsToOpen.prev().removeClass('dac-closed');
-
-    // Scroll into view
-    namespaceList.scrollIntoView(selected);
-  }
-
-  /**
-   * Handles the toggling of resources.
-   * @param {Event} event
-   */
-  function toggleResourcesHandler(event) {
-    event.preventDefault();
-    var el = $(this);
-
-    // If resources for given namespace is not present, fetch correct data.
-    if (this.tagName === 'A' && !this.hasResources) {
-      addResourcesToView(buildResourcesViewForData(getDataForNamespace(el.text())), el.parent());
-    }
-
-    el.toggleClass('dac-closed').next().slideToggle(200);
-  }
-
-  /**
-   * @param {String} namespace
-   * @returns {Array} namespace data
-   */
-  function getDataForNamespace(namespace) {
-    var namespaceData = NAVTREE_DATA.filter(function(data) {
-      return data[0] === namespace;
-    });
-
-    return namespaceData.length ? namespaceData[0][2] : [];
-  }
-
-  /**
-   * Build a list item for a resource
-   * @param {Array} resource
-   * @returns {String}
-   */
-  function buildResourceItem(resource) {
-    return '<li class="api apilevel-' + resource[3] + '"><a href="/' + resource[1] + '">' + resource[0] + '</a></li>';
-  }
-
-  /**
-   * Build resources list items.
-   * @param {Array} resources
-   * @returns {String}
-   */
-  function buildResourceList(resources) {
-    return '<li><h2>' + resources[0] + '</h2><ul>' + resources[2].map(buildResourceItem).join('') + '</ul>';
-  }
-
-  /**
-   * Build a resources view
-   * @param {Array} data
-   * @returns {jQuery} resources in an unordered list.
-   */
-  function buildResourcesViewForData(data) {
-    return $('<ul>' + data.map(buildResourceList).join('') + '</ul>');
-  }
-
-  /**
-   * Add resources to a containing view.
-   * @param {jQuery} resources
-   * @param {jQuery} view
-   * @returns {jQuery} the overview link.
-   */
-  function addResourcesToView(resources, view) {
-    var namespace = view.children().eq(0);
-    var overview = $('<a href="' + namespace.attr('href') + '">Overview</a>');
-
-    // Mark namespace with content;
-    namespace[0].hasResources = true;
-
-    // Add correct classes / event listeners to resources.
-    resources.prepend($('<li>').html(overview))
-      .find('a')
-        .addClass('dac-reference-nav-resource')
-      .end()
-        .find('h2')
-        .addClass('dac-reference-nav-toggle dac-closed')
-        .on('click', toggleResourcesHandler)
-      .end()
-        .add(resources.find('ul'))
-        .addClass('dac-reference-nav-resources')
-      .end()
-        .appendTo(view);
-
-    return overview;
-  }
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacReferenceNav = function() {
-    return this.each(function() {
-      buildReferenceNav($(this));
-    });
-  };
-})(jQuery);
-
-/** Scroll a container to make a target element visible
- This is called when the page finished loading. */
-$.fn.scrollIntoView = function(target) {
-  if ('string' === typeof target) {
-    target = this.find(target);
-  }
-  if (this.is(':visible')) {
-    if (target.length == 0) {
-      // If no selected item found, exit
-      return;
-    }
-
-    // get the target element's offset from its container nav by measuring the element's offset
-    // relative to the document then subtract the container nav's offset relative to the document
-    var targetOffset = target.offset().top - this.offset().top;
-    var containerHeight = this.height();
-    if (targetOffset > containerHeight * .8) { // multiply nav height by .8 so we move up the item
-      // if it's more than 80% down the nav
-      // scroll the item up by an amount equal to 80% the container height
-      this.scrollTop(targetOffset - (containerHeight * .8));
-    }
-  }
-};
-
-(function($) {
-  $.fn.dacCurrentPage = function() {
-    // Highlight the header tabs...
-    // highlight Design tab
-    var baseurl = getBaseUri(window.location.pathname);
-    var urlSegments = baseurl.split('/');
-    var navEl = this;
-    var body = $('body');
-    var subNavEl = navEl.find('.dac-nav-secondary');
-    var parentNavEl;
-    var selected;
-    // In NDK docs, highlight appropriate sub-nav
-    if (body.hasClass('ndk')) {
-      if (body.hasClass('guide')) {
-        selected = navEl.find('> li.guides > a').addClass('selected');
-      } else if (body.hasClass('reference')) {
-        selected = navEl.find('> li.reference > a').addClass('selected');
-      } else if (body.hasClass('samples')) {
-        selected = navEl.find('> li.samples > a').addClass('selected');
-      } else if (body.hasClass('downloads')) {
-        selected = navEl.find('> li.downloads > a').addClass('selected');
-      }
-    } else if (body.hasClass('studio')) {
-      if (body.hasClass('features')) {
-        selected = navEl.find('> li.features > a').addClass('selected');
-      } else if (body.hasClass('guide')) {
-        selected = navEl.find('> li.guide > a').addClass('selected');
-      } else if (body.hasClass('preview')) {
-        selected = navEl.find('> li.preview > a').addClass('selected');
-      }
-    } else if (body.hasClass('design')) {
-      selected = navEl.find('> li.design > a').addClass('selected');
-      // highlight Home nav
-    } else if (body.hasClass('about')) {
-      parentNavEl = navEl.find('> li.home > a');
-      parentNavEl.addClass('has-subnav');
-      // In Home docs, also highlight appropriate sub-nav
-      if (urlSegments[1] === 'wear' || urlSegments[1] === 'tv' ||
-        urlSegments[1] === 'auto') {
-        selected = subNavEl.find('li.' + urlSegments[1] + ' > a').addClass('selected');
-      } else if (urlSegments[1] === 'about') {
-        selected = subNavEl.find('li.versions > a').addClass('selected');
-      } else {
-        selected = parentNavEl.removeClass('has-subnav').addClass('selected');
-      }
-      // highlight Develop nav
-    } else if (body.hasClass('develop') || body.hasClass('google')) {
-      parentNavEl = navEl.find('> li.develop > a');
-      parentNavEl.addClass('has-subnav');
-      // In Develop docs, also highlight appropriate sub-nav
-      if (urlSegments[1] === 'training') {
-        selected = subNavEl.find('li.training > a').addClass('selected');
-      } else if (urlSegments[1] === 'guide') {
-        selected = subNavEl.find('li.guide > a').addClass('selected');
-      } else if (urlSegments[1] === 'reference') {
-        // If the root is reference, but page is also part of Google Services, select Google
-        if (body.hasClass('google')) {
-          selected = subNavEl.find('li.google > a').addClass('selected');
-        } else {
-          selected = subNavEl.find('li.reference > a').addClass('selected');
-        }
-      } else if ((urlSegments[1] === 'tools') || (urlSegments[1] === 'sdk')) {
-        selected = subNavEl.find('li.tools > a').addClass('selected');
-      } else if (body.hasClass('google')) {
-        selected = subNavEl.find('li.google > a').addClass('selected');
-      } else if (body.hasClass('samples')) {
-        selected = subNavEl.find('li.samples > a').addClass('selected');
-      } else {
-        selected = parentNavEl.removeClass('has-subnav').addClass('selected');
-      }
-      // highlight Distribute nav
-    } else if (body.hasClass('distribute')) {
-      parentNavEl = navEl.find('> li.distribute > a');
-      parentNavEl.addClass('has-subnav');
-      // In Distribute docs, also highlight appropriate sub-nav
-      if (urlSegments[2] === 'users') {
-        selected = subNavEl.find('li.users > a').addClass('selected');
-      } else if (urlSegments[2] === 'engage') {
-        selected = subNavEl.find('li.engage > a').addClass('selected');
-      } else if (urlSegments[2] === 'monetize') {
-        selected = subNavEl.find('li.monetize > a').addClass('selected');
-      } else if (urlSegments[2] === 'analyze') {
-        selected = subNavEl.find('li.analyze > a').addClass('selected');
-      } else if (urlSegments[2] === 'tools') {
-        selected = subNavEl.find('li.disttools > a').addClass('selected');
-      } else if (urlSegments[2] === 'stories') {
-        selected = subNavEl.find('li.stories > a').addClass('selected');
-      } else if (urlSegments[2] === 'essentials') {
-        selected = subNavEl.find('li.essentials > a').addClass('selected');
-      } else if (urlSegments[2] === 'googleplay') {
-        selected = subNavEl.find('li.googleplay > a').addClass('selected');
-      } else {
-        selected = parentNavEl.removeClass('has-subnav').addClass('selected');
-      }
-    } else if (body.hasClass('preview')) {
-      selected = navEl.find('> li.preview > a').addClass('selected');
-    }
-    return $(selected);
-  };
-})(jQuery);
-
 (function($) {
   'use strict';
 
   /**
    * Toggle the visabilty of the mobile navigation.
    * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
+   * @param options
    * @constructor
    */
   function ToggleNav(el, options) {
     this.el = $(el);
     this.options = $.extend({}, ToggleNav.DEFAULTS_, options);
-    this.body = $(document.body);
-    this.navigation_ = this.body.find(this.options.navigation);
+    this.options.target = [this.options.navigation];
+
+    if (this.options.body) {this.options.target.push('body')}
+    if (this.options.dimmer) {this.options.target.push(this.options.dimmer)}
+
     this.el.on('click', this.clickHandler_.bind(this));
   }
 
-  ToggleNav.BREAKPOINT_ = 980;
-
-  /**
-   * Open on correct sizes
-   */
-  function toggleSidebarVisibility(body) {
-    var wasClosed = ('' + localStorage.getItem('navigation-open')) === 'false';
-
-    if (wasClosed) {
-      body.removeClass(ToggleNav.DEFAULTS_.activeClass);
-    } else if (window.innerWidth >= ToggleNav.BREAKPOINT_) {
-      body.addClass(ToggleNav.DEFAULTS_.activeClass);
-    } else {
-      body.removeClass(ToggleNav.DEFAULTS_.activeClass);
-    }
-  }
-
   /**
    * ToggleNav Default Settings
-   * @type {{body: boolean, dimmer: string, navigation: string, activeClass: string}}
+   * @type {{body: boolean, dimmer: string, navigation: string, toggleClass: string}}
    * @private
    */
   ToggleNav.DEFAULTS_ = {
     body: true,
     dimmer: '.dac-nav-dimmer',
-    animatingClass: 'dac-nav-animating',
     navigation: '[data-dac-nav]',
-    activeClass: 'dac-nav-open'
+    toggleClass: 'dac-nav-open'
   };
 
   /**
    * The actual toggle logic.
-   * @param {Event} event
+   * @param event
    * @private
    */
   ToggleNav.prototype.clickHandler_ = function(event) {
     event.preventDefault();
-    var animatingClass = this.options.animatingClass;
-    var body = this.body;
-
-    body.addClass(animatingClass);
-    body.toggleClass(this.options.activeClass);
-
-    setTimeout(function() {
-      body.removeClass(animatingClass);
-    }, this.navigation_.transitionDuration());
-
-    if (window.innerWidth >= ToggleNav.BREAKPOINT_) {
-      localStorage.setItem('navigation-open', body.hasClass(this.options.activeClass));
-    }
+    $(this.options.target.join(', ')).toggleClass(this.options.toggleClass);
   };
 
   /**
    * jQuery plugin
    * @param  {object} options - Override default options.
    */
-  $.fn.dacToggleMobileNav = function() {
+  $.fn.dacToggleMobileNav = function(options) {
     return this.each(function() {
-      var el = $(this);
-      new ToggleNav(el, el.data());
+      new ToggleNav(this, options);
     });
   };
 
-  $.fn.dacSidebarToggle = function(body) {
-    toggleSidebarVisibility(body);
-    $(window).on('resize', toggleSidebarVisibility.bind(null, body));
-  };
-
   /**
    * Data Attribute API
    */
-  $(function() {
-    $('[data-dac-toggle-nav]').dacToggleMobileNav();
+  $(window).on('load.aranja', function() {
+    $('[data-dac-toggle-nav]').each(function() {
+      $(this).dacToggleMobileNav($(this).data());
+    });
   });
 })(jQuery);
 
@@ -4586,7 +5078,6 @@
       .attr('name', 'dac-newsletter-iframe')
       .attr('src', '')
       .insertBefore(this.form);
-    this.el.find('[data-newsletter-language]').val(window.polyglot.t('newsletter.languageVal'));
     this.form.on('submit', this.submitHandler_.bind(this));
   }
 
@@ -4645,613 +5136,6 @@
   });
 })(jQuery);
 
-/* globals METADATA, YOUTUBE_RESOURCES, BLOGGER_RESOURCES */
-window.metadata = {};
-
-/**
- * Prepare metadata and indices for querying.
- */
-window.metadata.prepare = (function() {
-  // Helper functions.
-  function mergeArrays() {
-    return Array.prototype.concat.apply([], arguments);
-  }
-
-  /**
-   * Creates lookup maps for a resource index.
-   * I.e. where MAP['some tag'][resource.id] === true when that resource has 'some tag'.
-   * @param resourceDict
-   * @returns {{}}
-   */
-  function buildResourceLookupMap(resourceDict) {
-    var map = {};
-    for (var key in resourceDict) {
-      var dictForKey = {};
-      var srcArr = resourceDict[key];
-      for (var i = 0; i < srcArr.length; i++) {
-        dictForKey[srcArr[i].index] = true;
-      }
-      map[key] = dictForKey;
-    }
-    return map;
-  }
-
-  /**
-   * Merges metadata maps for english and the current language into the global store.
-   */
-  function mergeMetadataMap(name, locale) {
-    if (locale && locale !== 'en' && METADATA[locale]) {
-      METADATA[name] = $.extend(METADATA.en[name], METADATA[locale][name]);
-    } else {
-      METADATA[name] = METADATA.en[name];
-    }
-  }
-
-  /**
-   * Index all resources by type, url, tag and category.
-   * @param resources
-   */
-  function createIndices(resources) {
-    // URL, type, tag and category lookups
-    var byType = METADATA.byType = {};
-    var byUrl = METADATA.byUrl = {};
-    var byTag = METADATA.byTag = {};
-    var byCategory = METADATA.byCategory = {};
-
-    for (var i = 0; i < resources.length; i++) {
-      var res = resources[i];
-
-      // Store index.
-      res.index = i;
-
-      // Index by type.
-      var type = res.type;
-      if (type) {
-        byType[type] = byType[type] || [];
-        byType[type].push(res);
-      }
-
-      // Index by tag.
-      var tags = res.tags || [];
-      for (var j = 0; j < tags.length; j++) {
-        var tag = tags[j];
-        if (tag) {
-          byTag[tag] = byTag[tag] || [];
-          byTag[tag].push(res);
-        }
-      }
-
-      // Index by category.
-      var category = res.category;
-      if (category) {
-        byCategory[category] = byCategory[category] || [];
-        byCategory[category].push(res);
-      }
-
-      // Index by url.
-      var url = res.url;
-      if (url) {
-        res.baseUrl = url.replace(/^intl\/\w+[\/]/, '');
-        byUrl[res.baseUrl] = res;
-      }
-    }
-    METADATA.hasType = buildResourceLookupMap(byType);
-    METADATA.hasTag = buildResourceLookupMap(byTag);
-    METADATA.hasCategory = buildResourceLookupMap(byCategory);
-  }
-
-  return function() {
-    // Only once.
-    if (METADATA.all) { return; }
-
-    // Get current language.
-    var locale = getLangPref();
-
-    // Merge english resources.
-    METADATA.all = mergeArrays(
-      METADATA.en.about,
-      METADATA.en.design,
-      METADATA.en.distribute,
-      METADATA.en.develop,
-      YOUTUBE_RESOURCES,
-      BLOGGER_RESOURCES,
-      METADATA.en.extras
-    );
-
-    // Merge local language resources.
-    if (locale !== 'en' && METADATA[locale]) {
-      METADATA.all = mergeArrays(
-        METADATA.all,
-        METADATA[locale].about,
-        METADATA[locale].design,
-        METADATA[locale].distribute,
-        METADATA[locale].develop,
-        METADATA[locale].extras
-      );
-    }
-
-    mergeMetadataMap('collections', locale);
-    mergeMetadataMap('searchHeroCollections', locale);
-    mergeMetadataMap('carousel', locale);
-
-    // Create query indicies for resources.
-    createIndices(METADATA.all, locale);
-
-    // Reference metadata.
-    METADATA.androidReference = window.DATA;
-    METADATA.googleReference = mergeArrays(window.GMS_DATA, window.GCM_DATA);
-  };
-})();
-
-/* global METADATA, util */
-window.metadata.query = (function($) {
-  var pageMap = {};
-
-  function buildResourceList(opts) {
-    window.metadata.prepare();
-    var expressions = parseResourceQuery(opts.query || '');
-    var instanceMap = {};
-    var results = [];
-
-    for (var i = 0; i < expressions.length; i++) {
-      var clauses = expressions[i];
-
-      // Get all resources for first clause
-      var resources = getResourcesForClause(clauses.shift());
-
-      // Concat to final results list
-      results = results.concat(resources.map(filterResources(clauses, i > 0, instanceMap)).filter(filterEmpty));
-    }
-
-    // Set correct order
-    if (opts.sortOrder && results.length) {
-      results = opts.sortOrder === 'random' ? util.shuffle(results) : results.sort(sortResultsByKey(opts.sortOrder));
-    }
-
-    // Slice max results.
-    if (opts.maxResults !== Infinity) {
-      results = results.slice(0, opts.maxResults);
-    }
-
-    // Remove page level duplicates
-    if (opts.allowDuplicates === undefined || opts.allowDuplicates === 'false') {
-      results = results.filter(removePageLevelDuplicates);
-
-      for (var index = 0; index < results.length; ++index) {
-        pageMap[results[index].index] = 1;
-      }
-    }
-
-    return results;
-  }
-
-  function filterResources(clauses, removeDuplicates, map) {
-    return function(resource) {
-      var resourceIsAllowed = true;
-
-      // References must be defined.
-      if (resource === undefined) {
-        return;
-      }
-
-      // Get canonical (localized) version of resource if possible.
-      resource = METADATA.byUrl[resource.baseUrl] || METADATA.byUrl[resource.url] || resource;
-
-      // Filter out resources already used
-      if (removeDuplicates) {
-        resourceIsAllowed = !map[resource.index];
-      }
-
-      // Must fulfill all criteria
-      if (clauses.length > 0) {
-        resourceIsAllowed = resourceIsAllowed && doesResourceMatchClauses(resource, clauses);
-      }
-
-      // Mark resource as used.
-      if (resourceIsAllowed) {
-        map[resource.index] = 1;
-      }
-
-      return resourceIsAllowed && resource;
-    };
-  }
-
-  function filterEmpty(resource) {
-    return resource;
-  }
-
-  function sortResultsByKey(key) {
-    var desc = key.charAt(0) === '-';
-
-    if (desc) {
-      key = key.substring(1);
-    }
-
-    return function(x, y) {
-      return (desc ? -1 : 1) * (parseInt(x[key], 10) - parseInt(y[key], 10));
-    };
-  }
-
-  function getResourcesForClause(clause) {
-    switch (clause.attr) {
-      case 'type':
-        return METADATA.byType[clause.value];
-      case 'tag':
-        return METADATA.byTag[clause.value];
-      case 'collection':
-        var resources = METADATA.collections[clause.value] || {};
-        return getResourcesByUrlCollection(resources.resources);
-      case 'history':
-        return getResourcesByUrlCollection($.dacGetVisitedUrls(clause.value));
-      case 'section':
-        return getResourcesByUrlCollection([clause.value].sections);
-      default:
-        return [];
-    }
-  }
-
-  function getResourcesByUrlCollection(resources) {
-    return (resources || []).map(function(url) {
-      return METADATA.byUrl[url];
-    });
-  }
-
-  function removePageLevelDuplicates(resource) {
-    return resource && !pageMap[resource.index];
-  }
-
-  function doesResourceMatchClauses(resource, clauses) {
-    for (var i = 0; i < clauses.length; i++) {
-      var map;
-      switch (clauses[i].attr) {
-        case 'type':
-          map = METADATA.hasType[clauses[i].value];
-          break;
-        case 'tag':
-          map = METADATA.hasTag[clauses[i].value];
-          break;
-      }
-
-      if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
-        return clauses[i].negative;
-      }
-    }
-
-    return true;
-  }
-
-  function parseResourceQuery(query) {
-    // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
-    var expressions = [];
-    var expressionStrs = query.split(',') || [];
-    for (var i = 0; i < expressionStrs.length; i++) {
-      var expr = expressionStrs[i] || '';
-
-      // Break expression into clauses (clause e.g. 'tag:foo')
-      var clauses = [];
-      var clauseStrs = expr.split(/(?=[\+\-])/);
-      for (var j = 0; j < clauseStrs.length; j++) {
-        var clauseStr = clauseStrs[j] || '';
-
-        // Get attribute and value from clause (e.g. attribute='tag', value='foo')
-        var parts = clauseStr.split(':');
-        var clause = {};
-
-        clause.attr = parts[0].replace(/^\s+|\s+$/g, '');
-        if (clause.attr) {
-          if (clause.attr.charAt(0) === '+') {
-            clause.attr = clause.attr.substring(1);
-          } else if (clause.attr.charAt(0) === '-') {
-            clause.negative = true;
-            clause.attr = clause.attr.substring(1);
-          }
-        }
-
-        if (parts.length > 1) {
-          clause.value = parts[1].replace(/^\s+|\s+$/g, '');
-        }
-
-        clauses.push(clause);
-      }
-
-      if (!clauses.length) {
-        continue;
-      }
-
-      expressions.push(clauses);
-    }
-
-    return expressions;
-  }
-
-  return buildResourceList;
-})(jQuery);
-
-/* global METADATA, getLangPref */
-
-window.metadata.search = (function() {
-  'use strict';
-
-  var currentLang = getLangPref();
-
-  function search(query) {
-    window.metadata.prepare();
-    return {
-      android: findDocsMatches(query, METADATA.androidReference),
-      docs: findDocsMatches(query, METADATA.googleReference),
-      resources: findResourceMatches(query)
-    };
-  }
-
-  function findDocsMatches(query, data) {
-    var results = [];
-
-    for (var i = 0; i < data.length; i++) {
-      var s = data[i];
-      if (query.length !== 0 && s.label.toLowerCase().indexOf(query.toLowerCase()) !== -1) {
-        results.push(s);
-      }
-    }
-
-    rankAutocompleteApiResults(query, results);
-
-    return results;
-  }
-
-  function findResourceMatches(query) {
-    var results = [];
-
-    // Search for matching JD docs
-    if (query.length >= 2) {
-      /* In some langs, spaces may be optional between certain non-Ascii word-glyphs. For
-       * those langs, only match query at word boundaries if query includes Ascii chars only.
-       */
-      var NO_BOUNDARY_LANGUAGES = ['ja','ko','vi','zh-cn','zh-tw'];
-      var isAsciiOnly = /^[\u0000-\u007f]*$/.test(query);
-      var noBoundaries = (NO_BOUNDARY_LANGUAGES.indexOf(window.getLangPref()) !== -1);
-      var exprBoundary = (!isAsciiOnly && noBoundaries) ? '' : '(?:^|\\s)';
-      var queryRegex = new RegExp(exprBoundary + query.toLowerCase(), 'g');
-
-      var all = METADATA.all;
-      for (var i = 0; i < all.length; i++) {
-        // current search comparison, with counters for tag and title,
-        // used later to improve ranking
-        var s = all[i];
-        s.matched_tag = 0;
-        s.matched_title = 0;
-        var matched = false;
-
-        // Check if query matches any tags; work backwards toward 1 to assist ranking
-        if (s.keywords) {
-          for (var j = s.keywords.length - 1; j >= 0; j--) {
-            // it matches a tag
-            if (s.keywords[j].toLowerCase().match(queryRegex)) {
-              matched = true;
-              s.matched_tag = j + 1; // add 1 to index position
-            }
-          }
-        }
-
-        // Check if query matches doc title
-        if (s.title.toLowerCase().match(queryRegex)) {
-          matched = true;
-          s.matched_title = 1;
-        }
-
-        // Remember the doc if it matches either
-        if (matched) {
-          results.push(s);
-        }
-      }
-
-      // Improve the current results
-      results = lookupBetterResult(results);
-
-      // Rank/sort all the matched pages
-      rankAutocompleteDocResults(results);
-
-      return results;
-    }
-  }
-
-  // Replaces a match with another resource by url, if it exists.
-  function lookupReplacementByUrl(match, url) {
-    var replacement = METADATA.byUrl[url];
-
-    // Replacement resource does not exists.
-    if (!replacement) { return; }
-
-    replacement.matched_title = Math.max(replacement.matched_title, match.matched_title);
-    replacement.matched_tag = Math.max(replacement.matched_tag, match.matched_tag);
-
-    return replacement;
-  }
-
-  // Find the localized version of a page if it exists.
-  function lookupLocalizedVersion(match) {
-    return METADATA.byUrl[match.baseUrl] || METADATA.byUrl[match.url];
-  }
-
-  // Find the main page for a tutorial when matching a subpage.
-  function lookupTutorialIndex(match) {
-    // Guard for non index tutorial pages.
-    if (match.type !== 'training' || match.url.indexOf('index.html') >= 0) { return; }
-
-    var indexUrl = match.url.replace(/[^\/]+$/, 'index.html');
-    return lookupReplacementByUrl(match, indexUrl);
-  }
-
-  // Find related results which are a better match for the user.
-  function lookupBetterResult(matches) {
-    var newMatches = [];
-
-    matches = matches.filter(function(match) {
-      var newMatch = match;
-      newMatch = lookupTutorialIndex(newMatch) || newMatch;
-      newMatch = lookupLocalizedVersion(newMatch) || newMatch;
-
-      if (newMatch !== match) {
-        newMatches.push(newMatch);
-      }
-
-      return newMatch === match;
-    });
-
-    return toUnique(newMatches.concat(matches));
-  }
-
-  /* Order the jd doc result list based on match quality */
-  function rankAutocompleteDocResults(matches) {
-    if (!matches || !matches.length) {
-      return;
-    }
-
-    var _resultScoreFn = function(match) {
-      var score = 1.0;
-
-      // if the query matched a tag
-      if (match.matched_tag > 0) {
-        // multiply score by factor relative to position in tags list (max of 3)
-        score *= 3 / match.matched_tag;
-
-        // if it also matched the title
-        if (match.matched_title > 0) {
-          score *= 2;
-        }
-      } else if (match.matched_title > 0) {
-        score *= 3;
-      }
-
-      if (match.lang === currentLang) {
-        score *= 5;
-      }
-
-      return score;
-    };
-
-    for (var i = 0; i < matches.length; i++) {
-      matches[i].__resultScore = _resultScoreFn(matches[i]);
-    }
-
-    matches.sort(function(a, b) {
-      var n = b.__resultScore - a.__resultScore;
-
-      if (n === 0) {
-        // lexicographical sort if scores are the same
-        n = (a.title < b.title) ? -1 : 1;
-      }
-
-      return n;
-    });
-  }
-
-  /* Order the result list based on match quality */
-  function rankAutocompleteApiResults(query, matches) {
-    query = query || '';
-    if (!matches || !matches.length) {
-      return;
-    }
-
-    // helper function that gets the last occurence index of the given regex
-    // in the given string, or -1 if not found
-    var _lastSearch = function(s, re) {
-      if (s === '') {
-        return -1;
-      }
-      var l = -1;
-      var tmp;
-      while ((tmp = s.search(re)) >= 0) {
-        if (l < 0) {
-          l = 0;
-        }
-        l += tmp;
-        s = s.substr(tmp + 1);
-      }
-      return l;
-    };
-
-    // helper function that counts the occurrences of a given character in
-    // a given string
-    var _countChar = function(s, c) {
-      var n = 0;
-      for (var i = 0; i < s.length; i++) {
-        if (s.charAt(i) === c) {
-          ++n;
-        }
-      }
-      return n;
-    };
-
-    var queryLower = query.toLowerCase();
-    var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
-    var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
-    var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
-
-    var _resultScoreFn = function(result) {
-      // scores are calculated based on exact and prefix matches,
-      // and then number of path separators (dots) from the last
-      // match (i.e. favoring classes and deep package names)
-      var score = 1.0;
-      var labelLower = result.label.toLowerCase();
-      var t;
-      var partsAfter;
-      t = _lastSearch(labelLower, partExactAlnumRE);
-      if (t >= 0) {
-        // exact part match
-        partsAfter = _countChar(labelLower.substr(t + 1), '.');
-        score *= 200 / (partsAfter + 1);
-      } else {
-        t = _lastSearch(labelLower, partPrefixAlnumRE);
-        if (t >= 0) {
-          // part prefix match
-          partsAfter = _countChar(labelLower.substr(t + 1), '.');
-          score *= 20 / (partsAfter + 1);
-        }
-      }
-
-      return score;
-    };
-
-    for (var i = 0; i < matches.length; i++) {
-      // if the API is deprecated, default score is 0; otherwise, perform scoring
-      if (matches[i].deprecated === 'true') {
-        matches[i].__resultScore = 0;
-      } else {
-        matches[i].__resultScore = _resultScoreFn(matches[i]);
-      }
-    }
-
-    matches.sort(function(a, b) {
-      var n = b.__resultScore - a.__resultScore;
-
-      if (n === 0) {
-        // lexicographical sort if scores are the same
-        n = (a.label < b.label) ? -1 : 1;
-      }
-
-      return n;
-    });
-  }
-
-  // Destructive but fast toUnique.
-  // http://stackoverflow.com/a/25082874
-  function toUnique(array) {
-    var c;
-    var b = array.length || 1;
-
-    while (c = --b) {
-      while (c--) {
-        if (array[b] === array[c]) {
-          array.splice(c, 1);
-        }
-      }
-    }
-    return array;
-  }
-
-  return search;
-})();
-
 (function($) {
   'use strict';
 
@@ -5281,7 +5165,7 @@
   ScrollButton.DEFAULTS_ = {
     duration: 300,
     easing: 'swing',
-    offset: '.dac-header',
+    offset: 0,
     scrollContainer: 'html, body'
   };
 
@@ -5297,26 +5181,11 @@
 
     event.preventDefault();
 
-    var position = this.getTargetPosition();
     $(this.options.scrollContainer).animate({
-      scrollTop: position - this.options.offset
+      scrollTop: this.target.offset().top - this.options.offset
     }, this.options);
   };
 
-  ScrollButton.prototype.getTargetPosition = function() {
-    if (this.options.scrollContainer === ScrollButton.DEFAULTS_.scrollContainer) {
-      return this.target.offset().top;
-    }
-    var scrollContainer = $(this.options.scrollContainer)[0];
-    var currentEl = this.target[0];
-    var pos = 0;
-    while (currentEl !== scrollContainer && currentEl !== null) {
-      pos += currentEl.offsetTop;
-      currentEl = currentEl.offsetParent;
-    }
-    return pos;
-  };
-
   /**
    * jQuery plugin
    * @param  {object} options - Override default options.
@@ -5337,605 +5206,6 @@
   });
 })(jQuery);
 
-/* global getLangPref */
-(function($) {
-  var LANG;
-
-  function getSearchLang() {
-    if (!LANG) {
-      LANG = getLangPref();
-
-      // Fix zh-cn to be zh-CN.
-      LANG = LANG.replace(/-\w+/, function(m) { return m.toUpperCase(); });
-    }
-    return LANG;
-  }
-
-  function customSearch(query, start) {
-    var searchParams = {
-      // current cse instance:
-      //cx: '001482626316274216503:zu90b7s047u',
-      // new cse instance:
-      cx: '000521750095050289010:zpcpi1ea4s8',
-      key: 'AIzaSyCFhbGnjW06dYwvRCU8h_zjdpS4PYYbEe8',
-      q: query,
-      start: start || 1,
-      num: 9,
-      hl: getSearchLang(),
-      fields: 'queries,items(pagemap,link,title,htmlSnippet,formattedUrl)'
-    };
-
-    return $.get('https://content.googleapis.com/customsearch/v1?' +  $.param(searchParams));
-  }
-
-  function renderResults(el, results, searchAppliance) {
-    var referenceResults = searchAppliance.getReferenceResults();
-    if (!results.items) {
-      el.append($('<div>').text('No results'));
-      return;
-    }
-
-    for (var i = 0; i < results.items.length; i++) {
-      var item = results.items[i];
-      var isDuplicate = false;
-      $(referenceResults.android).each(function(index, result) {
-        if (item.link.indexOf(result.link) > -1) {
-          isDuplicate = true;
-          return false;
-        }
-      });
-
-      if (!isDuplicate) {
-        var hasImage = item.pagemap && item.pagemap.cse_thumbnail;
-        var sectionMatch = item.link.match(/developer\.android\.com\/(\w*)/);
-        var section = (sectionMatch && sectionMatch[1]) || 'blog';
-
-        var entry = $('<div>').addClass('dac-custom-search-entry cols');
-
-        if (hasImage) {
-          var image = item.pagemap.cse_thumbnail[0];
-          entry.append($('<div>').addClass('dac-custom-search-image-wrapper')
-            .append($('<div>').addClass('dac-custom-search-image').css('background-image', 'url(' + image.src + ')')));
-        }
-
-        entry.append($('<div>').addClass('dac-custom-search-text-wrapper')
-          .append($('<p>').addClass('dac-custom-search-section').text(section))
-          .append(
-            $('<a>').text(item.title).attr('href', item.link).wrap('<h2>').parent().addClass('dac-custom-search-title')
-          )
-          .append($('<p>').addClass('dac-custom-search-snippet').html(item.htmlSnippet.replace(/<br>/g, '')))
-          .append($('<a>').addClass('dac-custom-search-link').text(item.formattedUrl).attr('href', item.link)));
-
-        el.append(entry);
-      }
-    }
-
-    if (results.queries.nextPage) {
-      var loadMoreButton = $('<button id="dac-custom-search-load-more">')
-        .addClass('dac-custom-search-load-more')
-        .text('Load more')
-        .click(function() {
-          loadMoreResults(el, results, searchAppliance);
-        });
-
-      el.append(loadMoreButton);
-    }
-  };
-
-  function loadMoreResults(el, results, searchAppliance) {
-    var query = results.queries.request[0].searchTerms;
-    var start = results.queries.nextPage[0].startIndex;
-    var loadMoreButton = el.find('#dac-custom-search-load-more');
-
-    loadMoreButton.text('Loading more...');
-
-    customSearch(query, start).then(function(results) {
-      loadMoreButton.remove();
-      renderResults(el, results, searchAppliance);
-    });
-  }
-
-  $.fn.customSearch = function(query, searchAppliance) {
-    var el = $(this);
-
-    customSearch(query).then(function(results) {
-      el.empty();
-      renderResults(el, results, searchAppliance);
-    });
-  };
-})(jQuery);
-
-/* global METADATA */
-
-(function($) {
-  $.fn.dacSearchRenderHero = function(resources, query) {
-    var el = $(this);
-    el.empty();
-
-    var resource = METADATA.searchHeroCollections[query];
-
-    if (resource) {
-      el.dacHero(resource, true);
-      el.show();
-
-      return true;
-    } else {
-      el.hide();
-    }
-  };
-})(jQuery);
-
-(function($) {
-  $.fn.dacSearchRenderReferences = function(results, query) {
-    var referenceCard = $('.suggest-card.reference');
-    referenceCard.data('searchreferences.dac', {results: results, query: query});
-    renderResults(referenceCard, results, query, false);
-  };
-
-  var ROW_COUNT_COLLAPSED = 20;
-  var ROW_COUNT_EXPANDED = 40;
-  var ROW_COUNT_GOOGLE_COLLAPSED = 1;
-  var ROW_COUNT_GOOGLE_EXPANDED = 8;
-
-  function onSuggestionClick(e) {
-    var normalClick = e.which === 1 && !e.ctrlKey && !e.shiftKey && !e.metaKey;
-    if (normalClick) {
-      e.preventDefault();
-    }
-
-    // When user clicks a suggested document, track it
-    var url = $(e.currentTarget).attr('href');
-    ga('send', 'event', 'Suggestion Click', 'clicked: ' + url,
-        'query: ' + $('#search_autocomplete').val().toLowerCase(),
-        {hitCallback: function() {
-          if (normalClick) {
-            document.location = url;
-          }
-        }});
-  }
-
-  function buildLink(match) {
-    var link = $('<a>').attr('href', window.toRoot + match.link);
-
-    var label = match.label;
-    var classNameStart = label.match(/[A-Z]/) ? label.search(/[A-Z]/) : label.lastIndexOf('.') + 1;
-    var newLink = '<span class="namespace">' +
-      label.substr(0, classNameStart) +
-      '</span>' +
-      label.substr(classNameStart, label.length);
-
-    link.html(newLink);
-    return link;
-  }
-
-  function buildSuggestion(match, query) {
-    var li = $('<li>').addClass('dac-search-results-reference-entry');
-
-    var link = buildLink(match);
-    link.highlightMatches(query);
-    li.append(link);
-    return li[0];
-  }
-
-  function buildResults(results, query) {
-    return results.map(function(match) {
-      return buildSuggestion(match, query);
-    });
-  }
-
-  function renderAndroidResults(list, gMatches, query) {
-    list.empty();
-
-    var header = $('<li class="dac-search-results-reference-header">android</li>');
-    list.append(header);
-
-    if (gMatches.length > 0) {
-      list.removeClass('no-results');
-
-      var resources = buildResults(gMatches, query);
-      list.append(resources);
-      return true;
-    } else {
-      list.append('<li class="dac-search-results-reference-entry-empty">No results</li>');
-    }
-  }
-
-  function renderGoogleDocsResults(list, gGoogleMatches, query) {
-    list = $('.suggest-card.reference ul');
-
-    if (gGoogleMatches.length > 0) {
-      list.append('<li class="dac-search-results-reference-header">in Google Services</li>');
-
-      var resources = buildResults(gGoogleMatches, query);
-      list.append(resources);
-
-      return true;
-    }
-  }
-
-  function renderResults(referenceCard, results, query, expanded) {
-    var list = referenceCard.find('ul');
-    list.toggleClass('is-expanded', !!expanded);
-
-    // Figure out how many results we can show in our fixed size box.
-    var total = expanded ? ROW_COUNT_EXPANDED : ROW_COUNT_COLLAPSED;
-    var googleCount = expanded ? ROW_COUNT_GOOGLE_EXPANDED : ROW_COUNT_GOOGLE_COLLAPSED;
-    googleCount = Math.max(googleCount, total - results.android.length);
-    googleCount = Math.min(googleCount, results.docs.length);
-
-    if (googleCount > 0) {
-      // If there are google results, reserve space for its header.
-      googleCount++;
-    }
-
-    var androidCount = Math.max(0, total - googleCount);
-    if (androidCount === 0) {
-      // Reserve space for "No reference results"
-      googleCount--;
-    }
-
-    renderAndroidResults(list, results.android.slice(0, androidCount), query);
-    renderGoogleDocsResults(list, results.docs.slice(0, googleCount - 1), query);
-
-    var totalResults = results.android.length + results.docs.length;
-    if (totalResults === 0) {
-      list.addClass('no-results');
-    }
-
-    // Tweak see more logic to account for references.
-    var hasMore = totalResults > ROW_COUNT_COLLAPSED && !util.matchesMedia('mobile');
-    if (hasMore) {
-      // We can't actually show all matches, only as many as the expanded list
-      // will fit, so we actually lie if the total results count is more
-      var moreCount = Math.min(totalResults, ROW_COUNT_EXPANDED + ROW_COUNT_GOOGLE_EXPANDED);
-      var $moreLink = $('<li class="dac-search-results-reference-entry-empty " data-toggle="show-more">see more matches</li>');
-      list.append($moreLink.on('click', onToggleMore));
-    }
-    var searchEl = $('#search-resources');
-    searchEl.toggleClass('dac-has-more', searchEl.hasClass('dac-has-more') || (hasMore && !expanded));
-    searchEl.toggleClass('dac-has-less', searchEl.hasClass('dac-has-less') || (hasMore && expanded));
-  }
-
-  function onToggleMore(e) {
-    var link = $(e.currentTarget);
-    var referenceCard = $('.suggest-card.reference');
-    var data = referenceCard.data('searchreferences.dac');
-
-    if (util.matchesMedia('mobile')) { return; }
-
-    renderResults(referenceCard, data.results, data.query, link.data('toggle') === 'show-more');
-  }
-
-  $(document).on('click', '.dac-search-results-resources [data-toggle="show-more"]', onToggleMore);
-  $(document).on('click', '.dac-search-results-resources [data-toggle="show-less"]', onToggleMore);
-  $(document).on('click', '.suggest-card.reference a', onSuggestionClick);
-})(jQuery);
-
-(function($) {
-  function highlightPage(query, page) {
-    page.find('.title').highlightMatches(query);
-  }
-
-  $.fn.dacSearchRenderResources = function(gDocsMatches, query) {
-    this.resourceWidget(gDocsMatches, {
-      itemsPerPage: 18,
-      initialResults: 6,
-      cardSizes: ['6x2'],
-      onRenderPage: highlightPage.bind(null, query)
-    });
-
-    return this;
-  };
-})(jQuery);
-
-/*global metadata */
-
-(function($, metadata) {
-  'use strict';
-
-  function Search() {
-    this.body = $('body');
-    this.lastQuery = null;
-    this.searchResults = $('#search-results');
-    this.searchClose = $('[data-search-close]');
-    this.searchClear = $('[data-search-clear]');
-    this.searchInput = $('#search_autocomplete');
-    this.searchResultsContent = $('#dac-search-results-content');
-    this.searchResultsFor = $('#search-results-for');
-    this.searchResultsHistory = $('#dac-search-results-history');
-    this.searchResultsResources = $('#search-resources');
-    this.searchResultsHero = $('#dac-search-results-hero');
-    this.searchResultsReference = $('#dac-search-results-reference');
-    this.searchHeader = $('[data-search]').data('search-input.dac');
-    this.currQueryReferenceResults = {};
-  }
-
-  Search.prototype.init = function() {
-    if (!devsite && this.checkRedirectToIndex()) { return; }
-
-    this.searchHistory = window.dacStore('search-history');
-
-    this.searchInput.focus(this.onSearchChanged.bind(this));
-    this.searchInput.keydown(this.handleKeyboardShortcut.bind(this));
-    this.searchInput.on('input', this.onSearchChanged.bind(this));
-    this.searchClear.click(this.clear.bind(this));
-    this.searchClose.click(this.close.bind(this));
-
-    this.customSearch = $.fn.debounce(function(query) {
-      $('#dac-custom-search-results').customSearch(query, this);
-    }.bind(this), 1000);
-    // Start search shortcut (/)
-    $('body').keyup(function(event) {
-      if (event.which === 191 && $(event.target).is(':not(:input)')) {
-        this.searchInput.focus();
-      }
-    }.bind(this));
-
-    $(window).on('popstate', this.onPopState.bind(this));
-    $(window).hashchange(this.onHashChange.bind(this));
-    this.onHashChange();
-  };
-
-  Search.prototype.checkRedirectToIndex = function() {
-    var query = this.getUrlQuery();
-    var target = window.getLangTarget();
-    var prefix = (target !== 'en') ? '/intl/' + target : '';
-    var pathname = location.pathname.slice(prefix.length);
-    if (query != null && pathname !== '/index.html') {
-      location.href = prefix + '/index.html' + location.hash;
-      return true;
-    }
-  };
-
-  Search.prototype.handleKeyboardShortcut = function(event) {
-    // Close (esc)
-    if (event.which === 27) {
-      this.searchClose.trigger('click');
-      event.preventDefault();
-    }
-
-    // Previous result (up arrow)
-    if (event.which === 38) {
-      this.previousResult();
-      event.preventDefault();
-    }
-
-    // Next result (down arrow)
-    if (event.which === 40) {
-      this.nextResult();
-      event.preventDefault();
-    }
-
-    // Navigate to result (enter)
-    if (event.which === 13) {
-      this.navigateToResult();
-      event.preventDefault();
-    }
-  };
-
-  Search.prototype.goToResult = function(relativeIndex) {
-    var links = this.searchResults.find('a').filter(':visible');
-    var selectedLink = this.searchResults.find('.dac-selected');
-
-    if (selectedLink.length) {
-      var found = $.inArray(selectedLink[0], links);
-
-      selectedLink.removeClass('dac-selected');
-      links.eq(found + relativeIndex).addClass('dac-selected');
-      return true;
-    } else {
-      if (relativeIndex > 0) {
-        links.first().addClass('dac-selected');
-      }
-    }
-  };
-
-  Search.prototype.previousResult = function() {
-    this.goToResult(-1);
-  };
-
-  Search.prototype.nextResult = function() {
-    this.goToResult(1);
-  };
-
-  Search.prototype.navigateToResult = function() {
-    var query = this.getQuery();
-    var selectedLink = this.searchResults.find('.dac-selected');
-
-    if (selectedLink.length) {
-      selectedLink[0].click();
-    } else {
-      this.searchHistory.push(query);
-      this.addQueryToUrl(query);
-
-      var isMobileOrTablet = typeof window.orientation !== 'undefined';
-
-      if (isMobileOrTablet) {
-        this.searchInput.blur();
-      }
-    }
-  };
-
-  Search.prototype.onHashChange = function() {
-    var query = this.getUrlQuery();
-    if (query != null && query !== this.getQuery()) {
-      this.searchInput.val(query);
-      this.onSearchChanged();
-    }
-  };
-
-  Search.prototype.clear = function() {
-    this.searchInput.val('');
-    window.location.hash = '';
-    this.onSearchChanged();
-    this.searchInput.focus();
-  };
-
-  Search.prototype.close = function() {
-    this.removeQueryFromUrl();
-    this.searchInput.blur();
-    this.hideOverlay();
-  };
-
-  Search.prototype.getUrlQuery = function() {
-    var queryMatch = location.hash.match(/q=(.*)&?/);
-    return queryMatch && queryMatch[1] && decodeURI(queryMatch[1]);
-  };
-
-  Search.prototype.getQuery = function() {
-    return this.searchInput.val().replace(/(^ +)|( +$)/g, '');
-  };
-
-  Search.prototype.getReferenceResults = function() {
-    return this.currQueryReferenceResults;
-  };
-
-  Search.prototype.onSearchChanged = function() {
-    var query = this.getQuery();
-
-    this.showOverlay();
-    this.render(query);
-  };
-
-  Search.prototype.render = function(query) {
-    if (this.lastQuery === query) { return; }
-
-    if (query.length < 2) {
-      query = '';
-    }
-
-    this.lastQuery = query;
-    this.searchResultsFor.text(query);
-
-    // CSE results lag behind the metadata/reference results. We need to empty
-    // the CSE results and add 'Loading' text so user's aren't looking at two
-    // different sets of search results at one time.
-    var $loadingEl =
-        $('<div class="loadingCustomSearchResults">Loading Results...</div>');
-    $('#dac-custom-search-results').empty().prepend($loadingEl);
-
-    this.customSearch(query);
-    var metadataResults = metadata.search(query);
-    this.searchResultsResources.dacSearchRenderResources(metadataResults.resources, query);
-    this.searchResultsReference.dacSearchRenderReferences(metadataResults, query);
-    this.currQueryReferenceResults = metadataResults;
-    var hasHero = this.searchResultsHero.dacSearchRenderHero(metadataResults.resources, query);
-    var hasQuery = !!query;
-
-    this.searchResultsReference.toggle(!hasHero);
-    this.searchResultsContent.toggle(hasQuery);
-    this.searchResultsHistory.toggle(!hasQuery);
-    this.addQueryToUrl(query);
-    this.pushState();
-  };
-
-  Search.prototype.addQueryToUrl = function(query) {
-    var hash = 'q=' + encodeURI(query);
-
-    if (query) {
-      if (window.history.replaceState) {
-        window.history.replaceState(null, '', '#' + hash);
-      } else {
-        window.location.hash = hash;
-      }
-    }
-  };
-
-  Search.prototype.onPopState = function() {
-    if (!this.getUrlQuery()) {
-      this.hideOverlay();
-      this.searchHeader.unsetActiveState();
-    }
-  };
-
-  Search.prototype.removeQueryFromUrl = function() {
-    window.location.hash = '';
-  };
-
-  Search.prototype.pushState = function() {
-    if (window.history.pushState && !this.lastQuery.length) {
-      window.history.pushState(null, '');
-    }
-  };
-
-  Search.prototype.showOverlay = function() {
-    this.body.addClass('dac-modal-open dac-search-open');
-  };
-
-  Search.prototype.hideOverlay = function() {
-    this.body.removeClass('dac-modal-open dac-search-open');
-  };
-
-  $(document).on('ready.aranja', function() {
-    var search = new Search();
-    search.init();
-  });
-})(jQuery, metadata);
-
-window.dacStore = (function(window) {
-  /**
-   * Creates a new persistent store.
-   * If localStorage is unavailable, the items are stored in memory.
-   *
-   * @constructor
-   * @param {string} name    The name of the store
-   * @param {number} maxSize The maximum number of items the store can hold.
-   */
-  var Store = function(name, maxSize) {
-    var content = [];
-
-    var hasLocalStorage = !!window.localStorage;
-
-    if (hasLocalStorage) {
-      try {
-        content = JSON.parse(window.localStorage.getItem(name) || []);
-      } catch (e) {
-        // Store contains invalid data
-        window.localStorage.removeItem(name);
-      }
-    }
-
-    function push(item) {
-      if (content[0] === item) {
-        return;
-      }
-
-      content.unshift(item);
-
-      if (maxSize) {
-        content.splice(maxSize, content.length);
-      }
-
-      if (hasLocalStorage) {
-        window.localStorage.setItem(name, JSON.stringify(content));
-      }
-    }
-
-    function all() {
-      // Return a copy
-      return content.slice();
-    }
-
-    return {
-      push: push,
-      all: all
-    };
-  };
-
-  var stores = {
-    'search-history': new Store('search-history', 3)
-  };
-
-  /**
-   * Get a named persistent store.
-   * @param  {string} name
-   * @return {Store}
-   */
-  return function getStore(name) {
-    return stores[name];
-  };
-})(window);
-
 (function($) {
   'use strict';
 
@@ -5951,12 +5221,10 @@
   function SwapContent(el, options) {
     this.el = $(el);
     this.options = $.extend({}, SwapContent.DEFAULTS_, options);
-    this.options.dynamic = this.options.dynamic === 'true';
     this.containers = this.el.find(this.options.container);
     this.initiallyActive = this.containers.children('.' + this.options.activeClass).eq(0);
     this.el.on('swap-content', this.swap.bind(this));
     this.el.on('swap-reset', this.reset.bind(this));
-    this.el.find(this.options.swapButton).on('click', this.swap.bind(this));
   }
 
   /**
@@ -5967,8 +5235,6 @@
   SwapContent.DEFAULTS_ = {
     activeClass: 'dac-active',
     container: '[data-swap-container]',
-    dynamic: 'true',
-    swapButton: '[data-swap-button]',
     transitionSpeed: 500
   };
 
@@ -6002,15 +5268,9 @@
    * Perform the swap of content.
    */
   SwapContent.prototype.swap = function() {
+    console.log(this.containers);
     this.containers.each(function(index, container) {
       container = $(container);
-
-      if (!this.options.dynamic) {
-        container.children().toggleClass(this.options.activeClass);
-        this.complete.bind(this);
-        return;
-      }
-
       container.height(this.currentHeight(container)).children().toggleClass(this.options.activeClass);
       container.animate({height: this.currentHeight(container)}, this.options.transitionSpeed,
         this.complete.bind(this));
@@ -6037,178 +5297,6 @@
   });
 })(jQuery);
 
-/* Tabs */
-(function($) {
-  'use strict';
-
-  /**
-   * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
-   * @constructor
-   */
-  function Tabs(el, options) {
-    this.el = $(el);
-    this.options = $.extend({}, Tabs.DEFAULTS_, options);
-    this.init();
-  }
-
-  Tabs.DEFAULTS_ = {
-    activeClass: 'dac-active',
-    viewDataAttr: 'tab-view',
-    itemDataAttr: 'tab-item'
-  };
-
-  Tabs.prototype.init = function() {
-    var itemDataAttribute = '[data-' + this.options.itemDataAttr + ']';
-    this.tabEl_ = this.el.find(itemDataAttribute);
-    this.tabViewEl_ = this.el.find('[data-' + this.options.viewDataAttr + ']');
-    this.el.on('click.dac-tabs', itemDataAttribute, this.changeTabs.bind(this));
-  };
-
-  Tabs.prototype.changeTabs = function(event) {
-    var current = $(event.currentTarget);
-    var index = current.index();
-
-    if (current.hasClass(this.options.activeClass)) {
-      current.add(this.tabViewEl_.eq(index)).removeClass(this.options.activeClass);
-    } else {
-      this.tabEl_.add(this.tabViewEl_).removeClass(this.options.activeClass);
-      current.add(this.tabViewEl_.eq(index)).addClass(this.options.activeClass);
-    }
-  };
-
-  /**
-   * jQuery plugin
-   */
-  $.fn.dacTabs = function() {
-    return this.each(function() {
-      var el = $(this);
-      new Tabs(el, el.data());
-    });
-  };
-
-  /**
-   * Data Attribute API
-   */
-  $(function() {
-    $('[data-tabs]').dacTabs();
-  });
-})(jQuery);
-
-/* Toast Component */
-(function($) {
-  'use strict';
-  /**
-   * @constant
-   * @type {String}
-   */
-  var LOCAL_STORAGE_KEY = 'toast-closed-index';
-
-  /**
-   * Dictionary from local storage.
-   */
-  var toastDictionary = localStorage.getItem(LOCAL_STORAGE_KEY);
-  toastDictionary = toastDictionary ? JSON.parse(toastDictionary) : {};
-
-  /**
-   * Variable used for caching the body.
-   */
-  var bodyCached;
-
-  /**
-   * @param {HTMLElement} el - The DOM element.
-   * @param {Object} options
-   * @constructor
-   */
-  function Toast(el, options) {
-    this.el = $(el);
-    this.options = $.extend({}, Toast.DEFAULTS_, options);
-    this.init();
-  }
-
-  Toast.DEFAULTS_ = {
-    closeBtnClass: 'dac-toast-close-btn',
-    closeDuration: 200,
-    visibleClass: 'dac-visible',
-    wrapClass: 'dac-toast-wrap'
-  };
-
-  /**
-   * Generate a close button.
-   * @returns {*|HTMLElement}
-   */
-  Toast.prototype.closeBtn = function() {
-    this.closeBtnEl = this.closeBtnEl || $('<button class="' + this.options.closeBtnClass + '">' +
-      '<i class="dac-sprite dac-close-black"></i>' +
-    '</button>');
-    return this.closeBtnEl;
-  };
-
-  /**
-   * Initialize a new toast element
-   */
-  Toast.prototype.init = function() {
-    this.hash = this.el.text().replace(/[\s\n\t]/g, '').split('').slice(0, 128).join('');
-
-    if (toastDictionary[this.hash]) {
-      return;
-    }
-
-    this.closeBtn().on('click', this.onClickHandler.bind(this));
-    this.el.find('.' + this.options.wrapClass).append(this.closeBtn());
-    this.el.addClass(this.options.visibleClass);
-    this.dynamicPadding(this.el.outerHeight());
-  };
-
-  /**
-   * Add padding to make sure all page is visible.
-   */
-  Toast.prototype.dynamicPadding = function(val) {
-    var currentPadding = parseInt(bodyCached.css('padding-bottom') || 0);
-    bodyCached.css('padding-bottom', val + currentPadding);
-  };
-
-  /**
-   * Remove a toast from the DOM
-   */
-  Toast.prototype.remove = function() {
-    this.dynamicPadding(-this.el.outerHeight());
-    this.el.remove();
-  };
-
-  /**
-   * Handle removal of the toast.
-   */
-  Toast.prototype.onClickHandler = function() {
-    // Only fadeout toasts from top of stack. Others are removed immediately.
-    var duration = this.el.index() === 0 ? this.options.closeDuration : 0;
-    this.el.fadeOut(duration, this.remove.bind(this));
-
-    // Save closed state.
-    toastDictionary[this.hash] = 1;
-    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(toastDictionary));
-  };
-
-  /**
-   * jQuery plugin
-   * @param  {object} options - Override default options.
-   */
-  $.fn.dacToast = function() {
-    return this.each(function() {
-      var el = $(this);
-      new Toast(el, el.data());
-    });
-  };
-
-  /**
-   * Data Attribute API
-   */
-  $(function() {
-    bodyCached = $('#body-content');
-    $('[data-toast]').dacToast();
-  });
-})(jQuery);
-
 (function($) {
   function Toggle(el) {
     $(el).on('click.dac.togglesection', this.toggle);
@@ -6253,12 +5341,8 @@
 
     // If we're hiding, first set the maxHeight we're transitioning from.
     if (!visible) {
-      $el.css({
-          transitionDuration: '0s',
-          maxHeight: contentHeight + 'px'
-        })
-        .resolveStyles()
-        .css('transitionDuration', '');
+      $el.css('maxHeight', contentHeight + 'px')
+        .resolveStyles();
     }
 
     // Transition to new state
@@ -6266,12 +5350,7 @@
 
     // Reset maxHeight to css value after transition.
     setTimeout(function() {
-      $el.css({
-          transitionDuration: '0s',
-          maxHeight: ''
-        })
-        .resolveStyles()
-        .css('transitionDuration', '');
+      $el.css('maxHeight', '');
     }, duration);
   }
 
@@ -6296,263 +5375,3 @@
   $(document)
     .on('click.toggle', '[data-toggle="section"]', Toggle.prototype.toggle);
 })(jQuery);
-
-(function(window) {
-  /**
-   * Media query breakpoints. Should match CSS.
-   */
-  var BREAKPOINTS = {
-    mobile: [0, 719],
-    tablet: [720, 959],
-    desktop: [960, 9999]
-  };
-
-  /**
-   * Fisher-Yates Shuffle (Knuth shuffle).
-   * @param {Array} input
-   * @returns {Array} shuffled array.
-   */
-  function shuffle(input) {
-    for (var i = input.length; i >= 0; i--) {
-      var randomIndex = Math.floor(Math.random() * (i + 1));
-      var randomItem = input[randomIndex];
-      input[randomIndex] = input[i];
-      input[i] = randomItem;
-    }
-
-    return input;
-  }
-
-  /**
-   * Matches media breakpoints like in CSS.
-   * @param {string} form of either mobile, tablet or desktop.
-   */
-  function matchesMedia(form) {
-    var breakpoint = BREAKPOINTS[form];
-    return window.innerWidth >= breakpoint[0] && window.innerWidth <= breakpoint[1];
-  }
-
-  window.util = {
-    shuffle: shuffle,
-    matchesMedia: matchesMedia
-  };
-})(window);
-
-(function($, window) {
-  'use strict';
-
-  var YouTubePlayer = (function() {
-    var player;
-
-    function VideoPlayer() {
-      this.mPlayerPaused = false;
-      this.doneSetup = false;
-    }
-
-    VideoPlayer.prototype.setup = function() {
-      // loads the IFrame Player API code asynchronously.
-      $.getScript('https://www.youtube.com/iframe_api');
-
-      // Add the shadowbox HTML to the body
-      $('body').prepend(
-'<div id="video-player" class="Video">' +
-  '<div id="video-overlay" class="Video-overlay" />' +
-  '<div class="Video-container">' +
-    '<div class="Video-frame">' +
-      '<span class="Video-loading">Loading&hellip;</span>' +
-      '<div id="youTubePlayer"></div>' +
-    '</div>' +
-    '<div class="Video-controls">' +
-      '<button id="picture-in-picture" class="Video-button Video-button--picture-in-picture">' +
-      '<button id="close-video" class="Video-button Video-button--close" />' +
-    '</div>' +
-  '</div>' +
-'</div>');
-
-      this.videoPlayer = $('#video-player');
-
-      var pictureInPictureButton = this.videoPlayer.find('#picture-in-picture');
-      pictureInPictureButton.on('click.aranja', this.toggleMinimizeVideo.bind(this));
-
-      var videoOverlay = this.videoPlayer.find('#video-overlay');
-      var closeButton = this.videoPlayer.find('#close-video');
-      var closeVideo = this.closeVideo.bind(this);
-      videoOverlay.on('click.aranja', closeVideo);
-      closeButton.on('click.aranja', closeVideo);
-
-      this.doneSetup = true;
-    };
-
-    VideoPlayer.prototype.startYouTubePlayer = function(videoId) {
-      this.videoPlayer.show();
-
-      if (!this.isLoaded) {
-        this.queueVideo = videoId;
-        return;
-      }
-
-      this.mPlayerPaused = false;
-      // check if we've already created this player
-      if (!this.youTubePlayer) {
-        // check if there's a start time specified
-        var idAndHash = videoId.split('#');
-        var startTime = 0;
-        if (idAndHash.length > 1) {
-          startTime = idAndHash[1].split('t=')[1] !== undefined ? idAndHash[1].split('t=')[1] : 0;
-        }
-        // enable localized player
-        var lang = getLangPref();
-        var captionsOn = lang === 'en' ? 0 : 1;
-
-        this.youTubePlayer = new YT.Player('youTubePlayer', {
-          height: 720,
-          width: 1280,
-          videoId: idAndHash[0],
-          // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
-          playerVars: {start: startTime, hl: lang, cc_load_policy: captionsOn},
-          // jscs:enable
-          events: {
-            'onReady': this.onPlayerReady.bind(this),
-            'onStateChange': this.onPlayerStateChange.bind(this)
-          }
-        });
-      } else {
-        // if a video different from the one already playing was requested, cue it up
-        if (videoId !== this.getVideoId()) {
-          this.youTubePlayer.cueVideoById(videoId);
-        }
-        this.youTubePlayer.playVideo();
-      }
-    };
-
-    VideoPlayer.prototype.onPlayerReady = function(event) {
-      if (!isMobile) {
-        event.target.playVideo();
-        this.mPlayerPaused = false;
-      }
-    };
-
-    VideoPlayer.prototype.toggleMinimizeVideo = function(event) {
-      event.stopPropagation();
-      this.videoPlayer.toggleClass('Video--picture-in-picture');
-    };
-
-    VideoPlayer.prototype.closeVideo = function() {
-      try {
-        this.youTubePlayer.pauseVideo();
-      } catch (e) {
-      }
-      this.videoPlayer.fadeOut(200, function() {
-        this.videoPlayer.removeClass('Video--picture-in-picture');
-      }.bind(this));
-    };
-
-    VideoPlayer.prototype.getVideoId = function() {
-      // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
-      return this.youTubePlayer && this.youTubePlayer.getVideoData().video_id;
-      // jscs:enable
-    };
-
-    /* Track youtube playback for analytics */
-    VideoPlayer.prototype.onPlayerStateChange = function(event) {
-      var videoId = this.getVideoId();
-      var currentTime = this.youTubePlayer && this.youTubePlayer.getCurrentTime();
-
-      // Video starts, send the video ID
-      if (event.data === YT.PlayerState.PLAYING) {
-        if (this.mPlayerPaused) {
-          ga('send', 'event', 'Videos', 'Resume', videoId);
-        } else {
-          // track the start playing event so we know from which page the video was selected
-          ga('send', 'event', 'Videos', 'Start: ' + videoId, 'on: ' + document.location.href);
-        }
-        this.mPlayerPaused = false;
-      }
-
-      // Video paused, send video ID and video elapsed time
-      if (event.data === YT.PlayerState.PAUSED) {
-        ga('send', 'event', 'Videos', 'Paused', videoId, currentTime);
-        this.mPlayerPaused = true;
-      }
-
-      // Video finished, send video ID and video elapsed time
-      if (event.data === YT.PlayerState.ENDED) {
-        ga('send', 'event', 'Videos', 'Finished', videoId, currentTime);
-        this.mPlayerPaused = true;
-      }
-    };
-
-    return {
-      getPlayer: function() {
-        if (!player) {
-          player = new VideoPlayer();
-        }
-
-        return player;
-      }
-    };
-  })();
-
-  var videoPlayer = YouTubePlayer.getPlayer();
-
-  window.onYouTubeIframeAPIReady = function() {
-    videoPlayer.isLoaded = true;
-
-    if (videoPlayer.queueVideo) {
-      videoPlayer.startYouTubePlayer(videoPlayer.queueVideo);
-    }
-  };
-
-  function wrapLinkInPlayer(e) {
-    e.preventDefault();
-
-    if (!videoPlayer.doneSetup) {
-      videoPlayer.setup();
-    }
-
-    var videoIdMatches = $(e.currentTarget).attr('href').match(/(?:youtu.be\/|v=)([^&]*)/);
-    var videoId = videoIdMatches && videoIdMatches[1];
-
-    if (videoId) {
-      videoPlayer.startYouTubePlayer(videoId);
-    }
-  }
-
-  $(document).on('click.video', 'a[href*="youtube.com/watch"], a[href*="youtu.be"]', wrapLinkInPlayer);
-})(jQuery, window);
-
-/**
- * Wide table
- *
- * Wraps tables in a scrollable area so you can read them on mobile.
- */
-(function($) {
-  function initWideTable() {
-    $('table.jd-sumtable').each(function(i, table) {
-      $(table).wrap('<div class="dac-expand wide-table">');
-    });
-  }
-
-  $(function() {
-    initWideTable();
-  });
-})(jQuery);
-
-/** Utilities */
-
-/* returns the given string with all HTML brackets converted to entities
-    TODO: move this to the site's JS library */
-function escapeHTML(string) {
-  return string.replace(/</g,"&lt;")
-                .replace(/>/g,"&gt;");
-};
-
-function getQueryVariable(variable) {
-  var query = window.location.search.substring(1);
-  var vars = query.split("&");
-  for (var i=0;i<vars.length;i++) {
-    var pair = vars[i].split("=");
-    if(pair[0] == variable){return pair[1];}
-  }
-  return(false);
-};