1/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17$(document).ready(function() {
18  prettyPrint();
19  preventParentScrolls('nav');
20
21  var sluggify_ = function(s) {
22    return (s || '').replace(/ /g, '-').replace(/[^\w-]/g, '').toLowerCase();
23  };
24
25  $('h2, h3, h4.includetoc').each(function() {
26    $(this).attr('id', 'toc_' + sluggify_($(this).data('tocid') || $(this).data('toctitle') || $(this).text()));
27    $(this).click(function() {
28      smoothScrollToId($(this).attr('id'));
29    });
30  });
31
32  var buildNav_ = function(queries, $contentRoot, $navRoot) {
33    if (!queries || !queries.length) {
34      return;
35    }
36
37    $contentRoot.find(queries[0]).each(function() {
38      var $navNode = $('<div>')
39          .text($(this).html())
40          .appendTo($navRoot);
41      buildNav_(queries.splice(1), $(this), $navNode);
42    });
43  };
44
45  buildNav();
46});
47
48function buildNav() {
49  var currentLevel = 2;
50  var $currentParent = $('nav');
51  var $currentNode = null;
52
53  $('#page-content').find('h2, h3, h4.includetoc').each(function() {
54    var level = $(this).get(0).tagName.substring(1);
55
56    if (level < currentLevel) {
57      // ascend
58      for (var i = 0; i < (currentLevel - level); i++) {
59        $currentParent = $currentParent.parents('div.children, nav').first();
60      }
61
62    } else if (level > currentLevel) {
63      // descend
64      $currentParent = $('<div>')
65          .addClass('children')
66          .appendTo($currentNode);
67    }
68
69    var tocId = $(this).attr('id');
70    var navId = tocId.replace(/toc_/, 'nav_');
71
72    $interactionNode = $('<span>')
73        .html($(this).data('toctitle') || $(this).html())
74        .data('target', tocId)
75        .click(function() {
76          smoothScrollToId($(this).data('target'));
77        });
78
79    $currentNode = $('<div>')
80        .attr('id', navId)
81        .addClass('item')
82        .append($interactionNode)
83        .appendTo($currentParent);
84
85    currentLevel = level;
86  });
87
88  var headerPositionCache = [];
89  var rebuildHeaderPositionCache_ = function() {
90    headerPositionCache = [];
91    $('#page-content').find('h2, h3, h4.includetoc').each(function() {
92      headerPositionCache.push({
93        id: $(this).attr('id').replace(/toc_/, 'nav_'),
94        top: $(this).offset().top
95      });
96    });
97  };
98
99  var updateSelectedNavPosition_ = function() {
100    $('nav .item').removeClass('selected');
101    var scrollTop = $(window).scrollTop();
102    for (var i = headerPositionCache.length - 1; i >= 0; i--) {
103      if (scrollTop >= headerPositionCache[i].top) {
104        $('#' + headerPositionCache[i].id).addClass('selected');
105        break;
106      }
107    }
108  };
109
110  rebuildHeaderPositionCache_();
111  $(window).resize(function() {
112    rebuildHeaderPositionCache_();
113    updateSelectedNavPosition_();
114  });
115
116  $(window).scroll(function() {
117    updateSelectedNavPosition_();
118  });
119}
120
121function smoothScrollToId(id) {
122  var $target = $('#' + id);
123  $('body').animate({ scrollTop: $target.offset().top }, 200, 'swing', function() {
124    document.location.hash = id;
125  });
126}
127
128// Based on http://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element
129function preventParentScrolls($el) {
130  $($el).on('DOMMouseScroll mousewheel', function(ev) {
131    var $this = $(this),
132        scrollTop = this.scrollTop,
133        scrollHeight = this.scrollHeight,
134        height = $this.height(),
135        delta = (ev.type == 'DOMMouseScroll' ?
136            ev.originalEvent.detail * -40 :
137            ev.originalEvent.wheelDelta),
138        up = delta > 0;
139
140    if (!up && -delta > scrollHeight - height - scrollTop) {
141      // Scrolling down, but this will take us past the bottom.
142      $this.scrollTop(scrollHeight);
143    } else if (up && delta > scrollTop) {
144      // Scrolling up, but this will take us past the top.
145      $this.scrollTop(0);
146    } else {
147      $this.scrollTop(scrollTop - delta);
148    }
149
150    ev.stopPropagation();
151    ev.preventDefault();
152    ev.returnValue = false;
153    return false;
154  });
155}