Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 3 additions & 44 deletions js/search-index.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,9 @@
header("Location: http://php.net");
}

/*
$types = array(
"phpdoc:varentry",
"refentry",
"phpdoc:exceptionref",
"phpdoc:classref",
"section",
"chapter",
"book",
"reference",
"set",
"appendix",
"article",
);
*/
$combinedIndex = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-combined.json";
$tsstring = gmdate("D, d M Y H:i:s ", filemtime($combinedIndex)) . "GMT";

$indexfile = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-index.json";
$descfile = $_SERVER["DOCUMENT_ROOT"] . "/manual/$lang/search-description.json";

/* {{{ Cache this */
$time = max(filemtime($indexfile), filemtime($descfile));
$tsstring = gmdate("D, d M Y H:i:s ", $time) . "GMT";
if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) &&
($_SERVER["HTTP_IF_MODIFIED_SINCE"] == $tsstring)) {
header("HTTP/1.1 304 Not Modified");
Expand All @@ -41,26 +22,4 @@

header("Last-Modified: " . $tsstring);
header("Content-Type: application/javascript");
/* }}} */

$s = file_get_contents($indexfile);
$js = json_decode($s, true);

$index = [];
foreach ($js as $item) {
if ($item[0]) {
/* key: ID/filename, 0=>*/
$index[$item[1]] = [$item[0], "", $item[2]];
}
}

$s = file_get_contents($descfile);
$js = json_decode($s, true);

foreach ($js as $k => $item) {
if ($item && isset($index[$k])) {
$index[$k][1] = $item;
}
}

echo json_encode($index);
readfile($combinedIndex);
128 changes: 42 additions & 86 deletions js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,78 +11,21 @@ const initPHPSearch = async (language) => {
const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
const CACHE_DAYS = 14;

/**
* Converts the structure from search-index.php into an array of objects,
* mapping the index entries to their respective types.
*
* @param {object} index
* @returns {Array}
*/
const processIndex = (index) => {
return Object.entries(index)
.map(([id, [name, description, tag]]) => {
if (!name) return null;

let type = "General";
switch (tag) {
case "phpdoc:varentry":
type = "Variable";
break;

case "refentry":
type = "Function";
break;

case "phpdoc:exceptionref":
type = "Exception";
break;

case "phpdoc:classref":
type = "Class";
break;

case "set":
case "book":
case "reference":
type = "Extension";
break;
}

return {
id,
name,
description,
tag,
type,
methodName: name.split("::").pop(),
};
})
.filter(Boolean);
};

/**
* Looks up the search index cached in localStorage.
*
* @returns {Array|null}
*/
const lookupIndexCache = () => {
const key = `search-${language}`;
const key = `search2-${language}`;
const cache = window.localStorage.getItem(key);

if (!cache) {
if ((!cache) || (language === 'local')) {
return null;
}

const { data, time: cachedDate } = JSON.parse(cache);

// Invalidate old search cache format (previously an object)
// TODO: Remove this check once the new search index (a single array)
// has been in use for a while.
if (!Array.isArray(data)) {
console.log("Invalidating old search cache format");
return null;
}

const expireDate = cachedDate + CACHE_DAYS * MILLISECONDS_PER_DAY;

if (Date.now() > expireDate) {
Expand All @@ -98,23 +41,27 @@ const initPHPSearch = async (language) => {
* @returns {Promise<Array>} The search index.
*/
const fetchIndex = async () => {
const key = `search-${language}`;
const response = await fetch(`/js/search-index.php?lang=${language}`);
const data = await response.json();
const items = processIndex(data);

try {
localStorage.setItem(
key,
JSON.stringify({
data: items,
time: Date.now(),
}),
);
} catch (e) {
// Local storage might be full, or other error.
// Just continue without caching.
console.error("Failed to cache search index", e);
const key = `search2-${language}`;
let items;
if (language === 'local') {
items = localSearchIndexes;
} else {
const response = await fetch(`/js/search-index.php?lang=${language}`);
items = await response.json();

try {
localStorage.setItem(
key,
JSON.stringify({
data: items,
time: Date.now(),
}),
);
} catch (e) {
// Local storage might be full, or other error.
// Just continue without caching.
console.error("Failed to cache search index", e);
}
}

return items;
Expand All @@ -139,7 +86,7 @@ const initPHPSearch = async (language) => {
try {
return await loadIndex();
} catch (error) {
if (language !== "en") {
if ((language !== "en") && (language !== "local")) {
language = "en";
return loadIndexWithFallback();
}
Expand Down Expand Up @@ -200,7 +147,7 @@ const initSearchModal = () => {
const inputElement = document.getElementById("search-modal__input");

const focusTrapHandler = (event) => {
if (event.key != "Tab") {
if (event.key !== "Tab") {
return;
}

Expand Down Expand Up @@ -276,19 +223,25 @@ const initSearchModal = () => {
"navbar__search-button-mobile",
);
const searchButton = document.getElementById("navbar__search-button");
let buttons = [searchButton];

// Enhance mobile search
searchLink.setAttribute("hidden", "true");
searchButtonMobile.removeAttribute("hidden");
if (searchLink !== null) {
searchLink.setAttribute("hidden", "true");
searchButtonMobile.removeAttribute("hidden");
buttons.push(searchButtonMobile);
}

// Enhance desktop search
document
.querySelector(".navbar__search-form")
.setAttribute("hidden", "true");
const searchForm = document
.querySelector(".navbar__search-form");
if (searchForm !== null) {
searchForm.setAttribute("hidden", "true");
}
searchButton.removeAttribute("hidden");

// Open when the search button is clicked
[searchButton, searchButtonMobile].forEach((button) =>
buttons.forEach((button) =>
button.addEventListener("click", show),
);

Expand Down Expand Up @@ -390,7 +343,10 @@ const initSearchUI = ({ searchCallback, language, limit = 30 }) => {
const icon = ["General", "Extension"].includes(item.type)
? DOCUMENT_ICON
: BRACES_ICON;
const link = `/manual/${encodeURIComponent(language)}/${encodeURIComponent(item.id)}.php`;
let link = `/manual/${encodeURIComponent(language)}/${encodeURIComponent(item.id)}.php`;
if (language === 'local') {
link = encodeURIComponent(item.id) + '.html';
}

const description =
item.type !== "General"
Expand Down Expand Up @@ -459,7 +415,7 @@ const initSearchUI = ({ searchCallback, language, limit = 30 }) => {
if (selectedIndex !== -1) {
event.preventDefault();
resultsElements[selectedIndex].click();
} else {
} else if (language !== 'local') {
window.location.href = `/search.php?lang=${language}&q=${encodeURIComponent(inputElement.value)}`;
}
break;
Expand Down