]> git.lizzy.rs Git - rust.git/blobdiff - src/librustdoc/html/static/main.js
Rollup merge of #81379 - GuillaumeGomez:improve-urls, r=Nemo157
[rust.git] / src / librustdoc / html / static / main.js
index ec8024ffca5294894206c0b6acdbd0d7456954aa..7e613220f2d696a13223e00c4c3726125a93f236 100644 (file)
@@ -1,9 +1,6 @@
-// From rust:
-/* global ALIASES, currentCrate, rootPath */
-
 // Local js definitions:
-/* global addClass, getCurrentValue, hasClass */
-/* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
+/* global addClass, getSettingValue, hasClass */
+/* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
 /* global hideThemeButtonState, showThemeButtonState */
 
 if (!String.prototype.startsWith) {
@@ -40,6 +37,21 @@ if (!DOMTokenList.prototype.remove) {
     };
 }
 
+(function () {
+    var rustdocVars = document.getElementById("rustdoc-vars");
+    if (rustdocVars) {
+        window.rootPath = rustdocVars.attributes["data-root-path"].value;
+        window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
+    }
+    var sidebarVars = document.getElementById("sidebar-vars");
+    if (sidebarVars) {
+        window.sidebarCurrent = {
+            name: sidebarVars.attributes["data-name"].value,
+            ty: sidebarVars.attributes["data-ty"].value,
+            relpath: sidebarVars.attributes["data-relpath"].value,
+        };
+    }
+}());
 
 // Gets the human-readable string for the virtual-key code of the
 // given KeyboardEvent, ev.
@@ -79,6 +91,11 @@ function getThemePickerElement() {
     return document.getElementById("theme-picker");
 }
 
+// Returns the current URL without any query parameter or hash.
+function getNakedUrl() {
+    return window.location.href.split("?")[0].split("#")[0];
+}
+
 // Sets the focus on the search bar at the top of the page
 function focusSearchBar() {
     getSearchInput().focus();
@@ -240,7 +257,9 @@ function defocusSearchBar() {
             hideSearchResults(search);
             var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
             if (browserSupportsHistoryApi()) {
-                history.replaceState(hash, "", "?search=#" + hash);
+                // `window.location.search`` contains all the query parameters, not just `search`.
+                history.replaceState(hash, "",
+                    getNakedUrl() + window.location.search + "#" + hash);
             }
             elem = document.getElementById(hash);
             if (elem) {
@@ -565,7 +584,7 @@ function defocusSearchBar() {
                 var i, match,
                     url = document.location.href,
                     stripped = "",
-                    len = rootPath.match(/\.\.\//g).length + 1;
+                    len = window.rootPath.match(/\.\.\//g).length + 1;
 
                 for (i = 0; i < len; ++i) {
                     match = url.match(/\/[^\/]*$/);
@@ -648,8 +667,7 @@ function defocusSearchBar() {
          */
         function execQuery(query, searchWords, filterCrates) {
             function itemTypeFromName(typename) {
-                var length = itemTypes.length;
-                for (var i = 0; i < length; ++i) {
+                for (var i = 0, len = itemTypes.length; i < len; ++i) {
                     if (itemTypes[i] === typename) {
                         return i;
                     }
@@ -667,8 +685,7 @@ function defocusSearchBar() {
 
             function transformResults(results, isType) {
                 var out = [];
-                var length = results.length;
-                for (var i = 0; i < length; ++i) {
+                for (var i = 0, len = results.length; i < len; ++i) {
                     if (results[i].id > -1) {
                         var obj = searchIndex[results[i].id];
                         obj.lev = results[i].lev;
@@ -697,11 +714,11 @@ function defocusSearchBar() {
                     }
                 }
                 results = ar;
-                var i;
-                var nresults = results.length;
-                for (i = 0; i < nresults; ++i) {
-                    results[i].word = searchWords[results[i].id];
-                    results[i].item = searchIndex[results[i].id] || {};
+                var i, len, result;
+                for (i = 0, len = results.length; i < len; ++i) {
+                    result = results[i];
+                    result.word = searchWords[result.id];
+                    result.item = searchIndex[result.id] || {};
                 }
                 // if there are no results then return to default and fail
                 if (results.length === 0) {
@@ -775,8 +792,7 @@ function defocusSearchBar() {
                     return 0;
                 });
 
-                var length = results.length;
-                for (i = 0; i < length; ++i) {
+                for (i = 0, len = results.length; i < len; ++i) {
                     var result = results[i];
 
                     // this validation does not make sense when searching by types
@@ -833,11 +849,10 @@ function defocusSearchBar() {
                         var vlength = val.generics.length;
                         for (var y = 0; y < vlength; ++y) {
                             var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
-                            var elength = elems.length;
                             var firstGeneric = getObjectFromId(val.generics[y]).name;
-                            for (var x = 0; x < elength; ++x) {
+                            for (var x = 0, elength = elems.length; x < elength; ++x) {
                                 var tmp_lev = levenshtein(getObjectFromId(elems[x]).name,
-                                                          firstGeneric);
+                                                                          firstGeneric);
                                 if (tmp_lev < lev.lev) {
                                     lev.lev = tmp_lev;
                                     lev.pos = x;
@@ -861,7 +876,7 @@ function defocusSearchBar() {
             // Check for type name and type generics (if any).
             function checkType(obj, val, literalSearch) {
                 var lev_distance = MAX_LEV_DISTANCE + 1;
-                var x;
+                var len, x, y, e_len, firstGeneric;
                 if (obj[NAME] === val.name) {
                     if (literalSearch === true) {
                         if (val.generics && val.generics.length !== 0) {
@@ -870,10 +885,12 @@ function defocusSearchBar() {
                                 var elems = obj[GENERICS_DATA].slice(0);
                                 var allFound = true;
 
-                                for (var y = 0; allFound === true && y < val.generics.length; ++y) {
+                                len = val.generics.length;
+                                for (y = 0; allFound === true && y < len; ++y) {
                                     allFound = false;
-                                    var firstGeneric = getObjectFromId(val.generics[y]).name;
-                                    for (x = 0; allFound === false && x < elems.length; ++x) {
+                                    firstGeneric = getObjectFromId(val.generics[y]).name;
+                                    e_len = elems.length;
+                                    for (x = 0; allFound === false && x < e_len; ++x) {
                                         allFound = getObjectFromId(elems[x]).name === firstGeneric;
                                     }
                                     if (allFound === true) {
@@ -903,12 +920,10 @@ function defocusSearchBar() {
                 // Names didn't match so let's check if one of the generic types could.
                 if (literalSearch === true) {
                      if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                        var length = obj[GENERICS_DATA].length;
-                        for (x = 0; x < length; ++x) {
-                            if (obj[GENERICS_DATA][x] === val.name) {
-                                return true;
-                            }
-                        }
+                        return obj[GENERICS_DATA].some(
+                            function(name) {
+                                return name === val.name;
+                            });
                     }
                     return false;
                 }
@@ -965,7 +980,7 @@ function defocusSearchBar() {
                     if (typeof ret[0] === "string") {
                         ret = [ret];
                     }
-                    for (var x = 0; x < ret.length; ++x) {
+                    for (var x = 0, len = ret.length; x < len; ++x) {
                         var tmp = ret[x];
                         if (typePassesFilter(typeFilter, tmp[1]) === false) {
                             continue;
@@ -1072,23 +1087,22 @@ function defocusSearchBar() {
                 // aliases to be before the others in the displayed results.
                 var aliases = [];
                 var crateAliases = [];
-                var i;
                 if (filterCrates !== undefined) {
                     if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                        for (i = 0; i < ALIASES[filterCrates][query.search].length; ++i) {
-                            aliases.push(
-                                createAliasFromItem(
-                                    searchIndex[ALIASES[filterCrates][query.search][i]]));
+                        var query_aliases = ALIASES[filterCrates][query.search];
+                        var len = query_aliases.length;
+                        for (var i = 0; i < len; ++i) {
+                            aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
                         }
                     }
                 } else {
                     Object.keys(ALIASES).forEach(function(crate) {
                         if (ALIASES[crate][query.search]) {
                             var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                            for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
-                                pushTo.push(
-                                    createAliasFromItem(
-                                        searchIndex[ALIASES[crate][query.search][i]]));
+                            var query_aliases = ALIASES[crate][query.search];
+                            var len = query_aliases.length;
+                            for (var i = 0; i < len; ++i) {
+                                pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
                             }
                         }
                     });
@@ -1123,11 +1137,12 @@ function defocusSearchBar() {
 
             // quoted values mean literal search
             var nSearchWords = searchWords.length;
-            var i;
+            var i, it;
             var ty;
             var fullId;
             var returned;
             var in_args;
+            var len;
             if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
                 val.charAt(val.length - 1) === val.charAt(0))
             {
@@ -1175,7 +1190,7 @@ function defocusSearchBar() {
                 var input = parts[0];
                 // sort inputs so that order does not matter
                 var inputs = input.split(",").map(trimmer).sort();
-                for (i = 0; i < inputs.length; ++i) {
+                for (i = 0, len = inputs.length; i < len; ++i) {
                     inputs[i] = extractGenerics(inputs[i]);
                 }
                 var output = extractGenerics(parts[1]);
@@ -1200,7 +1215,7 @@ function defocusSearchBar() {
                             is_module = true;
                         } else {
                             var allFound = true;
-                            for (var it = 0; allFound === true && it < inputs.length; it++) {
+                            for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
                                 allFound = checkType(type, inputs[it], true);
                             }
                             in_args = allFound;
@@ -1243,7 +1258,7 @@ function defocusSearchBar() {
 
                 var paths = valLower.split("::");
                 var j;
-                for (j = 0; j < paths.length; ++j) {
+                for (j = 0, len = paths.length; j < len; ++j) {
                     if (paths[j] === "") {
                         paths.splice(j, 1);
                         j -= 1;
@@ -1365,7 +1380,7 @@ function defocusSearchBar() {
          * @return {[boolean]}       [Whether the result is valid or not]
          */
         function validateResult(name, path, keys, parent) {
-            for (var i = 0; i < keys.length; ++i) {
+            for (var i = 0, len = keys.length; i < len; ++i) {
                 // each check is for validation so we negate the conditions and invalidate
                 if (!(
                     // check for an exact name match
@@ -1508,15 +1523,15 @@ function defocusSearchBar() {
 
             if (type === "mod") {
                 displayPath = path + "::";
-                href = rootPath + path.replace(/::/g, "/") + "/" +
+                href = window.rootPath + path.replace(/::/g, "/") + "/" +
                        name + "/index.html";
             } else if (type === "primitive" || type === "keyword") {
                 displayPath = "";
-                href = rootPath + path.replace(/::/g, "/") +
+                href = window.rootPath + path.replace(/::/g, "/") +
                        "/" + type + "." + name + ".html";
             } else if (type === "externcrate") {
                 displayPath = "";
-                href = rootPath + name + "/index.html";
+                href = window.rootPath + name + "/index.html";
             } else if (item.parent !== undefined) {
                 var myparent = item.parent;
                 var anchor = "#" + type + "." + name;
@@ -1539,13 +1554,13 @@ function defocusSearchBar() {
                 } else {
                     displayPath = path + "::" + myparent.name + "::";
                 }
-                href = rootPath + path.replace(/::/g, "/") +
+                href = window.rootPath + path.replace(/::/g, "/") +
                        "/" + pageType +
                        "." + pageName +
                        ".html" + anchor;
             } else {
                 displayPath = item.path + "::";
-                href = rootPath + item.path.replace(/::/g, "/") +
+                href = window.rootPath + item.path.replace(/::/g, "/") +
                        "/" + type + "." + name + ".html";
             }
             return [displayPath, href];
@@ -1654,6 +1669,21 @@ function defocusSearchBar() {
             var ret_in_args = addTab(results.in_args, query, false);
             var ret_returned = addTab(results.returned, query, false);
 
+            // Navigate to the relevant tab if the current tab is empty, like in case users search
+            // for "-> String". If they had selected another tab previously, they have to click on
+            // it again.
+            if ((currentTab === 0 && ret_others[1] === 0) ||
+                    (currentTab === 1 && ret_in_args[1] === 0) ||
+                    (currentTab === 2 && ret_returned[1] === 0)) {
+                if (ret_others[1] !== 0) {
+                    currentTab = 0;
+                } else if (ret_in_args[1] !== 0) {
+                    currentTab = 1;
+                } else if (ret_returned[1] !== 0) {
+                    currentTab = 2;
+                }
+            }
+
             var output = "<h1>Results for " + escape(query.query) +
                 (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
                 "<div id=\"titles\">" +
@@ -1686,7 +1716,7 @@ function defocusSearchBar() {
             function getSmallest(arrays, positions, notDuplicates) {
                 var start = null;
 
-                for (var it = 0; it < positions.length; ++it) {
+                for (var it = 0, len = positions.length; it < len; ++it) {
                     if (arrays[it].length > positions[it] &&
                         (start === null || start > arrays[it][positions[it]].lev) &&
                         !notDuplicates[arrays[it][positions[it]].fullPath]) {
@@ -1701,7 +1731,7 @@ function defocusSearchBar() {
                 var positions = [];
                 var notDuplicates = {};
 
-                for (var x = 0; x < arrays.length; ++x) {
+                for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
                     positions.push(0);
                 }
                 while (ret.length < MAX_RESULTS) {
@@ -1710,7 +1740,7 @@ function defocusSearchBar() {
                     if (smallest === null) {
                         break;
                     }
-                    for (x = 0; x < arrays.length && ret.length < MAX_RESULTS; ++x) {
+                    for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
                         if (arrays[x].length > positions[x] &&
                                 arrays[x][positions[x]].lev === smallest &&
                                 !notDuplicates[arrays[x][positions[x]].fullPath]) {
@@ -1730,7 +1760,7 @@ function defocusSearchBar() {
                 "others": [],
             };
 
-            for (var i = 0; i < queries.length; ++i) {
+            for (var i = 0, len = queries.length; i < len; ++i) {
                 query = queries[i].trim();
                 if (query.length !== 0) {
                     var tmp = execQuery(getQuery(query), searchWords, filterCrates);
@@ -1787,10 +1817,12 @@ function defocusSearchBar() {
             // Because searching is incremental by character, only the most
             // recent search query is added to the browser history.
             if (browserSupportsHistoryApi()) {
+                var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
+                    window.location.hash;
                 if (!history.state && !params.search) {
-                    history.pushState(query, "", "?search=" + encodeURIComponent(query.raw));
+                    history.pushState(query, "", newURL);
                 } else {
-                    history.replaceState(query, "", "?search=" + encodeURIComponent(query.raw));
+                    history.replaceState(query, "", newURL);
                 }
             }
 
@@ -1884,7 +1916,7 @@ function defocusSearchBar() {
                             ALIASES[crate][alias_name] = [];
                         }
                         local_aliases = aliases[alias_name];
-                        for (j = 0; j < local_aliases.length; ++j) {
+                        for (j = 0, len = local_aliases.length; j < len; ++j) {
                             ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
                         }
                     }
@@ -1899,7 +1931,8 @@ function defocusSearchBar() {
                 clearInputTimeout();
                 if (search_input.value.length === 0) {
                     if (browserSupportsHistoryApi()) {
-                        history.replaceState("", window.currentCrate + " - Rust", "?search=");
+                        history.replaceState("", window.currentCrate + " - Rust",
+                            getNakedUrl() + window.location.hash);
                     }
                     hideSearchResults();
                 } else {
@@ -1977,7 +2010,7 @@ function defocusSearchBar() {
         startSearch();
 
         // Draw a convenient sidebar of known crates if we have a listing
-        if (rootPath === "../" || rootPath === "./") {
+        if (window.rootPath === "../" || window.rootPath === "./") {
             var sidebar = document.getElementsByClassName("sidebar-elems")[0];
             if (sidebar) {
                 var div = document.createElement("div");
@@ -1996,11 +2029,11 @@ function defocusSearchBar() {
                 crates.sort();
                 for (var i = 0; i < crates.length; ++i) {
                     var klass = "crate";
-                    if (rootPath !== "./" && crates[i] === window.currentCrate) {
+                    if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
                         klass += " current";
                     }
                     var link = document.createElement("a");
-                    link.href = rootPath + crates[i] + "/index.html";
+                    link.href = window.rootPath + crates[i] + "/index.html";
                     // The summary in the search index has HTML, so we need to
                     // dynamically render it as plaintext.
                     link.title = convertHTMLToPlaintext(rawSearchIndex[crates[i]].doc);
@@ -2052,8 +2085,7 @@ function defocusSearchBar() {
             div.appendChild(h3);
             var ul = document.createElement("ul");
 
-            var length = filtered.length;
-            for (var i = 0; i < length; ++i) {
+            for (var i = 0, len = filtered.length; i < len; ++i) {
                 var item = filtered[i];
                 var name = item[0];
                 var desc = item[1]; // can be null
@@ -2122,21 +2154,18 @@ function defocusSearchBar() {
         }
 
         var libs = Object.getOwnPropertyNames(imp);
-        var llength = libs.length;
-        for (var i = 0; i < llength; ++i) {
-            if (libs[i] === currentCrate) { continue; }
+        for (var i = 0, llength = libs.length; i < llength; ++i) {
+            if (libs[i] === window.currentCrate) { continue; }
             var structs = imp[libs[i]];
 
-            var slength = structs.length;
             struct_loop:
-            for (var j = 0; j < slength; ++j) {
+            for (var j = 0, slength = structs.length; j < slength; ++j) {
                 var struct = structs[j];
 
                 var list = struct.synthetic ? synthetic_implementors : implementors;
 
                 if (struct.synthetic) {
-                    var stlength = struct.types.length;
-                    for (var k = 0; k < stlength; k++) {
+                    for (var k = 0, stlength = struct.types.length; k < stlength; k++) {
                         if (inlined_types.has(struct.types[k])) {
                             continue struct_loop;
                         }
@@ -2151,7 +2180,7 @@ function defocusSearchBar() {
                     var href = elem.getAttribute("href");
 
                     if (href && href.indexOf("http") !== 0) {
-                        elem.setAttribute("href", rootPath + href);
+                        elem.setAttribute("href", window.rootPath + href);
                     }
                 });
 
@@ -2192,7 +2221,7 @@ function defocusSearchBar() {
         }
     }
 
-    function toggleAllDocs(pageId, fromAutoCollapse) {
+    function toggleAllDocs(fromAutoCollapse) {
         var innerToggle = document.getElementById(toggleAllDocsId);
         if (!innerToggle) {
             return;
@@ -2235,14 +2264,14 @@ function defocusSearchBar() {
                     }
                     if (!parent || !superParent || superParent.id !== "main" ||
                         hasClass(parent, "impl") === false) {
-                        collapseDocs(e, "hide", pageId);
+                        collapseDocs(e, "hide");
                     }
                 });
             }
         }
     }
 
-    function collapseDocs(toggle, mode, pageId) {
+    function collapseDocs(toggle, mode) {
         if (!toggle || !toggle.parentNode) {
             return;
         }
@@ -2362,27 +2391,27 @@ function defocusSearchBar() {
         }
     }
 
-    function collapser(pageId, e, collapse) {
+    function collapser(e, collapse) {
         // inherent impl ids are like "impl" or impl-<number>'.
         // they will never be hidden by default.
         var n = e.parentElement;
         if (n.id.match(/^impl(?:-\d+)?$/) === null) {
             // Automatically minimize all non-inherent impls
             if (collapse || hasClass(n, "impl")) {
-                collapseDocs(e, "hide", pageId);
+                collapseDocs(e, "hide");
             }
         }
     }
 
-    function autoCollapse(pageId, collapse) {
+    function autoCollapse(collapse) {
         if (collapse) {
-            toggleAllDocs(pageId, true);
+            toggleAllDocs(true);
         } else if (getSettingValue("auto-hide-trait-implementations") !== "false") {
             var impl_list = document.getElementById("trait-implementations-list");
 
             if (impl_list !== null) {
                 onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
-                    collapser(pageId, e, collapse);
+                    collapser(e, collapse);
                 });
             }
 
@@ -2390,7 +2419,7 @@ function defocusSearchBar() {
 
             if (blanket_list !== null) {
                 onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
-                    collapser(pageId, e, collapse);
+                    collapser(e, collapse);
                 });
             }
         }
@@ -2453,7 +2482,6 @@ function defocusSearchBar() {
         var toggle = createSimpleToggle(false);
         var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
         var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false";
-        var pageId = getPageId();
 
         var func = function(e) {
             var next = e.nextElementSibling;
@@ -2467,7 +2495,7 @@ function defocusSearchBar() {
                 var newToggle = toggle.cloneNode(true);
                 insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]);
                 if (hideMethodDocs === true && hasClass(e, "method") === true) {
-                    collapseDocs(newToggle, "hide", pageId);
+                    collapseDocs(newToggle, "hide");
                 }
             }
         };
@@ -2491,7 +2519,7 @@ function defocusSearchBar() {
                 // In case the option "auto-collapse implementors" is not set to false, we collapse
                 // all implementors.
                 if (hideImplementors === true && e.parentNode.id === "implementors-list") {
-                    collapseDocs(newToggle, "hide", pageId);
+                    collapseDocs(newToggle, "hide");
                 }
             }
         };
@@ -2505,7 +2533,7 @@ function defocusSearchBar() {
                 if (e.id.match(/^impl(?:-\d+)?$/) === null) {
                     // Automatically minimize all non-inherent impls
                     if (hasClass(e, "impl") === true) {
-                        collapseDocs(newToggle, "hide", pageId);
+                        collapseDocs(newToggle, "hide");
                     }
                 }
             };
@@ -2540,14 +2568,12 @@ function defocusSearchBar() {
         }
         onEachLazy(document.getElementsByClassName("impl-items"), function(e) {
             onEachLazy(e.getElementsByClassName("associatedconstant"), func);
-            var hiddenElems = e.getElementsByClassName("hidden");
-            var needToggle = false;
-
-            var needToggle = onEachLazy(e.getElementsByClassName("hidden"), function(hiddenElem) {
-                if (hasClass(hiddenElem, "content") === false &&
-                    hasClass(hiddenElem, "docblock") === false) {
-                    return true;
-                }
+            // We transform the DOM iterator into a vec of DOM elements to prevent performance
+            // issues on webkit browsers.
+            var hiddenElems = Array.prototype.slice.call(e.getElementsByClassName("hidden"));
+            var needToggle = hiddenElems.some(function(hiddenElem) {
+                return hasClass(hiddenElem, "content") === false &&
+                    hasClass(hiddenElem, "docblock") === false;
             });
             if (needToggle === true) {
                 var inner_toggle = newToggle.cloneNode(true);
@@ -2650,10 +2676,10 @@ function defocusSearchBar() {
 
         onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper);
         onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper);
-        var pageId = getPageId();
 
-        autoCollapse(pageId, getSettingValue("collapse") === "true");
+        autoCollapse(getSettingValue("collapse") === "true");
 
+        var pageId = getPageId();
         if (pageId !== null) {
             expandSection(pageId);
         }
@@ -2763,9 +2789,9 @@ function defocusSearchBar() {
         if (search_input.value !== "" && hasClass(search, "hidden")) {
             showSearchResults(search);
             if (browserSupportsHistoryApi()) {
-                history.replaceState(search_input.value,
-                                     "",
-                                     "?search=" + encodeURIComponent(search_input.value));
+                var extra = "?search=" + encodeURIComponent(search_input.value);
+                history.replaceState(search_input.value, "",
+                    getNakedUrl() + extra + window.location.hash);
             }
             document.title = searchTitle;
         }
@@ -2853,7 +2879,7 @@ function defocusSearchBar() {
             return 0;
         });
         var savedCrate = getSettingValue("saved-filter-crate");
-        for (var i = 0; i < crates_text.length; ++i) {
+        for (var i = 0, len = crates_text.length; i < len; ++i) {
             var option = document.createElement("option");
             option.value = crates_text[i];
             option.innerText = crates_text[i];