.. code:: sh
convert favicon.png favicon.ico
+
+magnifier.svg
+=============
+
+Doxygen search icon. Export as "Optimized SVG" and copy into the ``base.html``
+template.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 16.00091 16.000004"
+ enable-background="new 0 0 1000 1000"
+ xml:space="preserve"
+ id="svg10"
+ sodipodi:docname="magnifier.svg"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+ width="16.00091"
+ height="16.000004"><defs
+ id="defs14" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1024"
+ id="namedview12"
+ showgrid="false"
+ inkscape:zoom="32"
+ inkscape:cx="-4.6745911"
+ inkscape:cy="6.2637304"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer2"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" /><metadata
+ id="metadata2"> Svg Vector Icons : http://www.onlinewebfonts.com/icon <rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="bg"><path
+ style="stroke-width:1.00000012"
+ d="M 6 0 C 2.68562 0 0 2.68563 0 6 C 0 9.31439 2.68562 12 6 12 C 7.48575 12 8.8463413 11.459173 9.8945312 10.564453 C 9.8781312 10.902423 10.041871 11.322994 10.394531 11.714844 L 13.617188 15.503906 C 14.169037 16.117806 15.068921 16.169351 15.619141 15.619141 C 16.169361 15.068921 16.117806 14.169047 15.503906 13.617188 L 11.714844 10.392578 C 11.323004 10.039918 10.902423 9.8778881 10.564453 9.8925781 C 11.459173 8.8443881 12 7.4837969 12 5.9980469 C 12 2.6852969 9.31438 1.1842379e-15 6 0 z M 6 1.5625 A 4.4375 4.4375 0 0 1 10.4375 6 A 4.4375 4.4375 0 0 1 6 10.4375 A 4.4375 4.4375 0 0 1 1.5625 6 A 4.4375 4.4375 0 0 1 6 1.5625 z "
+ id="path4" /></g></svg>
\ No newline at end of file
border-left-width: 0.25rem;
border-left-color: #a5c9ea;
}
+a.m-dox-search-icon {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+a.m-dox-search-icon svg {
+ height: 0.9rem;
+ fill: #ffffff;
+}
+body > header > nav #m-navbar-collapse a.m-dox-search-icon svg {
+ vertical-align: -5%;
+}
+a.m-dox-search-icon:focus svg, a.m-dox-search-icon:hover svg, a.m-dox-search-icon:active svg {
+ fill: #a5c9ea;
+}
+.m-dox-search {
+ display: none;
+ z-index: 10;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(34, 39, 46, 0.75);
+}
+.m-dox-search:target {
+ display: block;
+}
+.m-dox-search > a {
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.m-dox-search-header {
+ margin-top: 2.5rem;
+ padding: 0.5rem 1rem;
+ height: 2rem;
+}
+.m-dox-search-header > div:first-child {
+ float: right;
+}
+.m-dox-search-content {
+ background-color: #22272e;
+ border-radius: 0.2rem;
+ padding: 1rem;
+}
+.m-dox-search input {
+ width: 100%;
+ height: 3rem;
+ font-size: 1.2rem;
+ border-width: 0;
+ color: #dcdcdc;
+ background-color: #34424d;
+ border-radius: 0.2rem;
+ margin-bottom: 1rem;
+ padding: 0 1rem;
+}
+.m-dox-search #search-notfound {
+ display: none;
+}
+.m-dox-search ul#search-results {
+ list-style-type: none;
+ padding-left: 0;
+ max-height: calc(100vh - 12.5rem);
+ overflow-y: auto;
+ display: none;
+}
+.m-dox-search ul#search-results li a {
+ display: block;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ text-decoration: none;
+ width: 100%;
+ line-height: 1.5rem;
+ color: #dcdcdc;
+}
+.m-dox-search ul#search-results li a > div {
+ white-space: nowrap;
+ overflow: hidden;
+ direction: rtl;
+}
+.m-dox-search ul#search-results li#search-current a {
+ background-color: #34424d;
+}
+.m-dox-search-typed {
+ color: #5b9dd9;
+}
+.m-dox-search input[type="search"] { -webkit-appearance: textfield; }
+.m-dox-search input[type="search"]::-webkit-search-decoration,
+.m-dox-search input[type="search"]::-webkit-search-cancel-button,
+.m-dox-search input[type="search"]::-webkit-search-results-button,
+.m-dox-search input[type="search"]::-webkit-search-results-decoration {
+ display: none;
+}
border-left-width: 0.25rem;
border-left-color: #a5c9ea;
}
+a.m-dox-search-icon {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+a.m-dox-search-icon svg {
+ height: 0.9rem;
+ fill: #ffffff;
+}
+body > header > nav #m-navbar-collapse a.m-dox-search-icon svg {
+ vertical-align: -5%;
+}
+a.m-dox-search-icon:focus svg, a.m-dox-search-icon:hover svg, a.m-dox-search-icon:active svg {
+ fill: #a5c9ea;
+}
+.m-dox-search {
+ display: none;
+ z-index: 10;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(34, 39, 46, 0.75);
+}
+.m-dox-search:target {
+ display: block;
+}
+.m-dox-search > a {
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.m-dox-search-header {
+ margin-top: 2.5rem;
+ padding: 0.5rem 1rem;
+ height: 2rem;
+}
+.m-dox-search-header > div:first-child {
+ float: right;
+}
+.m-dox-search-content {
+ background-color: #22272e;
+ border-radius: 0.2rem;
+ padding: 1rem;
+}
+.m-dox-search input {
+ width: 100%;
+ height: 3rem;
+ font-size: 1.2rem;
+ border-width: 0;
+ color: #dcdcdc;
+ background-color: #34424d;
+ border-radius: 0.2rem;
+ margin-bottom: 1rem;
+ padding: 0 1rem;
+}
+.m-dox-search #search-notfound {
+ display: none;
+}
+.m-dox-search ul#search-results {
+ list-style-type: none;
+ padding-left: 0;
+ max-height: calc(100vh - 12.5rem);
+ overflow-y: auto;
+ display: none;
+}
+.m-dox-search ul#search-results li a {
+ display: block;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ text-decoration: none;
+ width: 100%;
+ line-height: 1.5rem;
+ color: #dcdcdc;
+}
+.m-dox-search ul#search-results li a > div {
+ white-space: nowrap;
+ overflow: hidden;
+ direction: rtl;
+}
+.m-dox-search ul#search-results li#search-current a {
+ background-color: #34424d;
+}
+.m-dox-search-typed {
+ color: #5b9dd9;
+}
+.m-dox-search input[type="search"] { -webkit-appearance: textfield; }
+.m-dox-search input[type="search"]::-webkit-search-decoration,
+.m-dox-search input[type="search"]::-webkit-search-cancel-button,
+.m-dox-search input[type="search"]::-webkit-search-results-button,
+.m-dox-search input[type="search"]::-webkit-search-results-decoration {
+ display: none;
+}
border-left-color: var(--article-heading-color);
}
+a.m-dox-search-icon {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+a.m-dox-search-icon svg {
+ height: 0.9rem;
+ fill: var(--header-link-color);
+}
+body > header > nav #m-navbar-collapse a.m-dox-search-icon svg {
+ vertical-align: -5%;
+}
+a.m-dox-search-icon:focus svg, a.m-dox-search-icon:hover svg, a.m-dox-search-icon:active svg {
+ fill: var(--header-link-active-color);
+}
+.m-dox-search {
+ display: none;
+ z-index: 10;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: var(--header-background-color-landing);
+}
+.m-dox-search:target {
+ display: block;
+}
+.m-dox-search > a {
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.m-dox-search-header {
+ margin-top: 2.5rem;
+ padding: 0.5rem 1rem;
+ height: 2rem;
+}
+.m-dox-search-header > div:first-child {
+ float: right;
+}
+.m-dox-search-content {
+ background-color: var(--header-background-color);
+ border-radius: var(--border-radius);
+ padding: 1rem;
+}
+.m-dox-search input {
+ width: 100%;
+ height: 3rem;
+ font-size: 1.2rem;
+ border-width: 0;
+ color: var(--color);
+ background-color: var(--default-filled-background-color);
+ border-radius: var(--border-radius);
+ margin-bottom: 1rem;
+ padding: 0 1rem; /* putting it on all sides cuts text off in FF */
+}
+.m-dox-search #search-notfound {
+ display: none;
+}
+.m-dox-search ul#search-results {
+ list-style-type: none;
+ padding-left: 0;
+ /* Size breakdown:
+ 2.5 margin of .m-dox-search-header from top
+ 2 height of .m-dox-search-header
+ 1 padding around .m-dox-search-header (twice 0.5rem)
+ 1 padding of .m-dox-search-content from top
+ 3 height of the input field
+ 1 margin under input
+ 1 padding of .m-dox-search-content from bottom
+ 1 margin under .m-dox-search-content
+ ------
+ 12.5 total */
+ max-height: calc(100vh - 12.5rem);
+ overflow-y: auto;
+ display: none;
+}
+.m-dox-search ul#search-results li a {
+ display: block;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ text-decoration: none;
+ width: 100%;
+ line-height: 1.5rem;
+ color: var(--color);
+}
+.m-dox-search ul#search-results li a > div {
+ white-space: nowrap;
+ overflow: hidden;
+ /* This is here in order to cut the text off at the left side. Besides this
+ there's special patching needed for punctuation characters, see search.js
+ for details. */
+ direction: rtl;
+}
+.m-dox-search ul#search-results li#search-current a {
+ background-color: var(--default-filled-background-color);
+}
+.m-dox-search-typed {
+ color: var(--link-color);
+}
+
+/* WELL THANK YOU WEBKIT! FOR SURE I WANTED ALL THAT SHIT HERE! */
+.m-dox-search input[type="search"] { -webkit-appearance: textfield; }
+.m-dox-search input[type="search"]::-webkit-search-decoration,
+.m-dox-search input[type="search"]::-webkit-search-cancel-button,
+.m-dox-search input[type="search"]::-webkit-search-results-button,
+.m-dox-search input[type="search"]::-webkit-search-results-decoration {
+ display: none;
+}
+
/* kate: indent-width 2; */
border-left-width: 0.25rem;
border-left-color: #cb4b16;
}
+a.m-dox-search-icon {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+a.m-dox-search-icon svg {
+ height: 0.9rem;
+ fill: #000000;
+}
+body > header > nav #m-navbar-collapse a.m-dox-search-icon svg {
+ vertical-align: -5%;
+}
+a.m-dox-search-icon:focus svg, a.m-dox-search-icon:hover svg, a.m-dox-search-icon:active svg {
+ fill: #cb4b16;
+}
+.m-dox-search {
+ display: none;
+ z-index: 10;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(255, 255, 255, 0.75);
+}
+.m-dox-search:target {
+ display: block;
+}
+.m-dox-search > a {
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.m-dox-search-header {
+ margin-top: 2.5rem;
+ padding: 0.5rem 1rem;
+ height: 2rem;
+}
+.m-dox-search-header > div:first-child {
+ float: right;
+}
+.m-dox-search-content {
+ background-color: #ffffff;
+ border-radius: 0.2rem;
+ padding: 1rem;
+}
+.m-dox-search input {
+ width: 100%;
+ height: 3rem;
+ font-size: 1.2rem;
+ border-width: 0;
+ color: #000000;
+ background-color: #fbf0ec;
+ border-radius: 0.2rem;
+ margin-bottom: 1rem;
+ padding: 0 1rem;
+}
+.m-dox-search #search-notfound {
+ display: none;
+}
+.m-dox-search ul#search-results {
+ list-style-type: none;
+ padding-left: 0;
+ max-height: calc(100vh - 12.5rem);
+ overflow-y: auto;
+ display: none;
+}
+.m-dox-search ul#search-results li a {
+ display: block;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ text-decoration: none;
+ width: 100%;
+ line-height: 1.5rem;
+ color: #000000;
+}
+.m-dox-search ul#search-results li a > div {
+ white-space: nowrap;
+ overflow: hidden;
+ direction: rtl;
+}
+.m-dox-search ul#search-results li#search-current a {
+ background-color: #fbf0ec;
+}
+.m-dox-search-typed {
+ color: #ea7944;
+}
+.m-dox-search input[type="search"] { -webkit-appearance: textfield; }
+.m-dox-search input[type="search"]::-webkit-search-decoration,
+.m-dox-search input[type="search"]::-webkit-search-cancel-button,
+.m-dox-search input[type="search"]::-webkit-search-results-button,
+.m-dox-search input[type="search"]::-webkit-search-results-decoration {
+ display: none;
+}
border-left-width: 0.25rem;
border-left-color: #cb4b16;
}
+a.m-dox-search-icon {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+a.m-dox-search-icon svg {
+ height: 0.9rem;
+ fill: #000000;
+}
+body > header > nav #m-navbar-collapse a.m-dox-search-icon svg {
+ vertical-align: -5%;
+}
+a.m-dox-search-icon:focus svg, a.m-dox-search-icon:hover svg, a.m-dox-search-icon:active svg {
+ fill: #cb4b16;
+}
+.m-dox-search {
+ display: none;
+ z-index: 10;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: rgba(255, 255, 255, 0.75);
+}
+.m-dox-search:target {
+ display: block;
+}
+.m-dox-search > a {
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.m-dox-search-header {
+ margin-top: 2.5rem;
+ padding: 0.5rem 1rem;
+ height: 2rem;
+}
+.m-dox-search-header > div:first-child {
+ float: right;
+}
+.m-dox-search-content {
+ background-color: #ffffff;
+ border-radius: 0.2rem;
+ padding: 1rem;
+}
+.m-dox-search input {
+ width: 100%;
+ height: 3rem;
+ font-size: 1.2rem;
+ border-width: 0;
+ color: #000000;
+ background-color: #fbf0ec;
+ border-radius: 0.2rem;
+ margin-bottom: 1rem;
+ padding: 0 1rem;
+}
+.m-dox-search #search-notfound {
+ display: none;
+}
+.m-dox-search ul#search-results {
+ list-style-type: none;
+ padding-left: 0;
+ max-height: calc(100vh - 12.5rem);
+ overflow-y: auto;
+ display: none;
+}
+.m-dox-search ul#search-results li a {
+ display: block;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ text-decoration: none;
+ width: 100%;
+ line-height: 1.5rem;
+ color: #000000;
+}
+.m-dox-search ul#search-results li a > div {
+ white-space: nowrap;
+ overflow: hidden;
+ direction: rtl;
+}
+.m-dox-search ul#search-results li#search-current a {
+ background-color: #fbf0ec;
+}
+.m-dox-search-typed {
+ color: #ea7944;
+}
+.m-dox-search input[type="search"] { -webkit-appearance: textfield; }
+.m-dox-search input[type="search"]::-webkit-search-decoration,
+.m-dox-search input[type="search"]::-webkit-search-cancel-button,
+.m-dox-search input[type="search"]::-webkit-search-results-button,
+.m-dox-search input[type="search"]::-webkit-search-results-decoration {
+ display: none;
+}
:language: ini
.. role:: jinja(code)
:language: jinja
+.. role:: js(code)
+ :language: js
.. role:: py(code)
:language: py
.. role:: sh(code)
`Not yet implemented features`_
-------------------------------
-- Code search. I want to provide something that's actually usable to replace
- the terribly slow stock client-side search, but I'm not there yet.
- Clickable symbols in code snippets. Doxygen has quite a lot of false
positives while a lot of symbols stay unmatched. I need to find a way
around that.
:ini:`M_EXPAND_INNER_TYPES` Whether to expand inner types (e.g. a class
inside a class) in the symbol tree. If not
set, ``NO`` is used.
+:ini:`M_SEARCH_DISABLED` Disable search functionality. If this
+ option is set, no search data is compiled
+ and the rendered HTML does not contain any
+ search-related UI or support. If not set,
+ ``NO`` is used.
+:ini:`M_SEARCH_DOWNLOAD_BINARY` Download search data as a binary to save
+ bandwidth and initial processing time. If
+ not set, ``NO`` is used. See `Search`_ for
+ more information.
+:ini:`M_SEARCH_HELP` HTML code to display as help text on empty
+ search popup. If not set, a default message
+ is used. Has effect only if
+ :ini:`M_SEARCH_DISABLED` is not ``YES``.
+:ini:`M_SEARCH_EXTERNAL_URL` URL for external search. The ``{query}``
+ placeholder is replaced with urlencoded
+ search string. If not set, no external
+ search is offered. See `Search`_ for more
+ information. Has effect only if
+ :ini:`M_SEARCH_DISABLED` is not ``YES``.
=================================== =======================================
Note that namespace, directory and page lists are always fully expanded as
top-level *Namespaces* item and links to two subdirectories as sub-items of the
*Files* item.
+`Search`_
+---------
+
+Symbol search is implemented using JavaScript Typed Arrays and does not need
+any server-side functionality to perform well --- the client automatically
+downloads a tightly packed binary containing search data and performs search
+directly on it.
+
+However, due to `restrictions of Chromium-based browsers <https://bugs.chromium.org/p/chromium/issues/detail?id=40787&q=ajax%20local&colspec=ID%20Stars%20Pri%20Area%20Feature%20Type%20Status%20Summary%20Modified%20Owner%20Mstone%20OS>`_,
+it's not possible to download data using :js:`XMLHttpRequest` when served from
+local file-system. Because of that, the search defaults to producing a
+Base85-encoded representation of the search binary and loading that
+asynchronously as a plain JavaScript file. This results in the search data
+being 25% larger, but since this is for serving from a local filesystem, it's
+not considered a problem. If your docs are accessed through a server (or you
+don't need Chrome support), enable the :ini:`M_SEARCH_DOWNLOAD_BINARY` option.
+
+If :ini:`M_SEARCH_EXTERNAL_URL` is specified, full-text search using an
+external search engine is offered if nothing is found for given string or if
+the user has JavaScript disabled. It's recommended to restrict the search to
+a particular domain or add additional keywords to the search query to filter
+out irrelevant results. Example, using Google search engine and restricting
+the search to a subdomain:
+
+.. code:: ini
+
+ M_SEARCH_EXTERNAL_URL = "https://google.com/search?q=site:doc.magnum.graphics+{query}"
+
`Command-line options`_
=======================
template file from above
:py:`compound.id` Unique compound identifier, usually
corresponding to output file name
+:py:`compound.url` Compound URL (or where this file will
+ be saved)
:py:`compound.name` Compound name
:py:`compound.templates` Template specification. Set only for
classes. See `Template properties`_ for
structs and unions; on templated
classes contains also the list of
template parameter names.
-:py:`compound.save_as` Filename including extension where the
- result will be saved
======================================= =======================================
`Navigation properties`_
test/*/html/
test/*/xml/
+test/js-test-data/
+coverage/
import xml.etree.ElementTree as ET
import argparse
+import base64
import sys
import re
import html
def __init__(self):
self.basedir = ''
self.compounds: Dict[str, Any] = {}
+ self.search: List[Any] = []
self.examples: List[Any] = []
self.doxyfile: Dict[str, str] = {}
self.images: List[str] = []
self.current = ''
+ self.current_prefix = []
+ self.current_url = ''
def slugify(text: str) -> str:
# Maybe some Unicode normalization would be nice here?
if ''.join(enumvalue.find('briefdescription').itertext()).strip():
logging.warning("{}: ignoring brief description of enum value {}::{}".format(state.current, enum.name, value.name))
value.description = parse_desc(state, enumvalue.find('detaileddescription'))
- if value.description: enum.has_value_details = True
+ if value.description:
+ enum.has_value_details = True
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ state.search += [(state.current_url + '#' + value.id, state.current_prefix + [enum.name], value.name)]
enum.values += [value]
enum.has_details = enum.description or enum.has_value_details
- return enum if enum.brief or enum.has_details or enum.has_value_details else None
+ if enum.brief or enum.has_details or enum.has_value_details:
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ state.search += [(state.current_url + '#' + enum.id, state.current_prefix, enum.name)]
+ return enum
+ return None
def parse_template_params(state: State, element: ET.Element, description):
if element is None: return False, None
typedef.has_template_details, typedef.templates = parse_template_params(state, element.find('templateparamlist'), templates)
typedef.has_details = typedef.description or typedef.has_template_details
- return typedef if typedef.brief or typedef.has_details else None
+ if typedef.brief or typedef.has_details:
+ state.search += [(state.current_url + '#' + typedef.id, state.current_prefix, typedef.name)]
+ return typedef
+ return None
def parse_func(state: State, element: ET.Element):
assert element.tag == 'memberdef' and element.attrib['kind'] == 'function'
if params: logging.warning("{}: function parameter description doesn't match parameter names: {}".format(state.current, repr(params)))
func.has_details = func.description or func.has_template_details or func.has_param_details or func.return_value
- return func if func.brief or func.has_details else None
+ if func.brief or func.has_details:
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ state.search += [(state.current_url + '#' + func.id, state.current_prefix, func.name + '()')]
+ return func
+ return None
def parse_var(state: State, element: ET.Element):
assert element.tag == 'memberdef' and element.attrib['kind'] == 'variable'
var.description = parse_var_desc(state, element)
var.has_details = not not var.description
- return var if var.brief or var.has_details else None
+ if var.brief or var.has_details:
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ state.search += [(state.current_url + '#' + var.id, state.current_prefix, var.name)]
+ return var
+ return None
def parse_define(state: State, element: ET.Element):
assert element.tag == 'memberdef' and element.attrib['kind'] == 'define'
if params: logging.warning("{}: define parameter description doesn't match parameter names: {}".format(state.current, repr(params)))
define.has_details = define.description or define.return_value
- return define if define.brief or define.has_details else None
+ if define.brief or define.has_details:
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ state.search += [(state.current_url + '#' + define.id, [], define.name + ('' if define.params is None else '()'))]
+ return define
+ return None
def extract_metadata(state: State, xml):
logging.debug("Extracting metadata from {}".format(os.path.basename(xml)))
if state.doxyfile['M_FAVICON']:
state.doxyfile['M_FAVICON'] = (state.doxyfile['M_FAVICON'], mimetypes.guess_type(state.doxyfile['M_FAVICON'])[0])
+def _build_search_data(state: State, prefix, id: str, trie: Trie, map: ResultMap):
+ compound = state.compounds[id]
+ if not compound.brief and not compound.has_details: return 0
+
+ # Add current item name to prefix list
+ prefixed_name = prefix + [compound.leaf_name]
+
+ # Calculate fully-qualified name
+ if compound.kind in ['namespace', 'struct', 'class', 'union']:
+ joiner = '::'
+ elif compound.kind in ['file', 'dir']:
+ joiner = '/'
+ else:
+ joiner = ''
+
+ # If just a leaf name, add it once
+ if not joiner:
+ # TODO: escape elsewhere so i don't have to unescape here
+ name = html.unescape(compound.leaf_name)
+ trie.insert(name.lower(), map.add(name, compound.url))
+
+ # Otherwise add it multiple times with all possible prefixes
+ else:
+ # TODO: escape elsewhere so i don't have to unescape here
+ index = map.add(html.unescape(joiner.join(prefixed_name)), compound.url)
+ for i in range(len(prefixed_name)):
+ trie.insert(html.unescape(joiner.join(prefixed_name[i:])).lower(), index)
+
+ for i in compound.children:
+ if i in state.compounds:
+ _build_search_data(state, prefixed_name, i, trie, map)
+
+def build_search_data(state: State) -> bytearray:
+ trie = Trie()
+ map = ResultMap()
+
+ for id, compound in state.compounds.items():
+ if compound.parent: continue # start from the root
+ _build_search_data(state, [], id, trie, map)
+
+ for url, prefix, name in state.search:
+ # Add current item name to prefix list
+ prefixed_name = prefix + [name]
+
+ # TODO: escape elsewhere so i don't have to unescape here
+ index = map.add(html.unescape('::'.join(prefixed_name)), url)
+ for i in range(len(prefixed_name)):
+ trie.insert(html.unescape('::'.join(prefixed_name[i:])).lower(), index)
+
+ return serialize_search_data(trie, map)
+
+def base85encode_search_data(data: bytearray) -> bytearray:
+ return (b"/* Generated by http://mcss.mosra.cz/doxygen/. Do not edit. */\n" +
+ b"Search.load('" + base64.b85encode(data, True) + b"');\n")
+
def parse_xml(state: State, xml: str):
# Reset counter for unique math formulas
m.math.counter = 0
# Compound name is page filename, so we have to use title there. The same
# is for groups.
compound.name = compounddef.find('title').text if compound.kind in ['page', 'group'] else compounddef.find('compoundname').text
+ # Compound URL is ID, except for index page
+ compound.url = (compounddef.find('compoundname').text if compound.kind == 'page' else compound.id) + '.html'
compound.has_template_details = False
compound.templates = None
compound.brief = parse_desc(state, compounddef.find('briefdescription'))
for i in reversed(path_reverse):
compound.breadcrumb += [(state.compounds[i].leaf_name, state.compounds[i].url)]
+ # Save current prefix for search
+ state.current_prefix = [name for name, _ in compound.breadcrumb]
+ else:
+ state.current_prefix = []
+
+ # Save current compound URL for search data building
+ state.current_url = compound.url
+
if compound.kind == 'page':
# Drop TOC for pages, if not requested
if compounddef.find('tableofcontents') is None:
parsed = Empty()
parsed.version = root.attrib['version']
-
- # Decide about save as filename. Pages mess this up, because index page has
- # "indexpage" as a name so we have to use the compound name instead
- parsed.save_as = (compounddef.find('compoundname').text if compound.kind == 'page' else compound.id) + '.html'
-
parsed.compound = compound
return parsed
'M_FAVICON': [],
'M_LINKS_NAVBAR1': ['pages', 'namespaces'],
'M_LINKS_NAVBAR2': ['annotated', 'files'],
- 'M_PAGE_FINE_PRINT': ['[default]']
+ 'M_PAGE_FINE_PRINT': ['[default]'],
+ 'M_SEARCH_DISABLED': ['NO'],
+ 'M_SEARCH_DOWNLOAD_BINARY': ['NO'],
+ 'M_SEARCH_HELP': ['Search for symbols, headers, pages or example source files. You can omit any prefix from the symbol or file path.'],
+ 'M_SEARCH_EXTERNAL_URL': ['']
}
def parse_value(var):
'M_PAGE_HEADER',
'M_PAGE_FINE_PRINT',
'M_THEME_COLOR',
- 'M_FAVICON']:
+ 'M_FAVICON',
+ 'M_SEARCH_HELP',
+ 'M_SEARCH_EXTERNAL_URL']:
if i in config: state.doxyfile[i] = ' '.join(config[i])
# Int values that we want
if i in config: state.doxyfile[i] = int(' '.join(config[i]))
# Boolean values that we want
- for i in ['M_EXPAND_INNER_TYPES']:
+ for i in ['M_EXPAND_INNER_TYPES',
+ 'M_SEARCH_DISABLED',
+ 'M_SEARCH_DOWNLOAD_BINARY']:
if i in config: state.doxyfile[i] = ' '.join(config[i]) == 'YES'
# List values that we want. Drop empty lines.
template = env.get_template('{}.html'.format(parsed.compound.kind))
rendered = template.render(compound=parsed.compound,
DOXYGEN_VERSION=parsed.version,
- FILENAME=parsed.save_as,
+ FILENAME=parsed.compound.url,
**state.doxyfile)
- output = os.path.join(html_output, parsed.save_as)
+ output = os.path.join(html_output, parsed.compound.url)
with open(output, 'w') as f:
f.write(rendered)
with open(output, 'w') as f:
f.write(rendered)
+ if not state.doxyfile['M_SEARCH_DISABLED']:
+ data = build_search_data(state)
+
+ if state.doxyfile['M_SEARCH_DOWNLOAD_BINARY']:
+ with open(os.path.join(html_output, "searchdata.bin"), 'wb') as f:
+ f.write(data)
+ else:
+ with open(os.path.join(html_output, "searchdata.js"), 'wb') as f:
+ f.write(base85encode_search_data(data))
+
# Copy all referenced files
- for i in state.images + state.doxyfile['HTML_EXTRA_STYLESHEET'] + state.doxyfile['HTML_EXTRA_FILES']:
+ for i in state.images + state.doxyfile['HTML_EXTRA_STYLESHEET'] + state.doxyfile['HTML_EXTRA_FILES'] + ([] if state.doxyfile['M_SEARCH_DISABLED'] else ['search.js']):
# Skip absolute URLs
if urllib.parse.urlparse(i).netloc: continue
--- /dev/null
+/*
+ This file is part of m.css.
+
+ Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+"use strict"; /* it summons the Cthulhu in a proper way, they say */
+
+var Search = {
+ trie: null,
+ map: null,
+ symbolCount: 0,
+ maxResults: 0,
+
+ /* Always contains at least the root node offset and then one node offset
+ per entered character */
+ searchString: '',
+ searchStack: [],
+
+ init: function(buffer, maxResults) {
+ let view = new DataView(buffer);
+
+ /* The file is too short to contain at least the headers */
+ if(view.byteLength < 18) {
+ console.error("Search data too short");
+ return false;
+ }
+
+ if(view.getUint8(0) != 'M'.charCodeAt(0) ||
+ view.getUint8(1) != 'C'.charCodeAt(0) ||
+ view.getUint8(2) != 'S'.charCodeAt(0)) {
+ console.error("Invalid search data signature");
+ return false;
+ }
+
+ if(view.getUint8(3) != 0) {
+ console.error("Invalid search data version");
+ return false;
+ }
+
+ /* Separate the data into the trie and the result map */
+ let mapOffset = view.getUint32(4, true);
+ this.trie = new DataView(buffer, 8, mapOffset - 8);
+ this.map = new DataView(buffer, mapOffset);
+
+ /* Set initial properties */
+ this.symbolCount = (this.map.getUint32(0, true) & 0x00ffffff)/4 - 1;
+ this.maxResults = maxResults ? maxResults : 100;
+ this.searchString = '';
+ this.searchStack = [this.trie.getUint32(0, true)];
+
+ /* istanbul ignore if */
+ if(typeof document !== 'undefined') {
+ document.getElementById('search-symbolcount').innerHTML =
+ this.symbolCount + ' symbols (' +
+ Math.round(buffer.byteLength/102.4)/10 + "kB)";
+ document.getElementById('search-input').disabled = false;
+ document.getElementById('search-input').placeholder = "Type something here …";
+ document.getElementById('search-input').focus();
+
+ /* Search for the input value (there might be something already,
+ for example when going back in the browser) */
+ let value = document.getElementById('search-input').value;
+ if(value.length) Search.renderResults(value, Search.search(value));
+ }
+
+ return true;
+ },
+
+ download: /* istanbul ignore next */ function(url) {
+ var req = window.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest();
+ if(!req) return;
+
+ req.open("GET", url, true);
+ req.responseType = 'arraybuffer';
+ req.onreadystatechange = function() {
+ if(req.readyState != 4) return;
+
+ Search.init(req.response);
+ }
+ req.send();
+ },
+
+ base85decode: function(base85string) {
+ function charValue(char) {
+ if(char >= 48 && char < 58) /* 0-9 -> 0-9 */
+ return char - 48 + 0;
+ if(char >= 65 && char < 91) /* A-Z -> 10-35 */
+ return char - 65 + 10;
+ if(char >= 97 && char < 123) /* a-z -> 36-61 */
+ return char - 97 + 36;
+ if(char == 33) /* ! -> 62 */
+ return 62;
+ /* skipping 34 (') */
+ if(char >= 35 && char < 39) /* #-& -> 63-66 */
+ return char - 35 + 63;
+ /* skipping 39 (") */
+ if(char >= 40 && char < 44) /* (-+ -> 67-70 */
+ return char - 40 + 67;
+ /* skipping 44 (,) */
+ if(char == 45) /* - -> 71 */
+ return 71;
+ if(char >= 59 && char < 65) /* ;-@ -> 72-77 */
+ return char - 59 + 72;
+ if(char >= 94 && char < 97) /* ^-` -> 78-80 */
+ return char - 94 + 78;
+ if(char >= 123 && char < 127) /* {-~ -> 81-84 */
+ return char - 123 + 81;
+
+ return 0; /* Interpret padding values as zeros */
+ }
+
+ /* Pad the string for easier decode later. We don't read past the file
+ end, so it doesn't matter what garbage is there. */
+ if(base85string.length % 5) {
+ console.log("Expected properly padded base85 data");
+ return;
+ }
+
+ let buffer = new ArrayBuffer(base85string.length*4/5);
+ let data8 = new DataView(buffer);
+ for(let i = 0; i < base85string.length; i += 5) {
+ let char1 = charValue(base85string.charCodeAt(i + 0));
+ let char2 = charValue(base85string.charCodeAt(i + 1));
+ let char3 = charValue(base85string.charCodeAt(i + 2));
+ let char4 = charValue(base85string.charCodeAt(i + 3));
+ let char5 = charValue(base85string.charCodeAt(i + 4));
+
+ data8.setUint32(i*4/5, char5 +
+ char4*85 +
+ char3*85*85 +
+ char2*85*85*85 +
+ char1*85*85*85*85, false); /* BE, yes */
+ }
+
+ return buffer;
+ },
+
+ load: function(base85string) {
+ return this.init(this.base85decode(base85string));
+ },
+
+ search: function(searchString) {
+ /* Normalize the search string first */
+ searchString = searchString.toLowerCase().trim();
+
+ /* TODO: maybe i could make use of InputEvent.data and others here */
+
+ /* Find longest common prefix of previous and current value so we don't
+ need to needlessly search again */
+ let max = Math.min(searchString.length, this.searchString.length);
+ let commonPrefix = 0;
+ for(; commonPrefix != max; ++commonPrefix)
+ if(searchString[commonPrefix] != this.searchString[commonPrefix]) break;
+
+ /* Drop items off the stack if it has has more than is needed for the
+ common prefix (it needs to have at least one item, though) */
+ if(commonPrefix + 1 < this.searchStack.length)
+ this.searchStack.splice(commonPrefix + 1, this.searchStack.length - commonPrefix - 1);
+
+ /* Add new characters from the search string */
+ let foundPrefix = commonPrefix;
+ for(; foundPrefix != searchString.length; ++foundPrefix) {
+ /* Calculate offset and count of children */
+ let offset = this.searchStack[this.searchStack.length - 1];
+ let nodeSize = this.trie.getUint8(offset)*2;
+ let relChildOffset = 2 + this.trie.getUint8(offset + 1)*2;
+ let childCount = (nodeSize - relChildOffset)/4;
+
+ /* Go through all children and find the next offset */
+ let childOffset = offset + relChildOffset;
+ let found = false;
+ for(let j = 0; j != childCount; ++j) {
+ if(String.fromCharCode(this.trie.getUint8(childOffset + j*4 + 3)) != searchString[foundPrefix])
+ continue;
+
+ this.searchStack.push(this.trie.getUint32(childOffset + j*4, true) & 0x00ffffff);
+ found = true;
+ break;
+ }
+
+ /* Character not found, exit */
+ if(!found) break;
+ }
+
+ /* Save the whole found prefix for next time */
+ this.searchString = searchString.substr(0, foundPrefix);
+
+ /* If the whole thing was not found, return an empty result and offer
+ external search */
+ if(foundPrefix != searchString.length) {
+ /* istanbul ignore if */
+ if(typeof document !== 'undefined') {
+ let link = document.getElementById('search-external');
+ if(link)
+ link.href = link.dataset.searchEngine.replace('{query}', encodeURIComponent(searchString));
+ }
+ return [];
+ }
+
+ /* Otherwise recursively gather the results */
+ let results = [];
+ this.gatherResults(this.searchStack[this.searchStack.length - 1], 0, results);
+ return results;
+ },
+
+ gatherResults: function(offset, suffixLength, results) {
+ let valueCount = this.trie.getUint8(offset + 1);
+
+ /* Populate the results with all values associated with this node */
+ for(let i = 0; i != valueCount; ++i) {
+ let index = this.trie.getUint16(offset + (i + 1)*2, true);
+ //let flags = this.map.getUint8(index*4 + 3); /* not used yet */
+ let resultOffset = this.map.getUint32(index*4, true) & 0x00ffffff;
+ let nextResultOffset = this.map.getUint32((index + 1)*4, true) & 0x00ffffff;
+
+ let name = '';
+ let j = resultOffset;
+ for(; j != nextResultOffset; ++j) {
+ let c = this.map.getUint8(j);
+
+ /* End of null-delimited name */
+ if(!c) {
+ ++j;
+ break; /* null-delimited */
+ }
+
+ name += String.fromCharCode(c); /* eheh. IS THIS FAST?! */
+ }
+
+ let url = '';
+ for(; j != nextResultOffset; ++j) {
+ url += String.fromCharCode(this.map.getUint8(j));
+ }
+
+ results.push({name: name, url: url, suffixLength: suffixLength});
+
+ /* 'nuff said. */
+ /* TODO: remove once proper barriers are in */
+ if(results.length >= this.maxResults) return true;
+ }
+
+ /* Dig deeper. If the child already has enough, return. */
+ /* TODO: hmmm. this is helluvalot duplicated code. hmm. */
+ let nodeSize = this.trie.getUint8(offset)*2;
+ let relChildOffset = 2 + this.trie.getUint8(offset + 1)*2;
+ let childCount = (nodeSize - relChildOffset)/4;
+ let childOffset = offset + relChildOffset;
+ for(let j = 0; j != childCount; ++j)
+ if(this.gatherResults(this.trie.getUint32(childOffset + j*4, true) & 0x00ffffff, suffixLength + 1, results))
+ return true;
+
+ /* Still hungry. */
+ return false;
+ },
+
+ escapeForRtl: function(name) {
+ /* Besides the obvious escaping of HTML entities we also need
+ to escape punctuation, because due to the RTL hack to cut
+ text off on left side the punctuation characters get
+ reordered (of course). Prepending ‎ works for most
+ characters, parentheses we need to *soak* in it. But only
+ the right ones. And that for some reason needs to be also for &.
+ Huh. https://en.wikipedia.org/wiki/Right-to-left_mark */
+ return name.replace(/[\"&<>]/g, function (a) {
+ return { '"': '"', '&': '&', '<': '<', '>': '>' }[a];
+ }).replace(/[:=]/g, '‎$&').replace(/(\)|>|&|\/)/g, '‎$&‎');
+ },
+
+ renderResults: /* istanbul ignore next */ function(value, results) {
+ /* Normalize the value length so the slicing works properly */
+ value = value.trim();
+
+ if(!value.length) {
+ document.getElementById('search-help').style.display = 'block';
+ document.getElementById('search-results').style.display = 'none';
+ document.getElementById('search-notfound').style.display = 'none';
+ return;
+ }
+
+ document.getElementById('search-help').style.display = 'none';
+
+ if(results.length) {
+ document.getElementById('search-results').style.display = 'block';
+ document.getElementById('search-notfound').style.display = 'none';
+
+ var list = '';
+ for(let i = 0; i != results.length; ++i) {
+ list += '<li' + (i ? '' : ' id="search-current"') + '><a href="' + results[i].url + '" onmouseover="selectResult(event)"><div><span class="m-text m-dim">' + this.escapeForRtl(results[i].name.substr(0, results[i].name.length - value.length - results[i].suffixLength)) + '</span><span class="m-dox-search-typed">' + this.escapeForRtl(results[i].name.substr(results[i].name.length - value.length - results[i].suffixLength, value.length)) + '</span>' + this.escapeForRtl(results[i].name.substr(results[i].name.length - results[i].suffixLength)) + '</div></a></li>';
+ }
+ document.getElementById('search-results').innerHTML = list;
+ document.getElementById('search-current').scrollIntoView(true);
+
+ } else {
+ document.getElementById('search-results').style.display = 'none';
+ document.getElementById('search-notfound').style.display = 'block';
+ }
+ },
+};
+
+/* istanbul ignore next */
+function selectResult(event) {
+ if(event.currentTarget.parentNode.id == 'search-current') return;
+
+ let current = document.getElementById('search-current');
+ current.id = '';
+ event.currentTarget.parentNode.id = 'search-current';
+}
+
+/* istanbul ignore next */
+function showSearch() {
+ window.location.hash = '#search';
+ document.getElementById('search-input').value = '';
+ document.getElementById('search-input').focus();
+ document.getElementById('search-results').style.display = 'none';
+ document.getElementById('search-notfound').style.display = 'none';
+ document.getElementById('search-help').style.display = 'block';
+ return false;
+}
+
+/* istanbul ignore next */
+function hideSearch() {
+ window.location.hash = '#!';
+ window.history.pushState('', '', window.location.pathname);
+ return false;
+}
+
+/* Only in case we're running in a browser. Why a simple if(document) doesn't
+ work is beyond me. */ /* istanbul ignore if */
+if(typeof document !== 'undefined') {
+ document.getElementById('search-input').oninput = function(event) {
+ let value = document.getElementById('search-input').value;
+ Search.renderResults(value, Search.search(value));
+ };
+
+ document.onkeydown = function(event) {
+ /* Search shown */
+ if(window.location.hash == '#search') {
+ /* Close the search */
+ if(event.key == 'Escape') {
+ hideSearch();
+
+ /* Select next item */
+ } else if(event.key == 'ArrowDown' || (event.key == 'Tab' && !event.shiftKey)) {
+ let current = document.getElementById('search-current');
+ if(current) {
+ let next = current.nextSibling;
+ if(next) {
+ current.id = '';
+ next.id = 'search-current';
+ next.scrollIntoView(false);
+ }
+ }
+ return false; /* so the keypress doesn't affect input cursor */
+
+ /* Select prev item */
+ } else if(event.key == 'ArrowUp' || (event.key == 'Tab' && event.shiftKey)) {
+ let current = document.getElementById('search-current');
+ if(current) {
+ let prev = current.previousSibling;
+ if(prev) {
+ current.id = '';
+ prev.id = 'search-current';
+ prev.scrollIntoView(false);
+ }
+ }
+ return false; /* so the keypress doesn't affect input cursor */
+
+ /* Go to result */
+ } else if(event.key == 'Enter') {
+ document.getElementById('search-current').firstElementChild.click();
+ return false; /* so the keypress doesn't affect input cursor */
+ }
+
+ /* Search hidden */
+ } else {
+ /* Open the search on the T or Tab key */
+ if(event.key.toLowerCase() == 't' || event.key == 'Tab') {
+ showSearch();
+ return false; /* so T doesn't get entered into the box */
+ }
+ }
+ };
+}
+
+/* For Node.js testing */ /* istanbul ignore else */
+if(typeof module !== 'undefined') { module.exports = { Search: Search }; }
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} <span class="m-thin">{{ PROJECT_BRIEF }}</span>{% endif %}</a>
- {% if M_LINKS_NAVBAR1 or M_LINKS_NAVBAR2 %}
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ {% if M_LINKS_NAVBAR1 or M_LINKS_NAVBAR2 or not M_SEARCH_DISABLED %}
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ {% if not M_SEARCH_DISABLED %}
+ <a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a>
+ {% endif %}
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="{% if M_LINKS_NAVBAR2 %}m-col-t-6{% else %}m-col-t-12{% endif %} m-col-m-none">
{% endif %}
{% endfor %}
</ol>
- {% if M_LINKS_NAVBAR2 %}
+ {% if M_LINKS_NAVBAR2 or not M_SEARCH_DISABLED %}
{% set start = M_LINKS_NAVBAR1|length + 1 %}
<ol class="m-col-t-6 m-col-m-none" start="{{ start }}">
{% for title, link, id, sub in M_LINKS_NAVBAR2 %}
</li>
{% endif %}
{% endfor %}
+ {% if not M_SEARCH_DISABLED %}
+ <li class="m-show-m"><a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a></li>
+ {% endif %}
</ol>
{% endif %}
</div>
</div>
</div>
</article></main>
+{% if not M_SEARCH_DISABLED %}
+<div class="m-dox-search" id="search">
+ <a href="#!" onclick="return hideSearch()"></a>
+ <div class="m-container">
+ <div class="m-row">
+ <div class="m-col-m-8 m-push-m-2">
+ <div class="m-dox-search-header m-text m-small">
+ <div><span class="m-label m-default">Tab</span> / <span class="m-label m-default">T</span> to search, <span class="m-label m-default">Esc</span> to close</div>
+ <div id="search-symbolcount">…</div>
+ </div>
+ <div class="m-dox-search-content">
+ <input type="search" id="search-input" placeholder="Loading …" disabled="disabled" autofocus="autofocus" />
+ <noscript class="m-text m-danger m-text-center">Unlike everything else in the docs, the search functionality <em>requires</em> JavaScript.{% if M_SEARCH_EXTERNAL_URL %} Enable it or <a href="{{ M_SEARCH_EXTERNAL_URL|replace('{query}', '') }}">use an external search engine</a>.{% endif %}</noscript>
+ <div id="search-help" class="m-text m-dim m-text-center">
+ {{ M_SEARCH_HELP }}
+ </div>
+ <div id="search-notfound" class="m-text m-warning m-text-center">Sorry, nothing was found.{% if M_SEARCH_EXTERNAL_URL %}<br />Maybe try a full-text <a href="#" id="search-external" data-search-engine="{{ M_SEARCH_EXTERNAL_URL }}">search with external engine</a>?{% endif %}</div>
+ <ul id="search-results"></ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script src="search.js"></script>
+{% if M_SEARCH_DOWNLOAD_BINARY %}
+<script>
+ Search.download(window.location.pathname.substr(0, window.location.pathname.lastIndexOf('/') + 1) + "searchdata.bin");
+</script>
+{% else %}
+<script src="searchdata.js" async="async"></script>
+{% endif %}
+{% endif %}
{% if M_PAGE_FINE_PRINT %}
<footer><nav>
<div class="m-container">
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
-
+M_SEARCH_DISABLED = YES
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
M_EXPAND_INNER_TYPES = YES
M_PAGE_FINE_PRINT =
M_THEME_COLOR =
+M_SEARCH_DISABLED = YES
# Keeping navbar enabled so we can test for highlighted items
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
</div>
</article></main>
</body>
-</html>
\ No newline at end of file
+</html>
M_THEME_COLOR =
M_LINKS_NAVBAR1 = modules
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-12 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-12 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-12 m-col-m-none">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-12 m-col-m-none">
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
ALIASES = \
"m_div{1}=@xmlonly<mcss:div xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\">@endxmlonly" \
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_LINKS_NAVBAR1 = "files pages namespaces" \
"annotated namespaces pages"
M_LINKS_NAVBAR2 = "pages pages annotated"
+M_SEARCH_EXTERNAL_URL = "https://google.com/search?q=site:mcss.mosra.cz+{}"
+M_SEARCH_HELP = "Some <em>help</em>."
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A project <span class="m-thin">is cool</span></a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a>
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<li><a href="annotated.html">Classes</a></li>
</ol>
</li>
+ <li class="m-show-m"><a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a></li>
</ol>
</div>
</div>
</div>
</div>
</article></main>
+<div class="m-dox-search" id="search">
+ <a href="#!" onclick="return hideSearch()"></a>
+ <div class="m-container">
+ <div class="m-row">
+ <div class="m-col-m-8 m-push-m-2">
+ <div class="m-dox-search-header m-text m-small">
+ <div><span class="m-label m-default">Tab</span> / <span class="m-label m-default">T</span> to search, <span class="m-label m-default">Esc</span> to close</div>
+ <div id="search-symbolcount">…</div>
+ </div>
+ <div class="m-dox-search-content">
+ <input type="search" id="search-input" placeholder="Loading …" disabled="disabled" autofocus="autofocus" />
+ <noscript class="m-text m-danger m-text-center">Unlike everything else in the docs, the search functionality <em>requires</em> JavaScript. Enable it or <a href="https://google.com/search?q=site:mcss.mosra.cz+{}">use an external search engine</a>.</noscript>
+ <div id="search-help" class="m-text m-dim m-text-center">
+ Some <em>help</em>.
+ </div>
+ <div id="search-notfound" class="m-text m-warning m-text-center">Sorry, nothing was found.<br />Maybe try a full-text <a href="#" id="search-external" data-search-engine="https://google.com/search?q=site:mcss.mosra.cz+{}">search with external engine</a>?</div>
+ <ul id="search-results"></ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script src="search.js"></script>
+<script src="searchdata.js" async="async"></script>
<footer><nav>
<div class="m-container">
<div class="m-row">
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a>
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
<ol class="m-col-t-6 m-col-m-none" start="3">
<li><a href="annotated.html">Classes</a></li>
<li><a href="files.html">Files</a></li>
+ <li class="m-show-m"><a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a></li>
</ol>
</div>
</div>
</div>
</div>
</article></main>
+<div class="m-dox-search" id="search">
+ <a href="#!" onclick="return hideSearch()"></a>
+ <div class="m-container">
+ <div class="m-row">
+ <div class="m-col-m-8 m-push-m-2">
+ <div class="m-dox-search-header m-text m-small">
+ <div><span class="m-label m-default">Tab</span> / <span class="m-label m-default">T</span> to search, <span class="m-label m-default">Esc</span> to close</div>
+ <div id="search-symbolcount">…</div>
+ </div>
+ <div class="m-dox-search-content">
+ <input type="search" id="search-input" placeholder="Loading …" disabled="disabled" autofocus="autofocus" />
+ <noscript class="m-text m-danger m-text-center">Unlike everything else in the docs, the search functionality <em>requires</em> JavaScript.</noscript>
+ <div id="search-help" class="m-text m-dim m-text-center">
+ Search for symbols, headers, pages or example source files. You can omit any prefix from the symbol or file path.
+ </div>
+ <div id="search-notfound" class="m-text m-warning m-text-center">Sorry, nothing was found.</div>
+ <ul id="search-results"></ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script src="search.js"></script>
+<script src="searchdata.js" async="async"></script>
<footer><nav>
<div class="m-container">
<div class="m-row">
M_LINKS_NAVBAR2 =
M_PAGE_FINE_PRINT =
M_THEME_COLOR =
+M_SEARCH_DISABLED = YES
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-12 m-col-m-none">
--- /dev/null
+XML_OUTPUT =
+
+M_PAGE_FINE_PRINT =
+M_THEME_COLOR =
+M_LINKS_NAVBAR1 =
+M_LINKS_NAVBAR2 =
+M_SEARCH_DOWNLOAD_BINARY = YES
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a>
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
+ <div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
+ <div class="m-row">
+ <ol class="m-col-t-12 m-col-m-none">
+ </ol>
+ <ol class="m-col-t-6 m-col-m-none" start="1">
+ <li class="m-show-m"><a href="#search" class="m-dox-search-icon" title="Search" onclick="return showSearch()"><svg viewBox="0 0 16 16">
+ <path d="m6 0c-3.3144 0-6 2.6856-6 6 0 3.3144 2.6856 6 6 6 1.4858 0 2.8463-0.54083 3.8945-1.4355-0.0164 0.33797 0.14734 0.75854 0.5 1.1504l3.2227 3.7891c0.55185 0.6139 1.4517 0.66544 2.002 0.11524 0.55022-0.55022 0.49866-1.4501-0.11524-2.002l-3.7891-3.2246c-0.39184-0.35266-0.81242-0.51469-1.1504-0.5 0.89472-1.0482 1.4355-2.4088 1.4355-3.8945 0-3.3128-2.6856-5.998-6-5.998zm0 1.5625a4.4375 4.4375 0 0 1 4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375 4.4375 4.4375 4.4375 0 0 1-4.4375-4.4375 4.4375 4.4375 0 0 1 4.4375-4.4375z"/>
+ </svg></a></li>
+ </ol>
+ </div>
+ </div>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ My Project
+ </h1>
+ </div>
+ </div>
+ </div>
+</article></main>
+<div class="m-dox-search" id="search">
+ <a href="#!" onclick="return hideSearch()"></a>
+ <div class="m-container">
+ <div class="m-row">
+ <div class="m-col-m-8 m-push-m-2">
+ <div class="m-dox-search-header m-text m-small">
+ <div><span class="m-label m-default">Tab</span> / <span class="m-label m-default">T</span> to search, <span class="m-label m-default">Esc</span> to close</div>
+ <div id="search-symbolcount">…</div>
+ </div>
+ <div class="m-dox-search-content">
+ <input type="search" id="search-input" placeholder="Loading …" disabled="disabled" autofocus="autofocus" />
+ <noscript class="m-text m-danger m-text-center">Unlike everything else in the docs, the search functionality <em>requires</em> JavaScript.</noscript>
+ <div id="search-help" class="m-text m-dim m-text-center">
+ Search for symbols, headers, pages or example source files. You can omit any prefix from the symbol or file path.
+ </div>
+ <div id="search-notfound" class="m-text m-warning m-text-center">Sorry, nothing was found.</div>
+ <ul id="search-results"></ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script src="search.js"></script>
+<script>
+ Search.download(window.location.pathname.substr(0, window.location.pathname.lastIndexOf('/') + 1) + "searchdata.bin");
+</script>
+</body>
+</html>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8' standalone='no'?>
+<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.8.14">
+ <compounddef id="indexpage" kind="page">
+ <compoundname>index</compoundname>
+ <title>My Project</title>
+ <briefdescription>
+ </briefdescription>
+ <detaileddescription>
+ </detaileddescription>
+ </compounddef>
+</doxygen>
+
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
ALIASES = \
"m_footernavigation=@xmlonly<mcss:footernavigation xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" />@endxmlonly"
"page-b"
M_LINKS_NAVBAR2 = "page-b" \
"page-in-navbar page-b files"
+M_SEARCH_DISABLED = YES
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
</div>
</article></main>
</body>
-</html>
\ No newline at end of file
+</html>
<div class="m-container">
<div class="m-row">
<a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
- <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
- <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div class="m-col-t-3 m-hide-m m-text-right m-nopadr">
+ <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+ </div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="m-col-t-6 m-col-m-none">
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
M_THEME_COLOR =
M_LINKS_NAVBAR1 =
M_LINKS_NAVBAR2 =
+M_SEARCH_DISABLED = YES
--- /dev/null
+#!/usr/bin/env python
+
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+import base64
+import os
+import sys
+import pathlib
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
+
+from dox2html5 import Trie, ResultMap, serialize_search_data
+
+basedir = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))/'js-test-data'
+
+with open(basedir/'short.bin', 'wb') as f:
+ f.write(b'')
+with open(basedir/'wrong-magic.bin', 'wb') as f:
+ f.write(b'MOS\0 ')
+with open(basedir/'wrong-version.bin', 'wb') as f:
+ f.write(b'MCS\1 ')
+with open(basedir/'empty.bin', 'wb') as f:
+ f.write(serialize_search_data(Trie(), ResultMap()))
+
+trie = Trie()
+map = ResultMap()
+
+trie.insert("math", map.add("Math", "namespaceMath.html"))
+index = map.add("Math::min()", "namespaceMath.html#min")
+trie.insert("math::min()", index)
+trie.insert("min()", index)
+index = map.add("Math::Vector", "classMath_1_1Vector.html")
+trie.insert("math::vector", index)
+trie.insert("vector", index)
+index = map.add("Math::Vector::min()", "classMath_1_1Vector.html#min")
+trie.insert("math::vector::min()", index)
+trie.insert("vector::min()", index)
+trie.insert("min()", index)
+index = map.add("Math::Range", "classMath_1_1Range.html")
+trie.insert("math::range", index)
+trie.insert("range", index)
+index = map.add("Math::Range::min()", "classMath_1_1Range.html#min")
+trie.insert("math::range::min()", index)
+trie.insert("range::min()", index)
+trie.insert("min()", index)
+
+with open(basedir/'searchdata.bin', 'wb') as f:
+ f.write(serialize_search_data(trie, map))
+with open(basedir/'searchdata.b85', 'wb') as f:
+ f.write(base64.b85encode(serialize_search_data(trie, map), True))
--- /dev/null
+/** @dir Dir
+ * @brief A directory
+ */
+
+/** @file
+ * @brief A file
+ */
+
+/** @brief A namespace */
+namespace Namespace {
+
+/** @brief A class */
+class Class {
+ public:
+ /** @brief Function without arguments */
+ void foo();
+
+ void foo() const; /**< @overload */
+
+ void foo() &&; /**< @overload */
+
+ /** @brief Function with arguments */
+ void foo(with, arguments);
+};
+
+/** @brief A variable */
+constexpr int Variable = 5;
+
+/** @brief A typedef */
+typedef int Typedef;
+
+/** @brief An enum */
+enum class Enum {
+ Value = 15 /**< Enum value */
+};
+
+}
+
+/** @brief A macro */
+#define MACRO
+
+/** @brief Macro function */
+#define MACRO_FUNCTION()
+
+/** @brief Macro function with params */
+#define MACRO_FUNCTION_WITH_PARAMS(params)
+
+namespace UndocumentedNamespace {}
+
+class UndocumentedClass {};
+
+void undocumentedFunction();
+
+constexpr int UndocumentedVariable = 42;
+
+typedef int UndocumentedTypedef;
+
+enum class UndocumentedEnum {
+ UndocumentedValue
+};
+
+#define UNDOCUMENTED_MACRO
--- /dev/null
+INPUT = Dir UndocumentedDir input.dox
+QUIET = YES
+GENERATE_HTML = NO
+GENERATE_LATEX = NO
+GENERATE_XML = YES
+EXAMPLE_PATH = .
+
+M_PAGE_FINE_PRINT =
+M_THEME_COLOR =
+M_LINKS_NAVBAR1 =
+M_LINKS_NAVBAR2 =
+M_SEARCH_DOWNLOAD_BINARY = YES
--- /dev/null
+/* Hic sunt leones. */
--- /dev/null
+int main() {
+ return 42;
+}
--- /dev/null
+/**
+@page page A page
+
+- @subpage subpage Subpage
+*/
+
+/**
+@page subpage Subpage
+
+Some content.
+*/
+
+/** @example example.cpp */
--- /dev/null
+/*
+ This file is part of m.css.
+
+ Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+"use strict"; /* it summons the Cthulhu in a proper way, they say */
+
+const { Search } = require('../search.js');
+
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const { StringDecoder } = require('string_decoder');
+
+/* HTML escaping with RTL workarounds */
+{
+ assert.equal(Search.escapeForRtl("foo()"), "foo(‎)‎");
+ /* Not sure why / and & has to be escaped from both sides */
+ assert.equal(Search.escapeForRtl("Dir/"), "Dir‎/‎");
+ assert.equal(Search.escapeForRtl("foo() &&"), "foo(‎)‎ ‎&‎‎&‎");
+ assert.equal(Search.escapeForRtl("operator=()"), "operator‎=(‎)‎");
+ assert.equal(Search.escapeForRtl("NS::Class<int>"), "NS‎:‎:Class<int‎>‎");
+}
+
+/* Simple base85 decoding */
+{
+ let b85 = 'Xk~0{Zy-ZbL0VZLcW-iRWFa9T';
+ let buf = Search.base85decode(b85);
+ assert.equal(Buffer.from(buf).toString(), "hello CRAZY world!!!");
+}
+
+/* Unpadded base85 decoding */
+{
+ let b85 = 'Xk~0{Zy-ZbL0VZLcW-iRWFa9Te';
+ let buf = Search.base85decode(b85);
+ assert.ok(typeof buf === 'undefined');
+}
+
+/* Mess in base85 is interpreted as zeros */
+{
+ let b85 = '\'".: ';
+ let buf = Search.base85decode(b85);
+ assert.deepEqual(Buffer.from(buf), Buffer.from([0, 0, 0, 0]));
+}
+
+/* Verify that base85-decoded file is equivalent to the binary */
+{
+ let binary = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
+ assert.ok(binary.byteLength, 531);
+ let b85 = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.b85"), {encoding: 'utf-8'});
+ assert.deepEqual(new DataView(binary.buffer.slice(binary.byteOffset, binary.byteOffset + binary.byteLength)), new DataView(Search.base85decode(b85), 0, binary.byteLength));
+}
+
+/* Opening a too short file */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/short.bin"));
+ assert.ok(!Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
+}
+
+/* Opening file with wrong signature */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/wrong-magic.bin"));
+ assert.ok(!Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
+}
+
+/* Opening file with */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/wrong-version.bin"));
+ assert.ok(!Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
+}
+
+/* Search with empty data */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/empty.bin"));
+ assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
+ assert.equal(Search.symbolCount, 0);
+ assert.deepEqual(Search.search(''), []);
+}
+
+/* Search */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
+ assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
+ assert.equal(Search.symbolCount, 6);
+ assert.equal(Search.maxResults, 100);
+
+ /* Blow up */
+ let resultsForM = [
+ { name: 'Math',
+ url: 'namespaceMath.html',
+ suffixLength: 3 },
+ { name: 'Math::min()',
+ url: 'namespaceMath.html#min',
+ suffixLength: 10 },
+ { name: 'Math::Vector',
+ url: 'classMath_1_1Vector.html',
+ suffixLength: 11 },
+ { name: 'Math::Vector::min()',
+ url: 'classMath_1_1Vector.html#min',
+ suffixLength: 18 },
+ { name: 'Math::Range',
+ url: 'classMath_1_1Range.html',
+ suffixLength: 10 },
+ { name: 'Math::Range::min()',
+ url: 'classMath_1_1Range.html#min',
+ suffixLength: 17 },
+ { name: 'Math::min()',
+ url: 'namespaceMath.html#min',
+ suffixLength: 4 },
+ { name: 'Math::Vector::min()',
+ url: 'classMath_1_1Vector.html#min',
+ suffixLength: 4 },
+ { name: 'Math::Range::min()',
+ url: 'classMath_1_1Range.html#min',
+ suffixLength: 4 } ];
+ assert.deepEqual(Search.search('m'), resultsForM);
+
+ /* Add more characters */
+ assert.deepEqual(Search.search('min'), [
+ { name: 'Math::min()',
+ url: 'namespaceMath.html#min',
+ suffixLength: 2 },
+ { name: 'Math::Vector::min()',
+ url: 'classMath_1_1Vector.html#min',
+ suffixLength: 2 },
+ { name: 'Math::Range::min()',
+ url: 'classMath_1_1Range.html#min',
+ suffixLength: 2 } ]);
+
+ /* Go back, get the same thing */
+ assert.deepEqual(Search.search('m'), resultsForM);
+
+ /* Search for something else */
+ let resultsForVec = [
+ { name: 'Math::Vector',
+ url: 'classMath_1_1Vector.html',
+ suffixLength: 3 },
+ { name: 'Math::Vector::min()',
+ url: 'classMath_1_1Vector.html#min',
+ suffixLength: 10 }];
+ assert.deepEqual(Search.search('vec'), resultsForVec);
+
+ /* Uppercase things and spaces */
+ assert.deepEqual(Search.search(' Vec '), resultsForVec);
+
+ /* Not found */
+ assert.deepEqual(Search.search('pizza'), []);
+}
+
+/* Search, limiting the results to 3 */
+{
+ let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
+ assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), 3));
+ assert.equal(Search.symbolCount, 6);
+ assert.equal(Search.maxResults, 3);
+ assert.deepEqual(Search.search('m'), [
+ { name: 'Math',
+ url: 'namespaceMath.html',
+ suffixLength: 3 },
+ { name: 'Math::min()',
+ url: 'namespaceMath.html#min',
+ suffixLength: 10 },
+ { name: 'Math::Vector',
+ url: 'classMath_1_1Vector.html',
+ suffixLength: 11 }]);
+}
+
+/* Search loaded from a base85-encoded file should work properly */
+{
+ let b85 = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.b85"), {encoding: 'utf-8'});
+ assert.ok(Search.load(b85));
+ assert.equal(Search.symbolCount, 6);
+ assert.equal(Search.maxResults, 100);
+ assert.deepEqual(Search.search('min'), [
+ { name: 'Math::min()',
+ url: 'namespaceMath.html#min',
+ suffixLength: 2 },
+ { name: 'Math::Vector::min()',
+ url: 'classMath_1_1Vector.html#min',
+ suffixLength: 2 },
+ { name: 'Math::Range::min()',
+ url: 'classMath_1_1Range.html#min',
+ suffixLength: 2 } ]);
+}
+
+/* Not testing Search.download() because the xmlhttprequest npm package is *crap* */
from dox2html5 import parse_doxyfile, State
class Doxyfile(unittest.TestCase):
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+
+ # Display ALL THE DIFFS
+ self.maxDiff = None
+
def test(self):
state = State()
parse_doxyfile(state, 'test/doxyfile/Doxyfile')
'M_LINKS_NAVBAR2': ['annotated', 'files'],
'M_PAGE_FINE_PRINT': 'this is "quotes"',
'M_PAGE_HEADER': 'this is "quotes" \'apostrophes\'',
+ 'M_SEARCH_DISABLED': False,
+ 'M_SEARCH_DOWNLOAD_BINARY': False,
+ 'M_SEARCH_EXTERNAL_URL': '',
+ 'M_SEARCH_HELP': 'Search for symbols, headers, pages or example source files. '
+ 'You can omit any prefix from the symbol or file path.',
'M_THEME_COLOR': '#22272e',
'OUTPUT_DIRECTORY': '',
'PROJECT_BRIEF': 'is cool',
# DEALINGS IN THE SOFTWARE.
#
+import os
+
from test import BaseTestCase
class Layout(BaseTestCase):
def test(self):
self.run_dox2html5(wildcard='index.xml')
self.assertEqual(*self.actual_expected_contents('pages.html'))
+ self.assertTrue(os.path.exists(os.path.join(self.path, 'html', 'm-dark+doxygen.compiled.css')))
+ self.assertTrue(os.path.exists(os.path.join(self.path, 'html', 'search.js')))
+ self.assertTrue(os.path.exists(os.path.join(self.path, 'html', 'searchdata.js')))
class LayoutMinimal(BaseTestCase):
def __init__(self, *args, **kwargs):
def test(self):
self.run_dox2html5(wildcard='indexpage.xml')
self.assertEqual(*self.actual_expected_contents('index.html'))
+
+class LayoutSearchBinary(BaseTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, 'search_binary', *args, **kwargs)
+
+ def test(self):
+ self.run_dox2html5(wildcard='indexpage.xml')
+ self.assertEqual(*self.actual_expected_contents('index.html'))
+ self.assertTrue(os.path.exists(os.path.join(self.path, 'html', 'searchdata.bin')))
#
import argparse
-import unittest
+import os
import sys
+import unittest
from types import SimpleNamespace as Empty
from dox2html5 import Trie, ResultMap, serialize_search_data, search_data_header_struct
+from test import IntegrationTestCase
+
def _pretty_print_trie(serialized: bytearray, hashtable, stats, base_offset, indent, draw_pipe, show_merged) -> str:
# Visualize where the trees were merged
if show_merged and base_offset in hashtable: return ' #'
""")
self.assertEqual(len(serialized), 241)
+class Search(IntegrationTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, '', *args, **kwargs)
+
+ def test(self):
+ self.run_dox2html5(index_pages=[], wildcard='*.xml')
+
+ with open(os.path.join(self.path, 'html', 'searchdata.bin'), 'rb') as f:
+ search_data_pretty = pretty_print(f.read())[0]
+ #print(search_data_pretty)
+ self.assertEqual(search_data_pretty, """
+namespace [0]
+| ::class [1]
+| | ::foo() [6, 7, 8, 9]
+| enum [11]
+| | ::value [10]
+| typedef [12]
+| variable [13]
+class [1]
+| ::foo() [6, 7, 8, 9]
+a page [2]
+subpage [3]
+dir [4]
+| /file.h [5]
+file.h [5]
+|oo() [6, 7, 8, 9]
+enum [11]
+| ::value [10]
+value [10]
+| riable [13]
+typedef [12]
+macro [14]
+| _function() [15]
+| _with_params() [16]
+0: Namespace [0] -> namespaceNamespace.html
+1: Namespace::Class [0] -> classNamespace_1_1Class.html
+2: A page [0] -> page.html
+3: Subpage [0] -> subpage.html
+4: Dir [0] -> dir_da5033def2d0db76e9883b31b76b3d0c.html
+5: Dir/File.h [0] -> File_8h.html
+6: Namespace::Class::foo() [0] -> classNamespace_1_1Class.html#aaeba4096356215868370d6ea476bf5d9
+7: Namespace::Class::foo() [0] -> classNamespace_1_1Class.html#ac03c5b93907dda16763eabd26b25500a
+8: Namespace::Class::foo() [0] -> classNamespace_1_1Class.html#ac9e7e80d06281e30cfcc13171d117ade
+9: Namespace::Class::foo() [0] -> classNamespace_1_1Class.html#ac03e8437172963981197eb393e0550d3
+10: Namespace::Enum::Value [0] -> namespaceNamespace.html#add172b93283b1ab7612c3ca6cc5dcfeaa689202409e48743b914713f96d93947c
+11: Namespace::Enum [0] -> namespaceNamespace.html#add172b93283b1ab7612c3ca6cc5dcfea
+12: Namespace::Typedef [0] -> namespaceNamespace.html#abe2a245304bc2234927ef33175646e08
+13: Namespace::Variable [0] -> namespaceNamespace.html#ad3121960d8665ab045ca1bfa1480a86d
+14: MACRO [0] -> File_8h.html#a824c99cb152a3c2e9111a2cb9c34891e
+15: MACRO_FUNCTION() [0] -> File_8h.html#a025158d6007b306645a8eb7c7a9237c1
+16: MACRO_FUNCTION_WITH_PARAMS() [0] -> File_8h.html#a88602bba5a72becb4f2dc544ce12c420
+""".strip())
+
if __name__ == '__main__': # pragma: no cover
parser = argparse.ArgumentParser()
parser.add_argument('file', help="file to pretty-print")