From f2f0511d3ed9cacb5436055c8d6124ab657667b8 Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Thu, 2 Jun 2016 16:20:55 -0400 Subject: [PATCH] track the expanded elements in the url This tracks the expanded list via a dedicated parameter, which expand all fills up. It uses the history api to ensure that urls that users create are sharable, including all the sections expanded correctly. Change-Id: I10d03226b15c9d4b880c42fb3c2ae0c05d8fb8ac --- os_api_ref/assets/api-site.js | 101 +++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/os_api_ref/assets/api-site.js b/os_api_ref/assets/api-site.js index 13d0f75..9775cd1 100644 --- a/os_api_ref/assets/api-site.js +++ b/os_api_ref/assets/api-site.js @@ -1,18 +1,39 @@ (function() { + // the list of expanded element ids + var expanded = []; + // whether we should sync expand changes with the location + // url. We need to make this false during large scale + // operations because we're using the history API, which is + // expensive. So a bulk expand turns this off, expands + // everything, turns it back on, then does a history sync. + var should_sync = true; $(document).ready(function() { - // Change the text on the expando buttons when appropriate + // Change the text on the expando buttons when + // appropriate. This also add or removes them to the list of + // expanded sections, and then syncs that list to the history + // after such a change. $('.api-detail') .on('hide.bs.collapse', function(e) { processButton(this, 'detail'); + var index = expanded.indexOf(this.id); + if (index > -1) { + expanded.splice(index, 1); + } + sync_expanded(); }) .on('show.bs.collapse', function(e) { processButton(this, 'close'); + expanded.push(this.id); + sync_expanded(); }); + // Expand the world. Wires up the expand all button, it turns + // off tye sync while it is running to save the costs with the + // history API. var expandAllActive = true; - // Expand the world $('#expand-all').click(function () { + should_sync = false; if (expandAllActive) { expandAllActive = false; $('.api-detail').collapse('show'); @@ -23,13 +44,32 @@ $('.api-detail').collapse('hide'); $('#expand-all').attr('data-toggle', 'collapse'); $(this).text('Show All'); - }}); + } + should_sync = true; + sync_expanded(); + }); - // if ?expand_all is in the query string, then expand all - // sections. This is useful for linking to nested elements, which - // only work if that element is expanded. - if (window.location.search.substring(1).indexOf("expand_all") > -1) { - $('#expand-all').click(); + // if there is an expanded parameter passed in a url, we run + // through and expand all the appropriate things. + if (window.location.search.substring(1).indexOf("expanded") > -1) { + should_sync = false; + var parts = window.location.search.substring(1).split('&'); + for (var i = 0; i < parts.length; i++) { + var keyval = parts[i].split('='); + if (keyval[0] == "expanded" && keyval[1]) { + var expanded_ids = keyval[1].split(','); + for (var j = 0; j < expanded_ids.length; j++) { + $('#' + expanded_ids[j]).collapse('show'); + } + } + } + should_sync = true; + // This is needed because the hash *might* be inside a + // collapsed section. + // + // NOTE(sdague): this doesn't quite seem to work while + // we're changing the rest of the document. + $(document.body).scrollTop($(window.location.hash).offset().top); } }); @@ -41,4 +81,49 @@ .toggleClass('btn-info') .toggleClass('btn-default'); } + + // Take the expanded array and push it into history. Because + // sphinx is building css appropriate ids, they should not have + // any special characters we need to encode. So we can simply join + // them into a comma separated list. + function sync_expanded() { + if (should_sync) { + var url = UpdateQueryString('expanded', expanded.join(',')); + history.pushState('', 'new expand', url); + } + } + + // Generically update the query string for a url. Credit to + // http://stackoverflow.com/questions/5999118/add-or-update-query-string-parameter + // for making this properly generic. + function UpdateQueryString(key, value, url) { + if (!url) url = window.location.href; + var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"), + hash; + + if (re.test(url)) { + if (typeof value !== 'undefined' && value !== null) + return url.replace(re, '$1' + key + "=" + value + '$2$3'); + else { + hash = url.split('#'); + url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); + if (typeof hash[1] !== 'undefined' && hash[1] !== null) + url += '#' + hash[1]; + return url; + } + } + else { + if (typeof value !== 'undefined' && value !== null) { + var separator = url.indexOf('?') !== -1 ? '&' : '?'; + hash = url.split('#'); + url = hash[0] + separator + key + '=' + value; + if (typeof hash[1] !== 'undefined' && hash[1] !== null) + url += '#' + hash[1]; + return url; + } + else + return url; + } + } + })();