From e80874f5c742381c834772cc13ca8c2fa115a712 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer
Date: Sun, 11 Apr 2010 18:21:05 +0200
Subject: [PATCH] Docs: Add AJAX search field to the docs
-) Add a search field to the toc pane in our docs. If the user types three
letters or more, an AJAX request is sent to the server to perform a
search. The AJAX part is implemented in JS, the search part itself is
implemented in PHP on the server side, which wades through a pre-generated
index file (pure text).
-) The texi2html init file now has the ability to create our own search
index file, currently in pure text (the search script will go through it
line for line.
-) The search box is shown via JavaScript only if the files are viewed over
http. If the user has JavaScript disabled or if the files are viewed as
static pages (and not over http), then no search box appears, since it
would not work anyway.
-) Also, don't show search box when AJAX initialization failed.
-) Print nice message if search index can't be opened, don't fail with error
-) Install and use language-dependent index files for all known languages
-) If we don't have a texinfo index, don't load the JS and don't print
out the search box. (Workaround: css_lines is called before
init_out, so we need to do the check already in css_lines!)
If the index would be empty, don't create an empty .idx file
-) If AJAX works and a user presses enter, the same AJAX query is triggered
as when entering some letters, and the results appear on the same page.
However, if for some reason AJAX does not work (in particular, if the
onSubmit action handler isn't called), then a search page is loaded
with the same results as the AJAX query...
---
Documentation/GNUmakefile | 18 ++++-
Documentation/css/lilypond-mccarty.css | 25 ++++++
Documentation/lily_index_search.php | 62 +++++++++++++++
Documentation/lily_search.js | 101 +++++++++++++++++++++++++
Documentation/lilypond-texi2html.init | 79 +++++++++++++++++++
make/doc-i18n-root-rules.make | 1 +
scripts/build/www_post.py | 2 +-
7 files changed, 286 insertions(+), 2 deletions(-)
create mode 100644 Documentation/lily_index_search.php
create mode 100644 Documentation/lily_search.js
diff --git a/Documentation/GNUmakefile b/Documentation/GNUmakefile
index d1ffcf4e82..5a8cec7c06 100644
--- a/Documentation/GNUmakefile
+++ b/Documentation/GNUmakefile
@@ -91,9 +91,19 @@ include $(depth)/make/stepmake.make
OUT_TXT_FILES = $(addprefix $(outdir)/, $(addsuffix .txt, $(README_TOP_FILES)))
+
### Web site idiosyncrases
$(XREF_MAPS_DIR)/web.xref-map: XREF_MAP_FLAGS += --split=node
+### AJAX scripts
+JS_FILES = $(call src-wildcard,*.js)
+PHP_FILES = $(call src-wildcard,*.php)
+EXTRA_DIST_FILES += $(JS_FILES)
+
+OUT_JS_FILES = $(JS_FILES:%.js=$(outdir)/%.js)
+OUT_PHP_FILES = $(PHP_FILES:%.php=$(outdir)/%.php)
+
+
### bad hack for badly-integrated roadmap.
$(outdir)/ROADMAP:
@@ -168,7 +178,7 @@ local-clean:
ifeq ($(out),www)
local-WWW-1: $(OUT_TEXINFO_MANUALS) $(PDF_FILES) info
-local-WWW-2: txt-to-html $(OUT_HTML_FILES) $(DEEP_HTML_FILES) $(source-links) $(OMF_FILES)
+local-WWW-2: txt-to-html $(OUT_HTML_FILES) $(DEEP_HTML_FILES) $(OUT_JS_FILES) $(OUT_PHP_FILES) $(source-links) $(OMF_FILES)
endif
@@ -233,6 +243,12 @@ $(outdir)/snippets.texi: $(GENERATED_ITELY_FILES) $(SNIPPET_LY_FILES)
$(outdir)/%.bib: %.bib
ln -f $< $@
+$(outdir)/%.js: %.js
+ ln -f $< $@
+
+$(outdir)/%.php: %.php
+ ln -f $< $@
+
## notation.texi deps
$(top-build-dir)/mf/$(outconfbase)/feta16list.ly:
$(MAKE) -C $(top-src-dir)/mf
diff --git a/Documentation/css/lilypond-mccarty.css b/Documentation/css/lilypond-mccarty.css
index 4ccf54f206..7bafd33101 100644
--- a/Documentation/css/lilypond-mccarty.css
+++ b/Documentation/css/lilypond-mccarty.css
@@ -414,6 +414,31 @@ table.menu {
.nav_table { display: none; }
}
+/***********************************************************/
+/* FORMATTING of AJAX SEARCH BOX */
+/***********************************************************/
+
+div#search {
+ border: none;
+ border-bottom: 1pt solid #C5972C;
+ background: #E8E3AC;
+ padding-left: 3px;
+ padding-top: 2px;
+ padding-bottom: 1px;
+}
+div#search p, div#search form {
+ padding: 0;
+ margin: 0;
+}
+#search_results {
+ font-size: 0.75em;
+ padding: 0;
+ margin: 0;
+ display: none;
+}
+#search_results table {
+ width: 100%;
+}
/***********************************************************/
/* OTHER */
diff --git a/Documentation/lily_index_search.php b/Documentation/lily_index_search.php
new file mode 100644
index 0000000000..f1b1e17b29
--- /dev/null
+++ b/Documentation/lily_index_search.php
@@ -0,0 +1,62 @@
+"en", "de"=>"de", "nl"=>"nl", "jp"=>"jp", "hu"=>"hu", "fr"=>"fr", ""=>"en");
+ $manuals = array ("essay"=>"essay", "extending"=>"extending", "learning"=>"learning", "notation"=>"notation", "usage"=>"usage");
+
+ $lang = $languages[$_REQUEST['lang']];
+ $man = $manuals[$_REQUEST['manual']];
+ if (!$man) {
+ echo "Invalid manual " . $_REQUEST['lang'] . "
";
+ exit ();
+ }
+ $bigpage = ($_REQUEST['bigpage'] == "1");
+ $search_string = $_REQUEST['q'];
+ // If enter was pressed, browsers will use the returned HTML for a complete page!
+ $form_submitted = $_REQUEST['form_submitted'];
+
+
+ $relpath = "";
+ if ($form_submitted) {
+ if (! $bigpage) {
+ $relpath = "$man/";
+ }
+ echo "\n";
+ }
+
+ $filename = "./$man";
+ if ($bigpage) { $filename .= "-big-page"; }
+ $filename .= ".$lang.idx";
+
+ $found = 0;
+ $file = @fopen($filename, "r");
+ if ($file ) {
+ while ( (($line=fgets($file)) !== false) ) {
+ $line = rtrim($line);
+ $entries = split ("\t", $line);
+ if (stripos ($entries[0], $search_string) !== false) {
+ if ($found == 0) {
+ echo "Search results for "".htmlentities($search_string, ENT_QUOTES)."":
\n";
+ echo "
\n";
+ } else if ($found > 50) {
+ echo "Too many hits, displaying only 50 results |
\n";
+ break;
+ }
+ // format the entry and print it out
+ echo "$entries[1] | \n";
+ echo " $entries[3] |
\n";
+ $found++;
+ }
+ }
+ if ($found > 0) {
+ echo "
\n";
+ } else {
+ echo "No results found in the index.\n";
+ }
+ echo "
";
+ fclose($file);
+ } else {
+ echo "Unable to open search index $filename
";
+ }
+ if ($form_submitted) {
+ echo "\n";
+ }
+?>
\ No newline at end of file
diff --git a/Documentation/lily_search.js b/Documentation/lily_search.js
new file mode 100644
index 0000000000..09516b306b
--- /dev/null
+++ b/Documentation/lily_search.js
@@ -0,0 +1,101 @@
+var resObject = null;
+var useAjax = (document.location.protocol.toLowerCase() == 'http:');
+var isLocal = !useAjax;
+
+var previous_search = "";
+
+function erzXMLHttpRequestObject ()
+{
+ var resObject = null;
+ try {
+ resObject = new XMLHttpRequest ();
+ }
+ catch (Error) {
+ try {
+ resObject = new ActiveXObject ("Microsoft.XMLHTTP");
+ }
+ catch (Error) {
+ try {
+ resObject = new ActiveXObject ("MSXML2.XMLHTTP");
+ }
+ catch (Error) {
+ alert ("Unable to create XMLHttpRequest object for the search function!");
+ useAjax = false;
+ }
+ }
+ }
+ return resObject;
+}
+
+function searchResult (language, manual, bigpage)
+{
+ search_string = this.document.search_form.q.value;
+ if (useAjax && previous_search != search_string) {
+ if (useAjax && search_string.length >= 3) {
+ var reldir = "";
+ if (bigpage == 0) {
+ reldir = "../"
+ }
+ resObject.open ('get', reldir + 'lily_index_search.php?lang=' + escape(language) + '&manual=' + escape(manual) + '&bigpage=' + bigpage + '&q=' + escape(search_string), true);
+ resObject.onreadystatechange = handleResponse;
+ resObject.send (null);
+ } else {
+ clearResults ();
+ }
+ previous_search = search_string;
+ }
+}
+
+function result_field ()
+{
+ return document.getElementById ('search_results');
+}
+function assignResults (results)
+{
+ field = result_field ();
+ field.innerHTML = resObject.responseText;
+ field.style.display = 'block';
+}
+
+function handleResponse ()
+{
+ if (resObject.readyState == 4 ) {
+ assignResults (resObject.responseText);
+ }
+}
+
+function clearResults ()
+{
+ field = result_field ();
+ field.innerHTML = 0;
+ field.style.display = 'none';
+}
+
+
+function print_search_field (language, manual, bigpage)
+{
+ if (useAjax) {
+ // If the user presses enter and submits the form, also call the search
+ // script to print out the results in a separate page
+ search_call = "searchResult('" + language + "', '" + manual + "', " + bigpage + ")";
+ var reldir = "";
+ if (bigpage == 0) {
+ reldir = "../"
+ }
+ search_script = reldir + 'lily_index_search.php';
+ document.write("");
+ document.write("
");
+ document.write("
");
+ }
+}
+if (useAjax) {
+ resObject = erzXMLHttpRequestObject ();
+}
\ No newline at end of file
diff --git a/Documentation/lilypond-texi2html.init b/Documentation/lilypond-texi2html.init
index 6fcaafc5f4..2ea8175c47 100644
--- a/Documentation/lilypond-texi2html.init
+++ b/Documentation/lilypond-texi2html.init
@@ -583,9 +583,14 @@ $Texi2HTML::Config::USE_REL_REV = 1;
$Texi2HTML::Config::SPLIT_INDEX = 0;
$Texi2HTML::Config::SEPARATED_FOOTNOTES = 0; # Print footnotes on same page, not separated
+my $bigpage = 0;
+my $have_index_entries = 0;
if ($Texi2HTML::Config::SPLIT eq 'section' or
$Texi2HTML::Config::SPLIT eq 'node') {
$Texi2HTML::Config::element_file_name = \&lilypond_element_file_name;
+ $bigpage = 0;
+} else {
+ $bigpage = 1;
}
$Texi2HTML::Config::anchor = \&lilypond_anchor;
@@ -793,6 +798,17 @@ sub lilypond_css_lines ($$)
# docs still need this? -gp
$Texi2HTML::THISDOC{'CSS_LINES'} .=
"\n";
+
+ # Add the JavaScript file only if we have an index.
+ # Unfortunately, init_out is called after css_lines, so as a workaround
+ # here we have to check again for the existence of an index...
+ my $ix = main::get_index ("cp");
+ $have_index_entries = (scalar $ix > 0);
+ my $reldir = "";
+ $reldir = "../" unless $bigpage;
+ if ($have_index_entries) {
+ $Texi2HTML::THISDOC{'CSS_LINES'} .= "\n";
+ }
}
@@ -1359,6 +1375,14 @@ sub lilypond_print_toc_div ($$)
&ly_get_string ('Back to Documentation Index') .
"\n";
+ # AJAX search box, written in JavaScript
+ if ($have_index_entries) {
+ my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
+ print $fh '\n";
+ }
+
print $fh '