]> git.donarmstrong.com Git - lilypond.git/commitdiff
Docs: Add AJAX search field to the docs
authorReinhold Kainhofer <reinhold@kainhofer.com>
Sun, 11 Apr 2010 16:21:05 +0000 (18:21 +0200)
committerGraham Percival <graham@percival-music.ca>
Fri, 28 May 2010 10:40:56 +0000 (11:40 +0100)
-) 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
Documentation/css/lilypond-mccarty.css
Documentation/lily_index_search.php [new file with mode: 0644]
Documentation/lily_search.js [new file with mode: 0644]
Documentation/lilypond-texi2html.init
make/doc-i18n-root-rules.make
scripts/build/www_post.py

index d1ffcf4e825bd20ab991cbca5458f3f776e0539e..5a8cec7c06fff0381c319a698bff08e6fa7f4a9b 100644 (file)
@@ -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
index 4ccf54f206aa69a67f5d48ae11c0261a195b2904..7bafd331016e907d119e36f6335cf2f2bd1b7fbf 100644 (file)
@@ -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 (file)
index 0000000..f1b1e17
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+  $languages = array ("en"=>"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 "<p>Invalid manual " . $_REQUEST['lang'] . "</p>";
+    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 "<html><body>\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 "<p><b>Search results for &quot;".htmlentities($search_string, ENT_QUOTES)."&quot;:</b><br>\n";
+          echo "<table id=\"search_result_table\">\n";
+        } else if ($found > 50) {
+          echo "<tr><td colspan=2>Too many hits, displaying only 50 results</td></tr>\n";
+          break;
+        }
+        // format the entry and print it out
+        echo "<tr><td><a href=\"$relpath$entries[2]\">$entries[1]</a></td>\n";
+        echo "    <td><a href=\"$relpath$entries[4]\">$entries[3]</a></td></tr>\n";
+        $found++;
+      }
+    }
+    if ($found > 0) {
+      echo "</table>\n";
+    } else {
+      echo "No results found in the index.\n";
+    }
+    echo "</p>";
+    fclose($file);
+  } else {
+    echo "<p>Unable to open search index $filename</p>";
+  }
+  if ($form_submitted) {
+    echo "</body></html>\n";
+  }
+?>
\ No newline at end of file
diff --git a/Documentation/lily_search.js b/Documentation/lily_search.js
new file mode 100644 (file)
index 0000000..09516b3
--- /dev/null
@@ -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("<div id=\"search\">");
+    document.write("<form name=\"search_form\" action=\"" + search_script + "\" onsubmit=\"" + search_call + "; return false;\">");
+    document.write("<input type=\"hidden\" name=\"lang\" value=\"" + escape(language) + "\" >");
+    document.write("<input type=\"hidden\" name=\"manual\" value=\"" + escape(manual) + "\" >");
+    document.write("<input type=\"hidden\" name=\"bigpage\" value=\"" + bigpage + "\" >");
+    document.write("<input type=\"hidden\" name=\"form_submitted\" value=\"1\" >");
+    document.write("<p class=\"searchbar\">Search: ");
+    document.write("  <input name=\"q\" onkeyup=\"" + search_call + "\" size=25></input></p>");
+    document.write("  <div id=\"search_results\"></div>");
+    document.write("</form>");
+    document.write("</div>");
+  }
+}
+if (useAjax) {
+  resObject = erzXMLHttpRequestObject ();
+}
\ No newline at end of file
index 6fcaafc5f43e8f95f9e06fd6528ee4a8c57efa53..2ea8175c47f182fcdbba072e2b3e010f54aff1f0 100644 (file)
@@ -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'} .=
       "<!--[if lte IE 7]>\n<link href=\"lilypond-ie-fixes.css\" rel=\"stylesheet\" type=\"text/css\">\n<![endif]-->\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'} .= "<script language=\"JavaScript\" src=\"${reldir}lily_search.js\"></script>\n";
+  }
 }
 
 
@@ -1359,6 +1375,14 @@ sub lilypond_print_toc_div ($$)
         &ly_get_string ('Back to Documentation Index') .
         "</a></p>\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 '<script language="JavaScript">print_search_field ("' . 
+             $Texi2HTML::THISDOC{current_lang} . '", "' . 
+             $docu_name . "\", " . $bigpage . ")</script>\n";
+      }
+
       print $fh '<h4 class="toc_header"> ' . &$anchor('',
                                     $Texi2HTML::HREF{'Top'},
                                     $topname,
@@ -1781,6 +1805,61 @@ sub makeinfo_like_paragraph ($$$$$$$$$$$$$)
 }
 
 
+#############################################################################
+###  WRITING OUT THE INDEX FOR THE AJAX SEARCH FIELD
+#############################################################################
+
+$Texi2HTML::Config::IDX_SUMMARY   = 1;
+$Texi2HTML::Config::init_out                  = \&lilypond_init_out;
+$Texi2HTML::Config::finish_out                = \&lilypond_finish_out;
+$Texi2HTML::Config::index_summary_file_entry  = \&lilypond_index_summary_file_entry;
+
+my @index_entries;
+
+sub lilypond_init_out ()
+{
+  t2h_default_init_out ();
+  # Check whether we have an index at all! If not -> don't print out a search box!
+  my $ix = main::get_index ("cp");
+  $have_index_entries = (scalar $ix > 0);
+}
+
+sub lilypond_index_summary_file_entry ($$$$$$$$$)
+{
+  my $index_name = shift;
+  my $entry_text = shift;
+  my $entry_reference = shift;
+  my $formatted_entry = shift;
+  my $texi_entry = shift;
+  my $entry_element_reference = shift;
+  my $entry_element_header = shift;
+  my $is_printed = shift;
+  my $manual_name = shift;
+
+  if ($index_name eq "cp")
+  {
+    # The entries in the index file have the form:
+    # SearchText \t FormattedText \t EntryURL \t SectionText \t SectionURL
+    push @index_entries, "$entry_text\t$formatted_entry\t$entry_reference\t$entry_element_header\t$entry_element_reference\n";
+  }
+}
+
+sub lilypond_finish_out ()
+{
+  if (((scalar @index_entries) > 0) and $have_index_entries) {
+    my $lang = $Texi2HTML::THISDOC{current_lang};
+    my $big = "";
+    $big = "-big-page" if $bigpage;
+    my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
+    my $idx_file = "$docu_dir/$docu_name$big.$lang.idx";
+    open IDXOUTFILE, ">:utf8", $idx_file;
+    print IDXOUTFILE @index_entries;
+    close IDXOUTFILE;
+  }
+}
+
+
+
 #############################################################################
 ###  OTHER SETTINGS
 #############################################################################
index 13d3618fa4c9ee496925556c4dd1079ce2f514a7..2d9dec0304852345fd538ddabd16ebd1bdf2ea24 100644 (file)
@@ -38,6 +38,7 @@ $(outdir)/pictures:
 
 $(TRANSLATION_LILY_IMAGES): $(MASTER_TEXI_FILES)
        find $(outdir) \( -name 'lily-*.png' -o -name 'lily-*.ly' \) | sed 's!$(outdir)/!!g' | xargs $(buildscript-dir)/mass-link hard $(outdir) $(top-build-dir)/Documentation/$(outdir)
+       find $(outdir) \( -name '*.??.idx' \) | sed 's!$(outdir)/!!g' | xargs $(buildscript-dir)/mass-link hard $(outdir) $(top-build-dir)/Documentation/$(outdir)
        touch $@
 
 $(outdir)/lilypond-%.info: $(outdir)/%.texi $(outdir)/$(INFO_IMAGES_DIR).info-images-dir-dep $(outdir)/version.itexi $(outdir)/weblinks.itexi
index a9ab88cd3ecbc9f2af49a86ec971313fa51f7d4e..17af63109edf3d3728ed44c5a49a1e77452cf21f 100644 (file)
@@ -41,7 +41,7 @@ dirs, symlinks, files = mirrortree.walk_tree (
     exclude_dirs = '(^|/)((' + \
         r'po|xref-maps|out|out-test|out-cov|.*?[.]t2d|\w*?-root)|^Documentation/(' + \
         '|'.join ([l.code for l in langdefs.LANGUAGES]) + '))(/|$)',
-    find_files = r'.*?\.(?:midi|html|pdf|png|jpe?g|txt|i?ly|signature|css|zip)$|VERSION',
+    find_files = r'.*?\.(?:midi|html|pdf|png|jpe?g|txt|i?ly|signature|css|zip|js|..\.idx|php)$|VERSION',
     exclude_files = r'lily-[0-9a-f]+.*\.(pdf|txt)')
 
 # actual mirrorring stuff