Javascript for info-style navigation - Week 12

by Mathieu Lirzin - 23 Aug 2017 12:37:49 GMT

This is an update on the work I am doing this summer for my Google Summer of Code. Please see the first article of this serie of reports for a general introduction on what this project is about.

Global search

Last week, I have described the first part of the implementation of the global search which is accessible with the s key. To match what is provided by info and info-mode I have added the incremental search features which consists in navigating throught all the results that match the query when typing the s and enter keys again. The search is done in order starting at the current page the user is currently visiting.

In order to provide this feature a first requirement is to remember the previous search that was made, and let the user access it when providing the empty string as input. Like in info the previous search is displayed in parenthese and described as the default search. This is fairly trivial since it only requires storing the previous input and include it in the prompt.

Regarding iterative search, things are more complicated. When two results are included in separate pages there are no issue since we already keep track of the page the search result is in. What is more tricky is to handle multiple results in the same page. We can identify the previous match by the fact that it is a text node whose parent is a span element that has the search-result id. The search throught the manual is done using a depth first traversal of the pages. To identify our next match, we need to walk the document text nodes and if we encouter a previous match we invalidate the possible results that were already scanned. If we find another match after that we first remove the previous span and then create the new one. If no other matches are found, we continue on the next page as usual. The trouble before finding that algorithm was that the highlighting and searching parts were initially separated in different steps which didn't fit that scenario.

With the incremental search added. The only major feature missing is allowing the use of regular expressions. As this has not been a priority in this project, I will leave its implementation as an exercice to the interested reader. ;)

Adapting to makeinfo output

Until now we have been using the XHTML output of Docbook XSL Stylesheets processed with xsltproc. Besides those stylesheets xsltproc takes an XML file generated with makeinfo --docbook as its input. This process was inherited from the initial prototype made by Per Bothner, however one of the goal of this project was to make the Javascript UI work with makeinfo --html output. There was some concerns regarding the fact that the HTML generated by makeinfo was not easy to work with on the Javascript side. The initial idea was to first improve makeinfo HTML backend and then adapt the Javascript code. After looking at the actual HTML used and based my experience with the current implementation, I didn't find any major issue preventing to port the Javascript code the current makeinfo HTML output.

So I took up the task of adapting the code. Since most information in the model of the application is gathered by scanning the DOM, I had to rewrite those scanning functions which was not complicated. What took more time to handle was the fact that there is no dedicated table of content file to be used as an iframe. Instead of a dedicated page, the table of content is included in index.html, so I have replaced the iframe with a simple div that is constructed from the included table of content.

Additionally index.html includes the Top and SEC_Contents anchors. In our model anchors having an URL like file:///home/foo/index.html#bar is referring to the bar.html file which is included as an iframe in index.html. It was not considered that the main page could include its own anchors. So I had to adapt by defining special ids corresponding to anchors in the main with the config.MAIN_ANCHORS constant. Those ids are now treated differently from regular iframe ones.

Customization

I must admit, I have never tried to work on the CSS seriously. I have quite blindy use the rules that was already provided by Docbook and in the Kawa manual, and add my own when needing some specific disposition. I finally took some time to cleanup the mess by removing the unneeded rules and separating the one that are specific to Kawa color style from the rest. The "essential" part has been kept in a info.css file while the kawa specific one has been put in kawa.css. This should allow package maintainers to reuse the info.css more easily.

One other aspect of the customization of the manuals is the possibility to add additional Javascript scripts. Since some the UI elements are added inside "DOMContentLoaded" event handlers, it is important to provide some hooks to allow the package maintainer to modify what is done after that step without having to modify info.js directly or having to deal with asynchronicity issues by adding a separate event handler on the same event. For that it is now possible to override the default config variable before loading the 'info.js' script, like shown here:

<script>
window.INFO_CONFIG = {
    hooks: { 
      on_main_load: function () { console.log ("index hook!"); },
      on_iframe_load: function () { console.log ("iframe hook!"); }
    }
};
</script>
<script src="info.js" type="text/javascript"></script>

Next and final step

This Google Summer of Code is coming to its end. Most of the remaining things that I am going to work on will be oriented towards the final evaluation that I will send on bug-texinfo@gnu.org soon. This will include some polishing and portability checks.

Follow the developpement

I have updated the live demo of the Kawa manual which is available here and add another one for the GNU hello manual which is available here . If you have already accessed those pages, it is possible that you face invalid cache issues. Make sure that your local cache is cleared.

The development of this project is done in public. You can checkout the "gsoc-2017" branch of the Git repository and run the build instructions from the README to see what is the current state of the project.