diff options
author | 2010-03-01 18:35:13 -0800 | |
---|---|---|
committer | 2010-06-04 10:51:02 -0700 | |
commit | 78eff9f5bd340eb1d105a1825ad936765dee7b14 (patch) | |
tree | 8589fa8a93121ce6c08dffd4cf4287eb4e114ffb | |
parent | b9e8f1f0890b58f17dfcd3ff98f8b2515a22ef04 (diff) |
Revamp resources listing, part 2 (droiddoc).
- Fix docs navigation JS to correctly handle pages with query strings (i.e. browser.html?tag=article)
- Add resource browser CSS and JS to complement browser.jd in frameworks/base.
- Add microtemplate.js library for ease HTML templates in JS.
Change-Id: I518eeb5fc5a05dc6775eb3870eb88ebb0fc7b72c
13 files changed, 406 insertions, 23 deletions
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js index 4c59dd6420..d61ce72181 100644 --- a/tools/droiddoc/templates/assets/android-developer-docs.js +++ b/tools/droiddoc/templates/assets/android-developer-docs.js @@ -27,10 +27,10 @@ function addLoadEvent(newfun) { var agent = navigator['userAgent'].toLowerCase(); // If a mobile phone, set flag and do mobile setup -if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod +if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod (agent.indexOf("blackberry") != -1) || (agent.indexOf("webos") != -1) || - (agent.indexOf("mini") != -1)) { // opera mini browsers + (agent.indexOf("mini") != -1)) { // opera mini browsers isMobile = true; addLoadEvent(mobileSetup); // If not a mobile browser, set the onresize event for IE6, and others @@ -126,7 +126,7 @@ function writeCookie(cookie, val, section, expiration) { expiration = date.toGMTString(); } document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/"; -} +} function init() { $("#side-nav").css({position:"absolute",left:0}); @@ -162,11 +162,84 @@ function init() { } } - if (devdocNav.length) { // only dev guide and sdk - highlightNav(location.href); + if (devdocNav.length) { // only dev guide, resources, and sdk + tryPopulateResourcesNav(); + highlightNav(location.href); } } +function tryPopulateResourcesNav() { + var sampleList = $('#devdoc-nav-sample-list'); + var articleList = $('#devdoc-nav-article-list'); + var tutorialList = $('#devdoc-nav-tutorial-list'); + var topicList = $('#devdoc-nav-topic-list'); + + if (!topicList.length || !ANDROID_TAGS || !ANDROID_RESOURCES) + return; + + var topics = []; + for (var topic in ANDROID_TAGS['topic']) { + topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]}); + } + topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; }); + for (var i = 0; i < topics.length; i++) { + topicList.append( + $('<li>').append( + $('<a>') + .attr('href', toRoot + "resources/browser.html?tag=" + topics[i].name) + .append($('<span>') + .addClass('en') + .html(topics[i].title) + ) + ) + ); + } + + var _renderResourceList = function(tag, listNode) { + var resources = []; + var tags; + var resource; + var i, j; + for (i = 0; i < ANDROID_RESOURCES.length; i++) { + resource = ANDROID_RESOURCES[i]; + tags = resource.tags || []; + var hasTag = false; + for (j = 0; j < tags.length; j++) + if (tags[j] == tag) { + hasTag = true; + break; + } + if (!hasTag) + continue; + resources.push(resource); + } + //resources.sort(function(x,y){ return (x.title.en < y.title.en) ? -1 : 1; }); + for (i = 0; i < resources.length; i++) { + resource = resources[i]; + var listItemNode = $('<li>').append( + $('<a>') + .attr('href', toRoot + "resources/" + resource.path) + .append($('<span>') + .addClass('en') + .html(resource.title.en) + ) + ); + tags = resource.tags || []; + for (j = 0; j < tags.length; j++) { + if (tags[j] == 'new') { + listItemNode.get(0).innerHTML += ' <span class="new">new!</span>'; + break; + } + } + listNode.append(listItemNode); + } + }; + + _renderResourceList('sample', sampleList); + _renderResourceList('article', articleList); + _renderResourceList('tutorial', tutorialList); +} + function highlightNav(fullPageName) { var lastSlashPos = fullPageName.lastIndexOf("/"); var firstSlashPos; @@ -180,17 +253,23 @@ function highlightNav(fullPageName) { if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html') fullPageName = fullPageName + "index.html"; } - var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length); - var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); + // First check if the exact URL, with query string and all, is in the navigation menu + var pathPageName = fullPageName.substr(firstSlashPos); var link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); - if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/resources/") != -1))) { -// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide) - lastBackstep = pathPageName.lastIndexOf("/"); - while (link.length == 0) { - backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); - link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 1)+"index.html']"); - lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1); - if (lastBackstep == 0) break; + if (link.length == 0) { + var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length); + pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); // +5 advances past ".html" + link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); + if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/resources/") != -1))) { + // if there's no match, then let's backstep through the directory until we find an index.html page + // that matches our ancestor directories (only for dev guide and resources) + lastBackstep = pathPageName.lastIndexOf("/"); + while (link.length == 0) { + backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); + link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 1)+"index.html']"); + lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1); + if (lastBackstep == 0) break; + } } } @@ -436,12 +515,12 @@ function scrollIntoView(nav) { function changeTabLang(lang) { var nodes = $("#header-tabs").find("."+lang); - for (i=0; i < nodes.length; i++) { // for each node in this language + for (i=0; i < nodes.length; i++) { // for each node in this language var node = $(nodes[i]); - node.siblings().css("display","none"); // hide all siblings - if (node.not(":empty").length != 0) { //if this languages node has a translation, show it + node.siblings().css("display","none"); // hide all siblings + if (node.not(":empty").length != 0) { //if this languages node has a translation, show it node.css("display","inline"); - } else { //otherwise, show English instead + } else { //otherwise, show English instead node.css("display","none"); node.siblings().filter(".en").css("display","inline"); } @@ -450,12 +529,12 @@ function changeTabLang(lang) { function changeNavLang(lang) { var nodes = $("#side-nav").find("."+lang); - for (i=0; i < nodes.length; i++) { // for each node in this language + for (i=0; i < nodes.length; i++) { // for each node in this language var node = $(nodes[i]); - node.siblings().css("display","none"); // hide all siblings - if (node.not(":empty").length != 0) { // if this languages node has a translation, show it + node.siblings().css("display","none"); // hide all siblings + if (node.not(":empty").length != 0) { // if this languages node has a translation, show it node.css("display","inline"); - } else { // otherwise, show English instead + } else { // otherwise, show English instead node.css("display","none"); node.siblings().filter(".en").css("display","inline"); } diff --git a/tools/droiddoc/templates/assets/android-developer-resource-browser.css b/tools/droiddoc/templates/assets/android-developer-resource-browser.css new file mode 100644 index 0000000000..a454caaa4c --- /dev/null +++ b/tools/droiddoc/templates/assets/android-developer-resource-browser.css @@ -0,0 +1,31 @@ +/* Resource Browser */ + +#resource-browser-results .no-results { + font-style: italic; + display: none; +} + +#resource-browser-results .result { + position: relative; + padding-left: 84px; + background: transparent none no-repeat scroll 4px 12px; + border-bottom: 1px solid #ddd; +} + +#resource-browser-results .tagged-article { + background-image: url(images/resource-article.png); +} + +#resource-browser-results .tagged-sample { + background-image: url(images/resource-sample.png); +} + +#resource-browser-results .tagged-tutorial { + background-image: url(images/resource-tutorial.png); +} + +#resource-browser-results .resource-meta { + margin-top: -1em; + font-size: 0.85em; + font-weight: normal; +} diff --git a/tools/droiddoc/templates/assets/android-developer-resource-browser.js b/tools/droiddoc/templates/assets/android-developer-resource-browser.js new file mode 100644 index 0000000000..dc65aa2665 --- /dev/null +++ b/tools/droiddoc/templates/assets/android-developer-resource-browser.js @@ -0,0 +1,235 @@ +(function() { // anonymize + +var allTags = {}; +var loadedResults = []; + +/** + * Initialization code run upon the DOM being ready. + */ +$(document).ready(function() { + // Parse page query parameters. + var params = parseParams(document.location.search); + params.tag = params.tag ? makeArray(params.tag) : null; + + // Load tag and resource dataset. + loadTags(); + loadResources(); + + showResults(params); + + // Watch for keypresses in the keyword filter textbox, and update + // search results to reflect the keyword filter. + $('#resource-browser-keyword-filter').keyup(function() { + // Filter results on screen by keyword. + var keywords = $(this).val().split(/\s+/g); + for (var i = 0; i < loadedResults.length; i++) { + var hide = false; + for (var j = 0; j < keywords.length; j++) { + if (!resultMatchesKeyword(loadedResults[i].result, keywords[j])) { + hide = true; + break; + } + } + + loadedResults[i].node[hide ? 'hide' : 'show'](); + } + }); +}); + +/** + * Returns whether or not the given search result contains the given keyword. + */ +function resultMatchesKeyword(result, keyword) { + keyword = keyword.toLowerCase(); + if (result.title && + result.title.en.toLowerCase().indexOf(keyword) >= 0) + return true; + else if (result.description && + result.description.en.toLowerCase().indexOf(keyword) >= 0) + return true; + else if (result.topicsHtml && + result.topicsHtml.replace(/\<.*?\>/g,'').toLowerCase().indexOf(keyword) >= 0) + return true; + return false; +} + +/** + * Populates the allTags array with tag data from the ANDROID_TAGS + * variable in the resource data JS file. + */ +function loadTags() { + for (var tagClass in ANDROID_TAGS) { + for (var tag in ANDROID_TAGS[tagClass]) { + allTags[tag] = { + displayTag: ANDROID_TAGS[tagClass][tag], + tagClass: tagClass + }; + } + } +} + +/** + * Massage the ANDROID_RESOURCES resource list in the resource data JS file. + */ +function loadResources() { + for (var i = 0; i < ANDROID_RESOURCES.length; i++) { + var resource = ANDROID_RESOURCES[i]; + + // Convert the tags array to a tags hash for easier querying. + resource.tagsHash = {}; + for (var j = 0; j < resource.tags.length; j++) + resource.tagsHash[resource.tags[j]] = true; + + // Determine the type and topics of the resource by inspecting its tags. + resource.topics = []; + for (tag in resource.tagsHash) + if (tag in allTags) { + if (allTags[tag].tagClass == 'type') { + resource.type = tag; + } else if (allTags[tag].tagClass == 'topic') { + resource.topics.push(tag); + } + } + + // Add a humanized topics list string. + resource.topicsHtml = humanizeList(resource.topics, function(item) { + return '<strong>' + allTags[item].displayTag + '</strong>'; + }); + } +} + +/** + * Loads resources for the given query parameters. + */ +function showResults(params) { + loadedResults = []; + $('#resource-browser-search-params').empty(); + $('#resource-browser-results').empty(); + + var i, j; + var searchTags = []; + if (params.tag) { + for (i = 0; i < params.tag.length; i++) { + var tag = params.tag[i]; + if (tag.toLowerCase() in allTags) { + searchTags.push(tag.toLowerCase()); + } + } + } + + if (searchTags.length) { + // Show query params. + var taggedWithHtml = ['Showing technical resources tagged with ']; + taggedWithHtml.push(humanizeList(searchTags, function(item) { + return '<strong>' + allTags[item].displayTag + '</strong>'; + })); + $('#resource-browser-search-params').html(taggedWithHtml.join('') + ':'); + } else { + $('#resource-browser-search-params').html('Showing all technical resources:'); + } + + var results = []; + + // Create the list of resources to show. + for (i = 0; i < ANDROID_RESOURCES.length; i++) { + var resource = ANDROID_RESOURCES[i]; + var skip = false; + + if (searchTags.length) { + for (j = 0; j < searchTags.length; j++) + if (!(searchTags[j] in resource.tagsHash)) { + skip = true; + break; + } + + if (skip) + continue; + + results.push(resource); + continue; + } + + results.push(resource); + } + + // Format and show the list of resource results. + if (results.length) { + $('#resource-browser-results .no-results').hide(); + for (i = 0; i < results.length; i++) { + var result = results[i]; + var resultJqNode = $(tmpl('tmpl_resource_browser_result', result)); + for (tag in result.tagsHash) + resultJqNode.addClass('tagged-' + tag); + $('#resource-browser-results').append(resultJqNode); + + loadedResults.push({ node: resultJqNode, result: result }); + } + } else { + $('#resource-browser-results .no-results').show(); + } +} + +/** + * Formats the given array into a human readable, English string, ala + * 'a, b and c', with an optional item formatter/wrapper function. + */ +function humanizeList(arr, itemFormatter) { + itemFormatter = itemFormatter || function(o){ return o; }; + arr = arr || []; + + var out = []; + for (var i = 0; i < arr.length; i++) { + out.push(itemFormatter(arr[i]) + + ((i < arr.length - 2) ? ', ' : '') + + ((i == arr.length - 2) ? ' and ' : '')); + } + + return out.join(''); +} + +/** + * Parses a parameter string, i.e. foo=1&bar=2 into + * a dictionary object. + */ +function parseParams(paramStr) { + var params = {}; + paramStr = paramStr.replace(/^[?#]/, ''); + + var pairs = paramStr.split('&'); + for (var i = 0; i < pairs.length; i++) { + var p = pairs[i].split('='); + var key = p[0] ? decodeURIComponent(p[0]) : p[0]; + var val = p[1] ? decodeURIComponent(p[1]) : p[1]; + if (val === '0') + val = 0; + if (val === '1') + val = 1; + + if (key in params) { + // Handle array values. + params[key] = makeArray(params[key]); + params[key].push(val); + } else { + params[key] = val; + } + } + + return params; +} + +/** + * Returns the argument as a single-element array, or the argument itself + * if it's already an array. + */ +function makeArray(o) { + if (!o) + return []; + + if (typeof o === 'object' && 'splice' in o) { + return o; + } else { + return [o]; + } +} + +})(); diff --git a/tools/droiddoc/templates/assets/images/resource-article.png b/tools/droiddoc/templates/assets/images/resource-article.png Binary files differnew file mode 100644 index 0000000000..416493f8c2 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-article.png diff --git a/tools/droiddoc/templates/assets/images/resource-big-article.png b/tools/droiddoc/templates/assets/images/resource-big-article.png Binary files differnew file mode 100644 index 0000000000..72732754b9 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-big-article.png diff --git a/tools/droiddoc/templates/assets/images/resource-big-sample.png b/tools/droiddoc/templates/assets/images/resource-big-sample.png Binary files differnew file mode 100644 index 0000000000..59b6b68bcb --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-big-sample.png diff --git a/tools/droiddoc/templates/assets/images/resource-big-tutorial.png b/tools/droiddoc/templates/assets/images/resource-big-tutorial.png Binary files differnew file mode 100644 index 0000000000..c32e89a584 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-big-tutorial.png diff --git a/tools/droiddoc/templates/assets/images/resource-big-video.png b/tools/droiddoc/templates/assets/images/resource-big-video.png Binary files differnew file mode 100644 index 0000000000..59d46a0a15 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-big-video.png diff --git a/tools/droiddoc/templates/assets/images/resource-sample.png b/tools/droiddoc/templates/assets/images/resource-sample.png Binary files differnew file mode 100644 index 0000000000..f7a411cafb --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-sample.png diff --git a/tools/droiddoc/templates/assets/images/resource-tutorial.png b/tools/droiddoc/templates/assets/images/resource-tutorial.png Binary files differnew file mode 100644 index 0000000000..10a14fec1e --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-tutorial.png diff --git a/tools/droiddoc/templates/assets/images/resource-video.png b/tools/droiddoc/templates/assets/images/resource-video.png Binary files differnew file mode 100644 index 0000000000..8fd5cae4c9 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/resource-video.png diff --git a/tools/droiddoc/templates/assets/microtemplate.js b/tools/droiddoc/templates/assets/microtemplate.js new file mode 100644 index 0000000000..ada1235f94 --- /dev/null +++ b/tools/droiddoc/templates/assets/microtemplate.js @@ -0,0 +1,35 @@ +// Simple JavaScript Templating +// John Resig - http://ejohn.org/ - MIT Licensed +(function(){ + var cache = {}; + + this.tmpl = function tmpl(str, data){ + // Figure out if we're getting a template, or if we need to + // load the template - and be sure to cache the result. + var fn = !/\W/.test(str) ? + cache[str] = cache[str] || + tmpl(document.getElementById(str).innerHTML) : + + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + + + // Introduce the data as local variables using with(){} + "with(obj){p.push('" + + + // Convert the template into pure JavaScript + str + .replace(/[\r\t\n]/g, " ") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "',$1,'") + .split("\t").join("');") + .split("%>").join("p.push('") + .split("\r").join("\\'") + + "');}return p.join('');"); + + // Provide some basic currying to the user + return data ? fn( data ) : fn; + }; +})();
\ No newline at end of file diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs index 5a7fd40401..b418e1e9b9 100644 --- a/tools/droiddoc/templates/head_tag.cs +++ b/tools/droiddoc/templates/head_tag.cs @@ -20,6 +20,9 @@ else ?> if:reference ?> <script src="<?cs var:toroot ?>assets/android-developer-reference.js" type="text/javascript"></script> <script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script><?cs +/if ?><?cs +if:resources ?> +<script src="<?cs var:toroot ?>resources/resources-data.js" type="text/javascript"></script><?cs /if ?> <noscript> <style type="text/css"> |