1
2/* API LEVEL TOGGLE */
3addLoadEvent(changeApiLevel);
4
5var API_LEVEL_ENABLED_COOKIE = "api_level_enabled";
6var API_LEVEL_COOKIE = "api_level";
7var minLevel = 1;
8var maxLevel = 1;
9
10function toggleApiLevelSelector(checkbox) {
11  var date = new Date();
12  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
13  var expiration = date.toGMTString();
14  if (checkbox.checked) {
15    $("#apiLevelSelector").removeAttr("disabled");
16    $("#api-level-toggle label").removeClass("disabled");
17    writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration);
18  } else {
19    $("#apiLevelSelector").attr("disabled","disabled");
20    $("#api-level-toggle label").addClass("disabled");
21    writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration);
22  }
23  changeApiLevel();
24}
25
26function buildApiLevelSelector() {
27  maxLevel = SINCE_DATA.length;
28  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);
29  var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
30  userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
31
32  if (userApiLevelEnabled == 0) {
33    $("#apiLevelSelector").attr("disabled","disabled");
34  } else {
35    $("#apiLevelCheckbox").attr("checked","checked");
36    $("#api-level-toggle label").removeClass("disabled");
37  }
38
39  minLevel = parseInt($("body").attr("class"));
40  // Handle provisional api levels; the provisional level will always be the highest possible level
41  // Provisional api levels will also have a length; other stuff that's just missing a level won't,
42  // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
43  if (isNaN(minLevel) && minLevel.length) {
44    minLevel = maxLevel;
45  }
46  var select = $("#apiLevelSelector").html("").change(changeApiLevel);
47  for (var i = maxLevel-1; i >= 0; i--) {
48    var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
49  //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
50    select.append(option);
51  }
52
53  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
54  var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
55  selectedLevelItem.setAttribute('selected',true);
56}
57
58function changeApiLevel() {
59  maxLevel = SINCE_DATA.length;
60  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);
61  var selectedLevel = maxLevel;
62
63  if (userApiLevelEnabled == 0) {
64    toggleVisisbleApis(selectedLevel, "body");
65  } else {
66    selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
67    toggleVisisbleApis(selectedLevel, "body");
68
69    var date = new Date();
70    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
71    var expiration = date.toGMTString();
72    writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
73  }
74
75  if (selectedLevel < minLevel) {
76    var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
77    $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>"
78                              + "<p>To use this " + thing + ", your application must specify API Level \"" + $("body").attr("class") + "\" or higher in its manifest "
79                              + "and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this "
80                              + "document, change the value of the API Level filter above.</p>"
81                              + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API Level?</a></p></div>");
82  } else {
83    $("#naMessage").hide();
84  }
85}
86
87function toggleVisisbleApis(selectedLevel, context) {
88  var apis = $(".api",context);
89  apis.each(function(i) {
90    var obj = $(this);
91    var className = obj.attr("class");
92    var apiLevelIndex = className.lastIndexOf("-")+1;
93    var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
94    apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
95    var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
96    if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
97      return;
98    }
99    apiLevel = parseInt(apiLevel);
100
101    // Handle provisional api levels; if this item's level is the provisional one, set it to the max
102    var selectedLevelNum = parseInt(selectedLevel)
103    var apiLevelNum = parseInt(apiLevel);
104    if (isNaN(apiLevelNum)) {
105        apiLevelNum = maxLevel;
106    }
107
108    // Grey things out that aren't available and give a tooltip title
109    if (apiLevelNum > selectedLevelNum) obj.addClass("absent").attr("title","Requires API Level \""
110            + apiLevel + "\" or higher");
111    else obj.removeClass("absent").removeAttr("title");
112  });
113}
114
115/* NAVTREE */
116
117function new_node(me, mom, text, link, children_data, api_level)
118{
119  var node = new Object();
120  node.children = Array();
121  node.children_data = children_data;
122  node.depth = mom.depth + 1;
123
124  node.li = document.createElement("li");
125  mom.get_children_ul().appendChild(node.li);
126
127  node.label_div = document.createElement("div");
128  node.label_div.className = "label";
129  if (api_level != null) {
130    $(node.label_div).addClass("api");
131    $(node.label_div).addClass("api-level-"+api_level);
132  }
133  node.li.appendChild(node.label_div);
134  node.label_div.style.paddingLeft = 10*node.depth + "px";
135
136  if (children_data == null) {
137    // 12 is the width of the triangle and padding extra space
138    node.label_div.style.paddingLeft = ((10*node.depth)+12) + "px";
139  } else {
140    node.label_div.style.paddingLeft = 10*node.depth + "px";
141    node.expand_toggle = document.createElement("a");
142    node.expand_toggle.href = "javascript:void(0)";
143    node.expand_toggle.onclick = function() {
144          if (node.expanded) {
145            $(node.get_children_ul()).slideUp("fast");
146            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
147            node.expanded = false;
148          } else {
149            expand_node(me, node);
150          }
151       };
152    node.label_div.appendChild(node.expand_toggle);
153
154    node.plus_img = document.createElement("img");
155    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
156    node.plus_img.className = "plus";
157    node.plus_img.border = "0";
158    node.expand_toggle.appendChild(node.plus_img);
159
160    node.expanded = false;
161  }
162
163  var a = document.createElement("a");
164  node.label_div.appendChild(a);
165  node.label = document.createTextNode(text);
166  a.appendChild(node.label);
167  if (link) {
168    a.href = me.toroot + link;
169  } else {
170    if (children_data != null) {
171      a.className = "nolink";
172      a.href = "javascript:void(0)";
173      a.onclick = node.expand_toggle.onclick;
174      // This next line shouldn't be necessary.  I'll buy a beer for the first
175      // person who figures out how to remove this line and have the link
176      // toggle shut on the first try. --joeo@android.com
177      node.expanded = false;
178    }
179  }
180
181
182  node.children_ul = null;
183  node.get_children_ul = function() {
184      if (!node.children_ul) {
185        node.children_ul = document.createElement("ul");
186        node.children_ul.className = "children_ul";
187        node.children_ul.style.display = "none";
188        node.li.appendChild(node.children_ul);
189      }
190      return node.children_ul;
191    };
192
193  return node;
194}
195
196function expand_node(me, node)
197{
198  if (node.children_data && !node.expanded) {
199    if (node.children_visited) {
200      $(node.get_children_ul()).slideDown("fast");
201    } else {
202      get_node(me, node);
203      if ($(node.label_div).hasClass("absent")) $(node.get_children_ul()).addClass("absent");
204      $(node.get_children_ul()).slideDown("fast");
205    }
206    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
207    node.expanded = true;
208
209    // perform api level toggling because new nodes are new to the DOM
210    var selectedLevel = $("#apiLevelSelector option:selected").val();
211    toggleVisisbleApis(selectedLevel, "#side-nav");
212  }
213}
214
215function get_node(me, mom)
216{
217  mom.children_visited = true;
218  for (var i in mom.children_data) {
219    var node_data = mom.children_data[i];
220    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
221        node_data[2], node_data[3]);
222  }
223}
224
225function this_page_relative(toroot)
226{
227  var full = document.location.pathname;
228  var file = "";
229  if (toroot.substr(0, 1) == "/") {
230    if (full.substr(0, toroot.length) == toroot) {
231      return full.substr(toroot.length);
232    } else {
233      // the file isn't under toroot.  Fail.
234      return null;
235    }
236  } else {
237    if (toroot != "./") {
238      toroot = "./" + toroot;
239    }
240    do {
241      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
242        var pos = full.lastIndexOf("/");
243        file = full.substr(pos) + file;
244        full = full.substr(0, pos);
245        toroot = toroot.substr(0, toroot.length-3);
246      }
247    } while (toroot != "" && toroot != "/");
248    return file.substr(1);
249  }
250}
251
252function find_page(url, data)
253{
254  var nodes = data;
255  var result = null;
256  for (var i in nodes) {
257    var d = nodes[i];
258    if (d[1] == url) {
259      return new Array(i);
260    }
261    else if (d[2] != null) {
262      result = find_page(url, d[2]);
263      if (result != null) {
264        return (new Array(i).concat(result));
265      }
266    }
267  }
268  return null;
269}
270
271function load_navtree_data(toroot) {
272  var navtreeData = document.createElement("script");
273  navtreeData.setAttribute("type","text/javascript");
274  navtreeData.setAttribute("src", toroot+"navtree_data.js");
275  $("head").append($(navtreeData));
276}
277
278function init_default_navtree(toroot) {
279  init_navtree("nav-tree", toroot, NAVTREE_DATA);
280
281  // perform api level toggling because because the whole tree is new to the DOM
282  var selectedLevel = $("#apiLevelSelector option:selected").val();
283  toggleVisisbleApis(selectedLevel, "#side-nav");
284}
285
286function init_navtree(navtree_id, toroot, root_nodes)
287{
288  var me = new Object();
289  me.toroot = toroot;
290  me.node = new Object();
291
292  me.node.li = document.getElementById(navtree_id);
293  me.node.children_data = root_nodes;
294  me.node.children = new Array();
295  me.node.children_ul = document.createElement("ul");
296  me.node.get_children_ul = function() { return me.node.children_ul; };
297  //me.node.children_ul.className = "children_ul";
298  me.node.li.appendChild(me.node.children_ul);
299  me.node.depth = 0;
300
301  get_node(me, me.node);
302
303  me.this_page = this_page_relative(toroot);
304  me.breadcrumbs = find_page(me.this_page, root_nodes);
305  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
306    var mom = me.node;
307    for (var i in me.breadcrumbs) {
308      var j = me.breadcrumbs[i];
309      mom = mom.children[j];
310      expand_node(me, mom);
311    }
312    mom.label_div.className = mom.label_div.className + " selected";
313    addLoadEvent(function() {
314      scrollIntoView("nav-tree");
315      });
316  }
317}
318
319/* TOGGLE INHERITED MEMBERS */
320
321/* Toggle an inherited class (arrow toggle)
322 * @param linkObj  The link that was clicked.
323 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
324 *                'null' to simply toggle.
325 */
326function toggleInherited(linkObj, expand) {
327    var base = linkObj.getAttribute("id");
328    var list = document.getElementById(base + "-list");
329    var summary = document.getElementById(base + "-summary");
330    var trigger = document.getElementById(base + "-trigger");
331    var a = $(linkObj);
332    if ( (expand == null && a.hasClass("closed")) || expand ) {
333        list.style.display = "none";
334        summary.style.display = "block";
335        trigger.src = toRoot + "assets/images/triangle-opened.png";
336        a.removeClass("closed");
337        a.addClass("opened");
338    } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
339        list.style.display = "block";
340        summary.style.display = "none";
341        trigger.src = toRoot + "assets/images/triangle-closed.png";
342        a.removeClass("opened");
343        a.addClass("closed");
344    }
345    return false;
346}
347
348/* Toggle all inherited classes in a single table (e.g. all inherited methods)
349 * @param linkObj  The link that was clicked.
350 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
351 *                'null' to simply toggle.
352 */
353function toggleAllInherited(linkObj, expand) {
354  var a = $(linkObj);
355  var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
356  var expandos = $(".jd-expando-trigger", table);
357  if ( (expand == null && a.text() == "[Expand]") || expand ) {
358    expandos.each(function(i) {
359      toggleInherited(this, true);
360    });
361    a.text("[Collapse]");
362  } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
363    expandos.each(function(i) {
364      toggleInherited(this, false);
365    });
366    a.text("[Expand]");
367  }
368  return false;
369}
370
371/* Toggle all inherited members in the class (link in the class title)
372 */
373function toggleAllClassInherited() {
374  var a = $("#toggleAllClassInherited"); // get toggle link from class title
375  var toggles = $(".toggle-all", $("#doc-content"));
376  if (a.text() == "[Expand All]") {
377    toggles.each(function(i) {
378      toggleAllInherited(this, true);
379    });
380    a.text("[Collapse All]");
381  } else {
382    toggles.each(function(i) {
383      toggleAllInherited(this, false);
384    });
385    a.text("[Expand All]");
386  }
387  return false;
388}
389
390/* Expand all inherited members in the class. Used when initiating page search */
391function ensureAllInheritedExpanded() {
392  var toggles = $(".toggle-all", $("#doc-content"));
393  toggles.each(function(i) {
394    toggleAllInherited(this, true);
395  });
396  $("#toggleAllClassInherited").text("[Collapse All]");
397}
398
399
400/* HANDLE KEY EVENTS
401 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
402 */
403var agent = navigator['userAgent'].toLowerCase();
404var mac = agent.indexOf("macintosh") != -1;
405
406$(document).keydown( function(e) {
407var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
408  if (control && e.which == 70) {  // 70 is "F"
409    ensureAllInheritedExpanded();
410  }
411});