[Libreoffice-commits] help.git: CustomTarget_html.mk help3xsl/default.css help3xsl/fuse.js help3xsl/fuzzysort.js help3xsl/get_bookmark.xsl help3xsl/help.js help3xsl/online_transform.xsl help3xsl/paginathing.js Package_html_static.mk source/text

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Fri Nov 9 20:15:19 UTC 2018


 CustomTarget_html.mk                    |    4 
 Package_html_static.mk                  |    2 
 help3xsl/default.css                    |    9 
 help3xsl/fuse.js                        |  997 --------------------------------
 help3xsl/fuzzysort.js                   |  608 +++++++++++++++++++
 help3xsl/get_bookmark.xsl               |   52 +
 help3xsl/help.js                        |   81 +-
 help3xsl/online_transform.xsl           |    2 
 help3xsl/paginathing.js                 |    4 
 source/text/shared/help/browserhelp.xhp |    2 
 10 files changed, 694 insertions(+), 1067 deletions(-)

New commits:
commit 9fb9990b3568c973cc41d5600492e0791e44d40a
Author:     Ilmari Lauhakangas <ilmari.lauhakangas at libreoffice.org>
AuthorDate: Fri Nov 9 19:46:07 2018 +0200
Commit:     Olivier Hallot <olivier.hallot at libreoffice.org>
CommitDate: Fri Nov 9 21:14:57 2018 +0100

    tdf#119415 change from fuse.js to fuzzysort.js
    
    Tied the index listing to the current module.
    Fuzzysort.js is MIT licensed.
    
    Change-Id: I5d95cc1870a1856089217846ed0af572abc882d7
    Reviewed-on: https://gerrit.libreoffice.org/63203
    Tested-by: Jenkins
    Reviewed-by: Olivier Hallot <olivier.hallot at libreoffice.org>

diff --git a/CustomTarget_html.mk b/CustomTarget_html.mk
index 00c6c7e2e..50fd6aedd 100644
--- a/CustomTarget_html.mk
+++ b/CustomTarget_html.mk
@@ -157,9 +157,9 @@ $(call gb_CustomTarget_get_workdir,helpcontent2/help3xsl)/%/bookmarks.js :
 	$(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),CAT,2)
 	$(call gb_Helper_abbreviate_dirs,\
 		( \
-			echo 'document.getElementsByClassName( "index" )[0].innerHTML='"'"'\' \
+			echo 'var bookmarks = [' \
 			&& cat $(filter %.part,$^) \
-			&& echo "'" \
+			&& echo '];' \
 		) > $@ \
 	)
 
diff --git a/Package_html_static.mk b/Package_html_static.mk
index bab29c9b0..59941b020 100644
--- a/Package_html_static.mk
+++ b/Package_html_static.mk
@@ -18,7 +18,7 @@ $(eval $(call gb_Package_add_file,helpcontent2_html_static,$(LIBO_SHARE_HELP_FOL
 
 $(eval $(call gb_Package_add_files,helpcontent2_html_static,$(LIBO_SHARE_HELP_FOLDER)$(if $(HELP_ONLINE),/$(PRODUCTVERSION)),\
 	default.css \
-	fuse.js \
+	fuzzysort.js \
 	help.js \
 	help2.js \
 	normalize.css \
diff --git a/help3xsl/default.css b/help3xsl/default.css
index a67a03628..6fee23221 100644
--- a/help3xsl/default.css
+++ b/help3xsl/default.css
@@ -287,7 +287,7 @@ h6 {
 .debug {
     border: 1px solid black;
     padding: 3px;
-    display: show;
+    display: none;
     background-color: #222;
     color: red;
     text-align: left;
@@ -563,13 +563,10 @@ aside input[type=checkbox]:checked ~ .contents-treeview {
 .pagination a {
     text-decoration: none;
 }
-.fuseshown {
+.index a {
     display: block;
 }
-.fusehidden {
-    display: none;
-}
-.fuseshown.hidden {
+.index .hidden {
     display: none;
 }
 li.active {
diff --git a/help3xsl/fuse.js b/help3xsl/fuse.js
deleted file mode 100644
index b451ec1c0..000000000
--- a/help3xsl/fuse.js
+++ /dev/null
@@ -1,997 +0,0 @@
-/*!
- * Fuse.js v3.2.0 - Lightweight fuzzy-search (http://fusejs.io)
- * 
- * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
- * All Rights Reserved. Apache Software License 2.0
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-(function webpackUniversalModuleDefinition(root, factory) {
-	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory();
-	else if(typeof define === 'function' && define.amd)
-		define("Fuse", [], factory);
-	else if(typeof exports === 'object')
-		exports["Fuse"] = factory();
-	else
-		root["Fuse"] = factory();
-})(this, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/ 	// The module cache
-/******/ 	var installedModules = {};
-/******/
-/******/ 	// The require function
-/******/ 	function __webpack_require__(moduleId) {
-/******/
-/******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId]) {
-/******/ 			return installedModules[moduleId].exports;
-/******/ 		}
-/******/ 		// Create a new module (and put it into the cache)
-/******/ 		var module = installedModules[moduleId] = {
-/******/ 			i: moduleId,
-/******/ 			l: false,
-/******/ 			exports: {}
-/******/ 		};
-/******/
-/******/ 		// Execute the module function
-/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-/******/
-/******/ 		// Flag the module as loaded
-/******/ 		module.l = true;
-/******/
-/******/ 		// Return the exports of the module
-/******/ 		return module.exports;
-/******/ 	}
-/******/
-/******/
-/******/ 	// expose the modules object (__webpack_modules__)
-/******/ 	__webpack_require__.m = modules;
-/******/
-/******/ 	// expose the module cache
-/******/ 	__webpack_require__.c = installedModules;
-/******/
-/******/ 	// identity function for calling harmony imports with the correct context
-/******/ 	__webpack_require__.i = function(value) { return value; };
-/******/
-/******/ 	// define getter function for harmony exports
-/******/ 	__webpack_require__.d = function(exports, name, getter) {
-/******/ 		if(!__webpack_require__.o(exports, name)) {
-/******/ 			Object.defineProperty(exports, name, {
-/******/ 				configurable: false,
-/******/ 				enumerable: true,
-/******/ 				get: getter
-/******/ 			});
-/******/ 		}
-/******/ 	};
-/******/
-/******/ 	// getDefaultExport function for compatibility with non-harmony modules
-/******/ 	__webpack_require__.n = function(module) {
-/******/ 		var getter = module && module.__esModule ?
-/******/ 			function getDefault() { return module['default']; } :
-/******/ 			function getModuleExports() { return module; };
-/******/ 		__webpack_require__.d(getter, 'a', getter);
-/******/ 		return getter;
-/******/ 	};
-/******/
-/******/ 	// Object.prototype.hasOwnProperty.call
-/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
-/******/
-/******/ 	// __webpack_public_path__
-/******/ 	__webpack_require__.p = "";
-/******/
-/******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 8);
-/******/ })
-/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = function (obj) {
-  return Object.prototype.toString.call(obj) === '[object Array]';
-};
-
-/***/ }),
-/* 1 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var bitapRegexSearch = __webpack_require__(5);
-var bitapSearch = __webpack_require__(7);
-var patternAlphabet = __webpack_require__(4);
-
-var Bitap = function () {
-  function Bitap(pattern, _ref) {
-    var _ref$location = _ref.location,
-        location = _ref$location === undefined ? 0 : _ref$location,
-        _ref$distance = _ref.distance,
-        distance = _ref$distance === undefined ? 100 : _ref$distance,
-        _ref$threshold = _ref.threshold,
-        threshold = _ref$threshold === undefined ? 0.6 : _ref$threshold,
-        _ref$maxPatternLength = _ref.maxPatternLength,
-        maxPatternLength = _ref$maxPatternLength === undefined ? 32 : _ref$maxPatternLength,
-        _ref$isCaseSensitive = _ref.isCaseSensitive,
-        isCaseSensitive = _ref$isCaseSensitive === undefined ? false : _ref$isCaseSensitive,
-        _ref$tokenSeparator = _ref.tokenSeparator,
-        tokenSeparator = _ref$tokenSeparator === undefined ? / +/g : _ref$tokenSeparator,
-        _ref$findAllMatches = _ref.findAllMatches,
-        findAllMatches = _ref$findAllMatches === undefined ? false : _ref$findAllMatches,
-        _ref$minMatchCharLeng = _ref.minMatchCharLength,
-        minMatchCharLength = _ref$minMatchCharLeng === undefined ? 1 : _ref$minMatchCharLeng;
-
-    _classCallCheck(this, Bitap);
-
-    this.options = {
-      location: location,
-      distance: distance,
-      threshold: threshold,
-      maxPatternLength: maxPatternLength,
-      isCaseSensitive: isCaseSensitive,
-      tokenSeparator: tokenSeparator,
-      findAllMatches: findAllMatches,
-      minMatchCharLength: minMatchCharLength
-    };
-
-    this.pattern = this.options.isCaseSensitive ? pattern : pattern.toLowerCase();
-
-    if (this.pattern.length <= maxPatternLength) {
-      this.patternAlphabet = patternAlphabet(this.pattern);
-    }
-  }
-
-  _createClass(Bitap, [{
-    key: 'search',
-    value: function search(text) {
-      if (!this.options.isCaseSensitive) {
-        text = text.toLowerCase();
-      }
-
-      // Exact match
-      if (this.pattern === text) {
-        return {
-          isMatch: true,
-          score: 0,
-          matchedIndices: [[0, text.length - 1]]
-        };
-      }
-
-      // When pattern length is greater than the machine word length, just do a a regex comparison
-      var _options = this.options,
-          maxPatternLength = _options.maxPatternLength,
-          tokenSeparator = _options.tokenSeparator;
-
-      if (this.pattern.length > maxPatternLength) {
-        return bitapRegexSearch(text, this.pattern, tokenSeparator);
-      }
-
-      // Otherwise, use Bitap algorithm
-      var _options2 = this.options,
-          location = _options2.location,
-          distance = _options2.distance,
-          threshold = _options2.threshold,
-          findAllMatches = _options2.findAllMatches,
-          minMatchCharLength = _options2.minMatchCharLength;
-
-      return bitapSearch(text, this.pattern, this.patternAlphabet, {
-        location: location,
-        distance: distance,
-        threshold: threshold,
-        findAllMatches: findAllMatches,
-        minMatchCharLength: minMatchCharLength
-      });
-    }
-  }]);
-
-  return Bitap;
-}();
-
-// let x = new Bitap("od mn war", {})
-// let result = x.search("Old Man's War")
-// console.log(result)
-
-module.exports = Bitap;
-
-/***/ }),
-/* 2 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var isArray = __webpack_require__(0);
-
-var deepValue = function deepValue(obj, path, list) {
-  if (!path) {
-    // If there's no path left, we've gotten to the object we care about.
-    list.push(obj);
-  } else {
-    var dotIndex = path.indexOf('.');
-    var firstSegment = path;
-    var remaining = null;
-
-    if (dotIndex !== -1) {
-      firstSegment = path.slice(0, dotIndex);
-      remaining = path.slice(dotIndex + 1);
-    }
-
-    var value = obj[firstSegment];
-
-    if (value !== null && value !== undefined) {
-      if (!remaining && (typeof value === 'string' || typeof value === 'number')) {
-        list.push(value.toString());
-      } else if (isArray(value)) {
-        // Search each item in the array.
-        for (var i = 0, len = value.length; i < len; i += 1) {
-          deepValue(value[i], remaining, list);
-        }
-      } else if (remaining) {
-        // An object. Recurse further.
-        deepValue(value, remaining, list);
-      }
-    }
-  }
-
-  return list;
-};
-
-module.exports = function (obj, path) {
-  return deepValue(obj, path, []);
-};
-
-/***/ }),
-/* 3 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = function () {
-  var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
-  var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
-
-  var matchedIndices = [];
-  var start = -1;
-  var end = -1;
-  var i = 0;
-
-  for (var len = matchmask.length; i < len; i += 1) {
-    var match = matchmask[i];
-    if (match && start === -1) {
-      start = i;
-    } else if (!match && start !== -1) {
-      end = i - 1;
-      if (end - start + 1 >= minMatchCharLength) {
-        matchedIndices.push([start, end]);
-      }
-      start = -1;
-    }
-  }
-
-  // (i-1 - start) + 1 => i - start
-  if (matchmask[i - 1] && i - start >= minMatchCharLength) {
-    matchedIndices.push([start, i - 1]);
-  }
-
-  return matchedIndices;
-};
-
-/***/ }),
-/* 4 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = function (pattern) {
-  var mask = {};
-  var len = pattern.length;
-
-  for (var i = 0; i < len; i += 1) {
-    mask[pattern.charAt(i)] = 0;
-  }
-
-  for (var _i = 0; _i < len; _i += 1) {
-    mask[pattern.charAt(_i)] |= 1 << len - _i - 1;
-  }
-
-  return mask;
-};
-
-/***/ }),
-/* 5 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var SPECIAL_CHARS_REGEX = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;
-
-module.exports = function (text, pattern) {
-  var tokenSeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : / +/g;
-
-  var regex = new RegExp(pattern.replace(SPECIAL_CHARS_REGEX, '\\$&').replace(tokenSeparator, '|'));
-  var matches = text.match(regex);
-  var isMatch = !!matches;
-  var matchedIndices = [];
-
-  if (isMatch) {
-    for (var i = 0, matchesLen = matches.length; i < matchesLen; i += 1) {
-      var match = matches[i];
-      matchedIndices.push([text.indexOf(match), match.length - 1]);
-    }
-  }
-
-  return {
-    // TODO: revisit this score
-    score: isMatch ? 0.5 : 1,
-    isMatch: isMatch,
-    matchedIndices: matchedIndices
-  };
-};
-
-/***/ }),
-/* 6 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = function (pattern, _ref) {
-  var _ref$errors = _ref.errors,
-      errors = _ref$errors === undefined ? 0 : _ref$errors,
-      _ref$currentLocation = _ref.currentLocation,
-      currentLocation = _ref$currentLocation === undefined ? 0 : _ref$currentLocation,
-      _ref$expectedLocation = _ref.expectedLocation,
-      expectedLocation = _ref$expectedLocation === undefined ? 0 : _ref$expectedLocation,
-      _ref$distance = _ref.distance,
-      distance = _ref$distance === undefined ? 100 : _ref$distance;
-
-  var accuracy = errors / pattern.length;
-  var proximity = Math.abs(expectedLocation - currentLocation);
-
-  if (!distance) {
-    // Dodge divide by zero error.
-    return proximity ? 1.0 : accuracy;
-  }
-
-  return accuracy + proximity / distance;
-};
-
-/***/ }),
-/* 7 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var bitapScore = __webpack_require__(6);
-var matchedIndices = __webpack_require__(3);
-
-module.exports = function (text, pattern, patternAlphabet, _ref) {
-  var _ref$location = _ref.location,
-      location = _ref$location === undefined ? 0 : _ref$location,
-      _ref$distance = _ref.distance,
-      distance = _ref$distance === undefined ? 100 : _ref$distance,
-      _ref$threshold = _ref.threshold,
-      threshold = _ref$threshold === undefined ? 0.6 : _ref$threshold,
-      _ref$findAllMatches = _ref.findAllMatches,
-      findAllMatches = _ref$findAllMatches === undefined ? false : _ref$findAllMatches,
-      _ref$minMatchCharLeng = _ref.minMatchCharLength,
-      minMatchCharLength = _ref$minMatchCharLeng === undefined ? 1 : _ref$minMatchCharLeng;
-
-  var expectedLocation = location;
-  // Set starting location at beginning text and initialize the alphabet.
-  var textLen = text.length;
-  // Highest score beyond which we give up.
-  var currentThreshold = threshold;
-  // Is there a nearby exact match? (speedup)
-  var bestLocation = text.indexOf(pattern, expectedLocation);
-
-  var patternLen = pattern.length;
-
-  // a mask of the matches
-  var matchMask = [];
-  for (var i = 0; i < textLen; i += 1) {
-    matchMask[i] = 0;
-  }
-
-  if (bestLocation !== -1) {
-    var score = bitapScore(pattern, {
-      errors: 0,
-      currentLocation: bestLocation,
-      expectedLocation: expectedLocation,
-      distance: distance
-    });
-    currentThreshold = Math.min(score, currentThreshold);
-
-    // What about in the other direction? (speed up)
-    bestLocation = text.lastIndexOf(pattern, expectedLocation + patternLen);
-
-    if (bestLocation !== -1) {
-      var _score = bitapScore(pattern, {
-        errors: 0,
-        currentLocation: bestLocation,
-        expectedLocation: expectedLocation,
-        distance: distance
-      });
-      currentThreshold = Math.min(_score, currentThreshold);
-    }
-  }
-
-  // Reset the best location
-  bestLocation = -1;
-
-  var lastBitArr = [];
-  var finalScore = 1;
-  var binMax = patternLen + textLen;
-
-  var mask = 1 << patternLen - 1;
-
-  for (var _i = 0; _i < patternLen; _i += 1) {
-    // Scan for the best match; each iteration allows for one more error.
-    // Run a binary search to determine how far from the match location we can stray
-    // at this error level.
-    var binMin = 0;
-    var binMid = binMax;
-
-    while (binMin < binMid) {
-      var _score3 = bitapScore(pattern, {
-        errors: _i,
-        currentLocation: expectedLocation + binMid,
-        expectedLocation: expectedLocation,
-        distance: distance
-      });
-
-      if (_score3 <= currentThreshold) {
-        binMin = binMid;
-      } else {
-        binMax = binMid;
-      }
-
-      binMid = Math.floor((binMax - binMin) / 2 + binMin);
-    }
-
-    // Use the result from this iteration as the maximum for the next.
-    binMax = binMid;
-
-    var start = Math.max(1, expectedLocation - binMid + 1);
-    var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen;
-
-    // Initialize the bit array
-    var bitArr = Array(finish + 2);
-
-    bitArr[finish + 1] = (1 << _i) - 1;
-
-    for (var j = finish; j >= start; j -= 1) {
-      var currentLocation = j - 1;
-      var charMatch = patternAlphabet[text.charAt(currentLocation)];
-
-      if (charMatch) {
-        matchMask[currentLocation] = 1;
-      }
-
-      // First pass: exact match
-      bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch;
-
-      // Subsequent passes: fuzzy match
-      if (_i !== 0) {
-        bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1];
-      }
-
-      if (bitArr[j] & mask) {
-        finalScore = bitapScore(pattern, {
-          errors: _i,
-          currentLocation: currentLocation,
-          expectedLocation: expectedLocation,
-          distance: distance
-        });
-
-        // This match will almost certainly be better than any existing match.
-        // But check anyway.
-        if (finalScore <= currentThreshold) {
-          // Indeed it is
-          currentThreshold = finalScore;
-          bestLocation = currentLocation;
-
-          // Already passed `loc`, downhill from here on in.
-          if (bestLocation <= expectedLocation) {
-            break;
-          }
-
-          // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`.
-          start = Math.max(1, 2 * expectedLocation - bestLocation);
-        }
-      }
-    }
-
-    // No hope for a (better) match at greater error levels.
-    var _score2 = bitapScore(pattern, {
-      errors: _i + 1,
-      currentLocation: expectedLocation,
-      expectedLocation: expectedLocation,
-      distance: distance
-    });
-
-    if (_score2 > currentThreshold) {
-      break;
-    }
-
-    lastBitArr = bitArr;
-  }
-
-  // Count exact matches (those with a score of 0) to be "almost" exact
-  return {
-    isMatch: bestLocation >= 0,
-    score: finalScore === 0 ? 0.001 : finalScore,
-    matchedIndices: matchedIndices(matchMask, minMatchCharLength)
-  };
-};
-
-/***/ }),
-/* 8 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var Bitap = __webpack_require__(1);
-var deepValue = __webpack_require__(2);
-var isArray = __webpack_require__(0);
-
-var Fuse = function () {
-  function Fuse(list, _ref) {
-    var _ref$location = _ref.location,
-        location = _ref$location === undefined ? 0 : _ref$location,
-        _ref$distance = _ref.distance,
-        distance = _ref$distance === undefined ? 100 : _ref$distance,
-        _ref$threshold = _ref.threshold,
-        threshold = _ref$threshold === undefined ? 0.6 : _ref$threshold,
-        _ref$maxPatternLength = _ref.maxPatternLength,
-        maxPatternLength = _ref$maxPatternLength === undefined ? 32 : _ref$maxPatternLength,
-        _ref$caseSensitive = _ref.caseSensitive,
-        caseSensitive = _ref$caseSensitive === undefined ? false : _ref$caseSensitive,
-        _ref$tokenSeparator = _ref.tokenSeparator,
-        tokenSeparator = _ref$tokenSeparator === undefined ? / +/g : _ref$tokenSeparator,
-        _ref$findAllMatches = _ref.findAllMatches,
-        findAllMatches = _ref$findAllMatches === undefined ? false : _ref$findAllMatches,
-        _ref$minMatchCharLeng = _ref.minMatchCharLength,
-        minMatchCharLength = _ref$minMatchCharLeng === undefined ? 1 : _ref$minMatchCharLeng,
-        _ref$id = _ref.id,
-        id = _ref$id === undefined ? null : _ref$id,
-        _ref$keys = _ref.keys,
-        keys = _ref$keys === undefined ? [] : _ref$keys,
-        _ref$shouldSort = _ref.shouldSort,
-        shouldSort = _ref$shouldSort === undefined ? true : _ref$shouldSort,
-        _ref$getFn = _ref.getFn,
-        getFn = _ref$getFn === undefined ? deepValue : _ref$getFn,
-        _ref$sortFn = _ref.sortFn,
-        sortFn = _ref$sortFn === undefined ? function (a, b) {
-      return a.score - b.score;
-    } : _ref$sortFn,
-        _ref$tokenize = _ref.tokenize,
-        tokenize = _ref$tokenize === undefined ? false : _ref$tokenize,
-        _ref$matchAllTokens = _ref.matchAllTokens,
-        matchAllTokens = _ref$matchAllTokens === undefined ? false : _ref$matchAllTokens,
-        _ref$includeMatches = _ref.includeMatches,
-        includeMatches = _ref$includeMatches === undefined ? false : _ref$includeMatches,
-        _ref$includeScore = _ref.includeScore,
-        includeScore = _ref$includeScore === undefined ? false : _ref$includeScore,
-        _ref$verbose = _ref.verbose,
-        verbose = _ref$verbose === undefined ? false : _ref$verbose;
-
-    _classCallCheck(this, Fuse);
-
-    this.options = {
-      location: location,
-      distance: distance,
-      threshold: threshold,
-      maxPatternLength: maxPatternLength,
-      isCaseSensitive: caseSensitive,
-      tokenSeparator: tokenSeparator,
-      findAllMatches: findAllMatches,
-      minMatchCharLength: minMatchCharLength,
-      id: id,
-      keys: keys,
-      includeMatches: includeMatches,
-      includeScore: includeScore,
-      shouldSort: shouldSort,
-      getFn: getFn,
-      sortFn: sortFn,
-      verbose: verbose,
-      tokenize: tokenize,
-      matchAllTokens: matchAllTokens
-    };
-
-    this.setCollection(list);
-  }
-
-  _createClass(Fuse, [{
-    key: 'setCollection',
-    value: function setCollection(list) {
-      this.list = list;
-      return list;
-    }
-  }, {
-    key: 'search',
-    value: function search(pattern) {
-      this._log('---------\nSearch pattern: "' + pattern + '"');
-
-      var _prepareSearchers2 = this._prepareSearchers(pattern),
-          tokenSearchers = _prepareSearchers2.tokenSearchers,
-          fullSearcher = _prepareSearchers2.fullSearcher;
-
-      var _search2 = this._search(tokenSearchers, fullSearcher),
-          weights = _search2.weights,
-          results = _search2.results;
-
-      this._computeScore(weights, results);
-
-      if (this.options.shouldSort) {
-        this._sort(results);
-      }
-
-      return this._format(results);
-    }
-  }, {
-    key: '_prepareSearchers',
-    value: function _prepareSearchers() {
-      var pattern = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
-
-      var tokenSearchers = [];
-
-      if (this.options.tokenize) {
-        // Tokenize on the separator
-        var tokens = pattern.split(this.options.tokenSeparator);
-        for (var i = 0, len = tokens.length; i < len; i += 1) {
-          tokenSearchers.push(new Bitap(tokens[i], this.options));
-        }
-      }
-
-      var fullSearcher = new Bitap(pattern, this.options);
-
-      return { tokenSearchers: tokenSearchers, fullSearcher: fullSearcher };
-    }
-  }, {
-    key: '_search',
-    value: function _search() {
-      var tokenSearchers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
-      var fullSearcher = arguments[1];
-
-      var list = this.list;
-      var resultMap = {};
-      var results = [];
-
-      // Check the first item in the list, if it's a string, then we assume
-      // that every item in the list is also a string, and thus it's a flattened array.
-      if (typeof list[0] === 'string') {
-        // Iterate over every item
-        for (var i = 0, len = list.length; i < len; i += 1) {
-          this._analyze({
-            key: '',
-            value: list[i],
-            record: i,
-            index: i
-          }, {
-            resultMap: resultMap,
-            results: results,
-            tokenSearchers: tokenSearchers,
-            fullSearcher: fullSearcher
-          });
-        }
-
-        return { weights: null, results: results };
-      }
-
-      // Otherwise, the first item is an Object (hopefully), and thus the searching
-      // is done on the values of the keys of each item.
-      var weights = {};
-      for (var _i = 0, _len = list.length; _i < _len; _i += 1) {
-        var item = list[_i];
-        // Iterate over every key
-        for (var j = 0, keysLen = this.options.keys.length; j < keysLen; j += 1) {
-          var key = this.options.keys[j];
-          if (typeof key !== 'string') {
-            weights[key.name] = {
-              weight: 1 - key.weight || 1
-            };
-            if (key.weight <= 0 || key.weight > 1) {
-              throw new Error('Key weight has to be > 0 and <= 1');
-            }
-            key = key.name;
-          } else {
-            weights[key] = {
-              weight: 1
-            };
-          }
-
-          this._analyze({
-            key: key,
-            value: this.options.getFn(item, key),
-            record: item,
-            index: _i
-          }, {
-            resultMap: resultMap,
-            results: results,
-            tokenSearchers: tokenSearchers,
-            fullSearcher: fullSearcher
-          });
-        }
-      }
-
-      return { weights: weights, results: results };
-    }
-  }, {
-    key: '_analyze',
-    value: function _analyze(_ref2, _ref3) {
-      var key = _ref2.key,
-          _ref2$arrayIndex = _ref2.arrayIndex,
-          arrayIndex = _ref2$arrayIndex === undefined ? -1 : _ref2$arrayIndex,
-          value = _ref2.value,
-          record = _ref2.record,
-          index = _ref2.index;
-      var _ref3$tokenSearchers = _ref3.tokenSearchers,
-          tokenSearchers = _ref3$tokenSearchers === undefined ? [] : _ref3$tokenSearchers,
-          _ref3$fullSearcher = _ref3.fullSearcher,
-          fullSearcher = _ref3$fullSearcher === undefined ? [] : _ref3$fullSearcher,
-          _ref3$resultMap = _ref3.resultMap,
-          resultMap = _ref3$resultMap === undefined ? {} : _ref3$resultMap,
-          _ref3$results = _ref3.results,
-          results = _ref3$results === undefined ? [] : _ref3$results;
-
-      // Check if the text value can be searched
-      if (value === undefined || value === null) {
-        return;
-      }
-
-      var exists = false;
-      var averageScore = -1;
-      var numTextMatches = 0;
-
-      if (typeof value === 'string') {
-        this._log('\nKey: ' + (key === '' ? '-' : key));
-
-        var mainSearchResult = fullSearcher.search(value);
-        this._log('Full text: "' + value + '", score: ' + mainSearchResult.score);
-
-        if (this.options.tokenize) {
-          var words = value.split(this.options.tokenSeparator);
-          var scores = [];
-
-          for (var i = 0; i < tokenSearchers.length; i += 1) {
-            var tokenSearcher = tokenSearchers[i];
-
-            this._log('\nPattern: "' + tokenSearcher.pattern + '"');
-
-            // let tokenScores = []
-            var hasMatchInText = false;
-
-            for (var j = 0; j < words.length; j += 1) {
-              var word = words[j];
-              var tokenSearchResult = tokenSearcher.search(word);
-              var obj = {};
-              if (tokenSearchResult.isMatch) {
-                obj[word] = tokenSearchResult.score;
-                exists = true;
-                hasMatchInText = true;
-                scores.push(tokenSearchResult.score);
-              } else {
-                obj[word] = 1;
-                if (!this.options.matchAllTokens) {
-                  scores.push(1);
-                }
-              }
-              this._log('Token: "' + word + '", score: ' + obj[word]);
-              // tokenScores.push(obj)
-            }
-
-            if (hasMatchInText) {
-              numTextMatches += 1;
-            }
-          }
-
-          averageScore = scores[0];
-          var scoresLen = scores.length;
-          for (var _i2 = 1; _i2 < scoresLen; _i2 += 1) {
-            averageScore += scores[_i2];
-          }
-          averageScore = averageScore / scoresLen;
-
-          this._log('Token score average:', averageScore);
-        }
-
-        var finalScore = mainSearchResult.score;
-        if (averageScore > -1) {
-          finalScore = (finalScore + averageScore) / 2;
-        }
-
-        this._log('Score average:', finalScore);
-
-        var checkTextMatches = this.options.tokenize && this.options.matchAllTokens ? numTextMatches >= tokenSearchers.length : true;
-
-        this._log('\nCheck Matches: ' + checkTextMatches);
-
-        // If a match is found, add the item to <rawResults>, including its score
-        if ((exists || mainSearchResult.isMatch) && checkTextMatches) {
-          // Check if the item already exists in our results
-          var existingResult = resultMap[index];
-          if (existingResult) {
-            // Use the lowest score
-            // existingResult.score, bitapResult.score
-            existingResult.output.push({
-              key: key,
-              arrayIndex: arrayIndex,
-              value: value,
-              score: finalScore,
-              matchedIndices: mainSearchResult.matchedIndices
-            });
-          } else {
-            // Add it to the raw result list
-            resultMap[index] = {
-              item: record,
-              output: [{
-                key: key,
-                arrayIndex: arrayIndex,
-                value: value,
-                score: finalScore,
-                matchedIndices: mainSearchResult.matchedIndices
-              }]
-            };
-
-            results.push(resultMap[index]);
-          }
-        }
-      } else if (isArray(value)) {
-        for (var _i3 = 0, len = value.length; _i3 < len; _i3 += 1) {
-          this._analyze({
-            key: key,
-            arrayIndex: _i3,
-            value: value[_i3],
-            record: record,
-            index: index
-          }, {
-            resultMap: resultMap,
-            results: results,
-            tokenSearchers: tokenSearchers,
-            fullSearcher: fullSearcher
-          });
-        }
-      }
-    }
-  }, {
-    key: '_computeScore',
-    value: function _computeScore(weights, results) {
-      this._log('\n\nComputing score:\n');
-
-      for (var i = 0, len = results.length; i < len; i += 1) {
-        var output = results[i].output;
-        var scoreLen = output.length;
-
-        var totalScore = 0;
-        var bestScore = 1;
-
-        for (var j = 0; j < scoreLen; j += 1) {
-          var weight = weights ? weights[output[j].key].weight : 1;
-          var score = weight === 1 ? output[j].score : output[j].score || 0.001;
-          var nScore = score * weight;
-
-          if (weight !== 1) {
-            bestScore = Math.min(bestScore, nScore);
-          } else {
-            output[j].nScore = nScore;
-            totalScore += nScore;
-          }
-        }
-
-        results[i].score = bestScore === 1 ? totalScore / scoreLen : bestScore;
-
-        this._log(results[i]);
-      }
-    }
-  }, {
-    key: '_sort',
-    value: function _sort(results) {
-      this._log('\n\nSorting....');
-      results.sort(this.options.sortFn);
-    }
-  }, {
-    key: '_format',
-    value: function _format(results) {
-      var finalOutput = [];
-
-      if (this.options.verbose) {
-        this._log('\n\nOutput:\n\n', JSON.stringify(results));
-      }
-
-      var transformers = [];
-
-      if (this.options.includeMatches) {
-        transformers.push(function (result, data) {
-          var output = result.output;
-          data.matches = [];
-
-          for (var i = 0, len = output.length; i < len; i += 1) {
-            var item = output[i];
-
-            if (item.matchedIndices.length === 0) {
-              continue;
-            }
-
-            var obj = {
-              indices: item.matchedIndices,
-              value: item.value
-            };
-            if (item.key) {
-              obj.key = item.key;
-            }
-            if (item.hasOwnProperty('arrayIndex') && item.arrayIndex > -1) {
-              obj.arrayIndex = item.arrayIndex;
-            }
-            data.matches.push(obj);
-          }
-        });
-      }
-
-      if (this.options.includeScore) {
-        transformers.push(function (result, data) {
-          data.score = result.score;
-        });
-      }
-
-      for (var i = 0, len = results.length; i < len; i += 1) {
-        var result = results[i];
-
-        if (this.options.id) {
-          result.item = this.options.getFn(result.item, this.options.id)[0];
-        }
-
-        if (!transformers.length) {
-          finalOutput.push(result.item);
-          continue;
-        }
-
-        var data = {
-          item: result.item
-        };
-
-        for (var j = 0, _len2 = transformers.length; j < _len2; j += 1) {
-          transformers[j](result, data);
-        }
-
-        finalOutput.push(data);
-      }
-
-      return finalOutput;
-    }
-  }, {
-    key: '_log',
-    value: function _log() {
-      if (this.options.verbose) {
-        var _console;
-
-        (_console = console).log.apply(_console, arguments);
-      }
-    }
-  }]);
-
-  return Fuse;
-}();
-
-module.exports = Fuse;
-
-/***/ })
-/******/ ]);
-});
diff --git a/help3xsl/fuzzysort.js b/help3xsl/fuzzysort.js
new file mode 100644
index 000000000..78c8fccf6
--- /dev/null
+++ b/help3xsl/fuzzysort.js
@@ -0,0 +1,608 @@
+/*
+MIT License
+
+Copyright (c) 2018 Stephen Kamenar
+
+WHAT: SublimeText-like Fuzzy Search
+
+USAGE:
+  fuzzysort.single('fs', 'Fuzzy Search') // {score: -16}
+  fuzzysort.single('test', 'test') // {score: 0}
+  fuzzysort.single('doesnt exist', 'target') // null
+
+  fuzzysort.go('mr', ['Monitor.cpp', 'MeshRenderer.cpp'])
+  // [{score: -18, target: "MeshRenderer.cpp"}, {score: -6009, target: "Monitor.cpp"}]
+
+  fuzzysort.highlight(fuzzysort.single('fs', 'Fuzzy Search'), '<b>', '</b>')
+  // <b>F</b>uzzy <b>S</b>earch
+*/
+
+// UMD (Universal Module Definition) for fuzzysort
+;(function(root, UMD) {
+  if(typeof define === 'function' && define.amd) define([], UMD)
+  else if(typeof module === 'object' && module.exports) module.exports = UMD()
+  else root.fuzzysort = UMD()
+})(this, function UMD() { function fuzzysortNew(instanceOptions) {
+
+  var fuzzysort = {
+
+    single: function(search, target, options) {
+      if(!search) return null
+      if(!isObj(search)) search = fuzzysort.getPreparedSearch(search)
+
+      if(!target) return null
+      if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+      var allowTypo = options && options.allowTypo!==undefined ? options.allowTypo
+        : instanceOptions && instanceOptions.allowTypo!==undefined ? instanceOptions.allowTypo
+        : true
+      var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo
+      return algorithm(search, target, search[0])
+      // var threshold = options && options.threshold || instanceOptions && instanceOptions.threshold || -9007199254740991
+      // var result = algorithm(search, target, search[0])
+      // if(result === null) return null
+      // if(result.score < threshold) return null
+      // return result
+    },
+
+    go: function(search, targets, options) {
+      if(!search) return noResults
+      search = fuzzysort.prepareSearch(search)
+      var searchLowerCode = search[0]
+
+      var threshold = options && options.threshold || instanceOptions && instanceOptions.threshold || -9007199254740991
+      var limit = options && options.limit || instanceOptions && instanceOptions.limit || 9007199254740991
+      var allowTypo = options && options.allowTypo!==undefined ? options.allowTypo
+        : instanceOptions && instanceOptions.allowTypo!==undefined ? instanceOptions.allowTypo
+        : true
+      var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo
+      var resultsLen = 0; var limitedCount = 0
+      var targetsLen = targets.length
+
+      // This code is copy/pasted 3 times for performance reasons [options.keys, options.key, no keys]
+
+      // options.keys
+      if(options && options.keys) {
+        var scoreFn = options.scoreFn || defaultScoreFn
+        var keys = options.keys
+        var keysLen = keys.length
+        for(var i = targetsLen - 1; i >= 0; --i) { var obj = targets[i]
+          var objResults = new Array(keysLen)
+          for (var keyI = keysLen - 1; keyI >= 0; --keyI) {
+            var key = keys[keyI]
+            var target = getValue(obj, key)
+            if(!target) { objResults[keyI] = null; continue }
+            if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+            objResults[keyI] = algorithm(search, target, searchLowerCode)
+          }
+          objResults.obj = obj // before scoreFn so scoreFn can use it
+          var score = scoreFn(objResults)
+          if(score === null) continue
+          if(score < threshold) continue
+          objResults.score = score
+          if(resultsLen < limit) { q.add(objResults); ++resultsLen }
+          else {
+            ++limitedCount
+            if(score > q.peek().score) q.replaceTop(objResults)
+          }
+        }
+
+      // options.key
+      } else if(options && options.key) {
+        var key = options.key
+        for(var i = targetsLen - 1; i >= 0; --i) { var obj = targets[i]
+          var target = getValue(obj, key)
+          if(!target) continue
+          if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+          var result = algorithm(search, target, searchLowerCode)
+          if(result === null) continue
+          if(result.score < threshold) continue
+
+          // have to clone result so duplicate targets from different obj can each reference the correct obj
+          result = {target:result.target, _targetLowerCodes:null, _nextBeginningIndexes:null, score:result.score, indexes:result.indexes, obj:obj} // hidden
+
+          if(resultsLen < limit) { q.add(result); ++resultsLen }
+          else {
+            ++limitedCount
+            if(result.score > q.peek().score) q.replaceTop(result)
+          }
+        }
+
+      // no keys
+      } else {
+        for(var i = targetsLen - 1; i >= 0; --i) { var target = targets[i]
+          if(!target) continue
+          if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+          var result = algorithm(search, target, searchLowerCode)
+          if(result === null) continue
+          if(result.score < threshold) continue
+          if(resultsLen < limit) { q.add(result); ++resultsLen }
+          else {
+            ++limitedCount
+            if(result.score > q.peek().score) q.replaceTop(result)
+          }
+        }
+      }
+
+      if(resultsLen === 0) return noResults
+      var results = new Array(resultsLen)
+      for(var i = resultsLen - 1; i >= 0; --i) results[i] = q.poll()
+      results.total = resultsLen + limitedCount
+      return results
+    },
+
+    goAsync: function(search, targets, options) {
+      var canceled = false
+      var p = new Promise(function(resolve, reject) {
+        if(!search) return resolve(noResults)
+        search = fuzzysort.prepareSearch(search)
+        var searchLowerCode = search[0]
+
+        var q = fastpriorityqueue()
+        var iCurrent = targets.length - 1
+        var threshold = options && options.threshold || instanceOptions && instanceOptions.threshold || -9007199254740991
+        var limit = options && options.limit || instanceOptions && instanceOptions.limit || 9007199254740991
+        var allowTypo = options && options.allowTypo!==undefined ? options.allowTypo
+          : instanceOptions && instanceOptions.allowTypo!==undefined ? instanceOptions.allowTypo
+          : true
+        var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo
+        var resultsLen = 0; var limitedCount = 0
+        function step() {
+          if(canceled) return reject('canceled')
+
+          var startMs = Date.now()
+
+          // This code is copy/pasted 3 times for performance reasons [options.keys, options.key, no keys]
+
+          // options.keys
+          if(options && options.keys) {
+            var scoreFn = options.scoreFn || defaultScoreFn
+            var keys = options.keys
+            var keysLen = keys.length
+            for(; iCurrent >= 0; --iCurrent) { var obj = targets[iCurrent]
+              var objResults = new Array(keysLen)
+              for (var keyI = keysLen - 1; keyI >= 0; --keyI) {
+                var key = keys[keyI]
+                var target = getValue(obj, key)
+                if(!target) { objResults[keyI] = null; continue }
+                if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+                objResults[keyI] = algorithm(search, target, searchLowerCode)
+              }
+              objResults.obj = obj // before scoreFn so scoreFn can use it
+              var score = scoreFn(objResults)
+              if(score === null) continue
+              if(score < threshold) continue
+              objResults.score = score
+              if(resultsLen < limit) { q.add(objResults); ++resultsLen }
+              else {
+                ++limitedCount
+                if(score > q.peek().score) q.replaceTop(objResults)
+              }
+
+              if(iCurrent%1000/*itemsPerCheck*/ === 0) {
+                if(Date.now() - startMs >= 10/*asyncInterval*/) {
+                  isNode?setImmediate(step):setTimeout(step)
+                  return
+                }
+              }
+            }
+
+          // options.key
+          } else if(options && options.key) {
+            var key = options.key
+            for(; iCurrent >= 0; --iCurrent) { var obj = targets[iCurrent]
+              var target = getValue(obj, key)
+              if(!target) continue
+              if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+              var result = algorithm(search, target, searchLowerCode)
+              if(result === null) continue
+              if(result.score < threshold) continue
+
+              // have to clone result so duplicate targets from different obj can each reference the correct obj
+              result = {target:result.target, _targetLowerCodes:null, _nextBeginningIndexes:null, score:result.score, indexes:result.indexes, obj:obj} // hidden
+
+              if(resultsLen < limit) { q.add(result); ++resultsLen }
+              else {
+                ++limitedCount
+                if(result.score > q.peek().score) q.replaceTop(result)
+              }
+
+              if(iCurrent%1000/*itemsPerCheck*/ === 0) {
+                if(Date.now() - startMs >= 10/*asyncInterval*/) {
+                  isNode?setImmediate(step):setTimeout(step)
+                  return
+                }
+              }
+            }
+
+          // no keys
+          } else {
+            for(; iCurrent >= 0; --iCurrent) { var target = targets[iCurrent]
+              if(!target) continue
+              if(!isObj(target)) target = fuzzysort.getPrepared(target)
+
+              var result = algorithm(search, target, searchLowerCode)
+              if(result === null) continue
+              if(result.score < threshold) continue
+              if(resultsLen < limit) { q.add(result); ++resultsLen }
+              else {
+                ++limitedCount
+                if(result.score > q.peek().score) q.replaceTop(result)
+              }
+
+              if(iCurrent%1000/*itemsPerCheck*/ === 0) {
+                if(Date.now() - startMs >= 10/*asyncInterval*/) {
+                  isNode?setImmediate(step):setTimeout(step)
+                  return
+                }
+              }
+            }
+          }
+
+          if(resultsLen === 0) return resolve(noResults)
+          var results = new Array(resultsLen)
+          for(var i = resultsLen - 1; i >= 0; --i) results[i] = q.poll()
+          results.total = resultsLen + limitedCount
+          resolve(results)
+        }
+
+        isNode?setImmediate(step):step()
+      })
+      p.cancel = function() { canceled = true }
+      return p
+    },
+
+    highlight: function(result, hOpen, hClose) {
+      if(result === null) return null
+      if(hOpen === undefined) hOpen = '<b>'
+      if(hClose === undefined) hClose = '</b>'
+      var highlighted = ''
+      var matchesIndex = 0
+      var opened = false
+      var target = result.target
+      var targetLen = target.length
+      var matchesBest = result.indexes
+      for(var i = 0; i < targetLen; ++i) { var char = target[i]
+        if(matchesBest[matchesIndex] === i) {
+          ++matchesIndex
+          if(!opened) { opened = true
+            highlighted += hOpen
+          }
+
+          if(matchesIndex === matchesBest.length) {
+            highlighted += char + hClose + target.substr(i+1)
+            break
+          }
+        } else {
+          if(opened) { opened = false
+            highlighted += hClose
+          }
+        }
+        highlighted += char
+      }
+
+      return highlighted
+    },
+
+    prepare: function(target) {
+      if(!target) return
+      return {target:target, _targetLowerCodes:fuzzysort.prepareLowerCodes(target), _nextBeginningIndexes:null, score:null, indexes:null, obj:null} // hidden
+    },
+    prepareSlow: function(target) {
+      if(!target) return
+      return {target:target, _targetLowerCodes:fuzzysort.prepareLowerCodes(target), _nextBeginningIndexes:fuzzysort.prepareNextBeginningIndexes(target), score:null, indexes:null, obj:null} // hidden
+    },
+    prepareSearch: function(search) {
+      if(!search) return
+      return fuzzysort.prepareLowerCodes(search)
+    },
+
+
+
+    // Below this point is only internal code
+    // Below this point is only internal code
+    // Below this point is only internal code
+    // Below this point is only internal code
+
+
+
+    getPrepared: function(target) {
+      if(target.length > 999) return fuzzysort.prepare(target) // don't cache huge targets
+      var targetPrepared = preparedCache.get(target)
+      if(targetPrepared !== undefined) return targetPrepared
+      targetPrepared = fuzzysort.prepare(target)
+      preparedCache.set(target, targetPrepared)
+      return targetPrepared
+    },
+    getPreparedSearch: function(search) {
+      if(search.length > 999) return fuzzysort.prepareSearch(search) // don't cache huge searches
+      var searchPrepared = preparedSearchCache.get(search)
+      if(searchPrepared !== undefined) return searchPrepared
+      searchPrepared = fuzzysort.prepareSearch(search)
+      preparedSearchCache.set(search, searchPrepared)
+      return searchPrepared
+    },
+
+    algorithm: function(searchLowerCodes, prepared, searchLowerCode) {
+      var targetLowerCodes = prepared._targetLowerCodes
+      var searchLen = searchLowerCodes.length
+      var targetLen = targetLowerCodes.length
+      var searchI = 0 // where we at
+      var targetI = 0 // where you at
+      var typoSimpleI = 0
+      var matchesSimpleLen = 0
+
+      // very basic fuzzy match; to remove non-matching targets ASAP!
+      // walk through target. find sequential matches.
+      // if all chars aren't found then exit
+      for(;;) {
+        var isMatch = searchLowerCode === targetLowerCodes[targetI]
+        if(isMatch) {
+          matchesSimple[matchesSimpleLen++] = targetI
+          ++searchI; if(searchI === searchLen) break
+          searchLowerCode = searchLowerCodes[typoSimpleI===0?searchI : (typoSimpleI===searchI?searchI+1 : (typoSimpleI===searchI-1?searchI-1 : searchI))]
+        }
+
+        ++targetI; if(targetI >= targetLen) { // Failed to find searchI
+          // Check for typo or exit
+          // we go as far as possible before trying to transpose
+          // then we transpose backwards until we reach the beginning
+          for(;;) {
+            if(searchI <= 1) return null // not allowed to transpose first char
+            if(typoSimpleI === 0) { // we haven't tried to transpose yet
+              --searchI
+              var searchLowerCodeNew = searchLowerCodes[searchI]
+              if(searchLowerCode === searchLowerCodeNew) continue // doesn't make sense to transpose a repeat char
+              typoSimpleI = searchI
+            } else {
+              if(typoSimpleI === 1) return null // reached the end of the line for transposing
+              --typoSimpleI
+              searchI = typoSimpleI
+              searchLowerCode = searchLowerCodes[searchI + 1]
+              var searchLowerCodeNew = searchLowerCodes[searchI]
+              if(searchLowerCode === searchLowerCodeNew) continue // doesn't make sense to transpose a repeat char
+            }
+            matchesSimpleLen = searchI
+            targetI = matchesSimple[matchesSimpleLen - 1] + 1
+            break
+          }
+        }
+      }
+
+      var searchI = 0
+      var typoStrictI = 0
+      var successStrict = false
+      var matchesStrictLen = 0
+
+      var nextBeginningIndexes = prepared._nextBeginningIndexes
+      if(nextBeginningIndexes === null) nextBeginningIndexes = prepared._nextBeginningIndexes = fuzzysort.prepareNextBeginningIndexes(prepared.target)
+      var firstPossibleI = targetI = matchesSimple[0]===0 ? 0 : nextBeginningIndexes[matchesSimple[0]-1]
+
+      // Our target string successfully matched all characters in sequence!
+      // Let's try a more advanced and strict test to improve the score
+      // only count it as a match if it's consecutive or a beginning character!
+      if(targetI !== targetLen) for(;;) {
+        if(targetI >= targetLen) {
+          // We failed to find a good spot for this search char, go back to the previous search char and force it forward
+          if(searchI <= 0) { // We failed to push chars forward for a better match
+            // transpose, starting from the beginning
+            ++typoStrictI; if(typoStrictI > searchLen-2) break
+            if(searchLowerCodes[typoStrictI] === searchLowerCodes[typoStrictI+1]) continue // doesn't make sense to transpose a repeat char
+            targetI = firstPossibleI
+            continue
+          }
+
+          --searchI
+          var lastMatch = matchesStrict[--matchesStrictLen]
+          targetI = nextBeginningIndexes[lastMatch]
+
+        } else {
+          var isMatch = searchLowerCodes[typoStrictI===0?searchI : (typoStrictI===searchI?searchI+1 : (typoStrictI===searchI-1?searchI-1 : searchI))] === targetLowerCodes[targetI]
+          if(isMatch) {
+            matchesStrict[matchesStrictLen++] = targetI
+            ++searchI; if(searchI === searchLen) { successStrict = true; break }
+            ++targetI
+          } else {
+            targetI = nextBeginningIndexes[targetI]
+          }
+        }
+      }
+
+      { // tally up the score & keep track of matches for highlighting later
+        if(successStrict) { var matchesBest = matchesStrict; var matchesBestLen = matchesStrictLen }
+        else { var matchesBest = matchesSimple; var matchesBestLen = matchesSimpleLen }
+        var score = 0
+        var lastTargetI = -1
+        for(var i = 0; i < searchLen; ++i) { var targetI = matchesBest[i]
+          // score only goes down if they're not consecutive
+          if(lastTargetI !== targetI - 1) score -= targetI
+          lastTargetI = targetI
+        }
+        if(!successStrict) {
+          score *= 1000
+          if(typoSimpleI !== 0) score += -20/*typoPenalty*/
+        } else {
+          if(typoStrictI !== 0) score += -20/*typoPenalty*/
+        }
+        score -= targetLen - searchLen
+        prepared.score = score
+        prepared.indexes = new Array(matchesBestLen); for(var i = matchesBestLen - 1; i >= 0; --i) prepared.indexes[i] = matchesBest[i]
+
+        return prepared
+      }
+    },
+
+    algorithmNoTypo: function(searchLowerCodes, prepared, searchLowerCode) {
+      var targetLowerCodes = prepared._targetLowerCodes
+      var searchLen = searchLowerCodes.length
+      var targetLen = targetLowerCodes.length
+      var searchI = 0 // where we at
+      var targetI = 0 // where you at
+      var matchesSimpleLen = 0
+
+      // very basic fuzzy match; to remove non-matching targets ASAP!
+      // walk through target. find sequential matches.
+      // if all chars aren't found then exit
+      for(;;) {
+        var isMatch = searchLowerCode === targetLowerCodes[targetI]
+        if(isMatch) {
+          matchesSimple[matchesSimpleLen++] = targetI
+          ++searchI; if(searchI === searchLen) break
+          searchLowerCode = searchLowerCodes[searchI]
+        }
+        ++targetI; if(targetI >= targetLen) return null // Failed to find searchI
+      }
+
+      var searchI = 0
+      var successStrict = false
+      var matchesStrictLen = 0
+
+      var nextBeginningIndexes = prepared._nextBeginningIndexes
+      if(nextBeginningIndexes === null) nextBeginningIndexes = prepared._nextBeginningIndexes = fuzzysort.prepareNextBeginningIndexes(prepared.target)
+      var firstPossibleI = targetI = matchesSimple[0]===0 ? 0 : nextBeginningIndexes[matchesSimple[0]-1]
+
+      // Our target string successfully matched all characters in sequence!
+      // Let's try a more advanced and strict test to improve the score
+      // only count it as a match if it's consecutive or a beginning character!
+      if(targetI !== targetLen) for(;;) {
+        if(targetI >= targetLen) {
+          // We failed to find a good spot for this search char, go back to the previous search char and force it forward
+          if(searchI <= 0) break // We failed to push chars forward for a better match
+
+          --searchI
+          var lastMatch = matchesStrict[--matchesStrictLen]
+          targetI = nextBeginningIndexes[lastMatch]
+
+        } else {
+          var isMatch = searchLowerCodes[searchI] === targetLowerCodes[targetI]
+          if(isMatch) {
+            matchesStrict[matchesStrictLen++] = targetI
+            ++searchI; if(searchI === searchLen) { successStrict = true; break }
+            ++targetI
+          } else {
+            targetI = nextBeginningIndexes[targetI]
+          }
+        }
+      }
+
+      { // tally up the score & keep track of matches for highlighting later
+        if(successStrict) { var matchesBest = matchesStrict; var matchesBestLen = matchesStrictLen }
+        else { var matchesBest = matchesSimple; var matchesBestLen = matchesSimpleLen }
+        var score = 0
+        var lastTargetI = -1
+        for(var i = 0; i < searchLen; ++i) { var targetI = matchesBest[i]
+          // score only goes down if they're not consecutive
+          if(lastTargetI !== targetI - 1) score -= targetI
+          lastTargetI = targetI
+        }
+        if(!successStrict) score *= 1000
+        score -= targetLen - searchLen
+        prepared.score = score
+        prepared.indexes = new Array(matchesBestLen); for(var i = matchesBestLen - 1; i >= 0; --i) prepared.indexes[i] = matchesBest[i]
+
+        return prepared
+      }
+    },
+
+    prepareLowerCodes: function(str) {
+      var strLen = str.length
+      var lowerCodes = [] // new Array(strLen)    sparse array is too slow
+      var lower = str.toLowerCase()
+      for(var i = 0; i < strLen; ++i) lowerCodes[i] = lower.charCodeAt(i)
+      return lowerCodes
+    },
+    prepareBeginningIndexes: function(target) {
+      var targetLen = target.length
+      var beginningIndexes = []; var beginningIndexesLen = 0
+      var wasUpper = false
+      var wasAlphanum = false
+      for(var i = 0; i < targetLen; ++i) {
+        var targetCode = target.charCodeAt(i)
+        var isUpper = targetCode>=65&&targetCode<=90
+        var isAlphanum = isUpper || targetCode>=97&&targetCode<=122 || targetCode>=48&&targetCode<=57
+        var isBeginning = isUpper && !wasUpper || !wasAlphanum || !isAlphanum
+        wasUpper = isUpper
+        wasAlphanum = isAlphanum
+        if(isBeginning) beginningIndexes[beginningIndexesLen++] = i
+      }
+      return beginningIndexes
+    },
+    prepareNextBeginningIndexes: function(target) {
+      var targetLen = target.length
+      var beginningIndexes = fuzzysort.prepareBeginningIndexes(target)
+      var nextBeginningIndexes = [] // new Array(targetLen)     sparse array is too slow
+      var lastIsBeginning = beginningIndexes[0]
+      var lastIsBeginningI = 0
+      for(var i = 0; i < targetLen; ++i) {
+        if(lastIsBeginning > i) {
+          nextBeginningIndexes[i] = lastIsBeginning
+        } else {
+          lastIsBeginning = beginningIndexes[++lastIsBeginningI]
+          nextBeginningIndexes[i] = lastIsBeginning===undefined ? targetLen : lastIsBeginning
+        }
+      }
+      return nextBeginningIndexes
+    },
+
+    cleanup: cleanup,
+    new: fuzzysortNew,
+  }
+  return fuzzysort
+} // fuzzysortNew
+
+// This stuff is outside fuzzysortNew, because it's shared with instances of fuzzysort.new()
+var isNode = typeof require !== 'undefined' && typeof window === 'undefined'
+// var MAX_INT = Number.MAX_SAFE_INTEGER
+// var MIN_INT = Number.MIN_VALUE
+var preparedCache = new Map()
+var preparedSearchCache = new Map()
+var noResults = []; noResults.total = 0
+var matchesSimple = []; var matchesStrict = []
+function cleanup() { preparedCache.clear(); preparedSearchCache.clear(); matchesSimple = []; matchesStrict = [] }
+function defaultScoreFn(a) {
+  var max = -9007199254740991
+  for (var i = a.length - 1; i >= 0; --i) {
+    var result = a[i]; if(result === null) continue
+    var score = result.score
+    if(score > max) max = score
+  }
+  if(max === -9007199254740991) return null
+  return max
+}
+
+// prop = 'key'              2.5ms optimized for this case, seems to be about as fast as direct obj[prop]
+// prop = 'key1.key2'        10ms
+// prop = ['key1', 'key2']   27ms
+function getValue(obj, prop) {
+  var tmp = obj[prop]; if(tmp !== undefined) return tmp
+  var segs = prop
+  if(!Array.isArray(prop)) segs = prop.split('.')
+  var len = segs.length
+  var i = -1
+  while (obj && (++i < len)) obj = obj[segs[i]]
+  return obj
+}
+
+function isObj(x) { return typeof x === 'object' } // faster as a function
+
+// Hacked version of https://github.com/lemire/FastPriorityQueue.js
+var fastpriorityqueue=function(){var r=[],o=0,e={};function n(){for(var e=0,n=r[e],c=1;c<o;){var f=c+1;e=c,f<o&&r[f].score<r[c].score&&(e=f),r[e-1>>1]=r[e],c=1+(e<<1)}for(var a=e-1>>1;e>0&&n.score<r[a].score;a=(e=a)-1>>1)r[e]=r[a];r[e]=n}return e.add=function(e){var n=o;r[o++]=e;for(var c=n-1>>1;n>0&&e.score<r[c].score;c=(n=c)-1>>1)r[n]=r[c];r[n]=e},e.poll=function(){if(0!==o){var e=r[0];return r[0]=r[--o],n(),e}},e.peek=function(e){if(0!==o)return r[0]},e.replaceTop=function(o){r[0]=o,n()},e};
+var q = fastpriorityqueue() // reuse this, except for async, it needs to make its own
+
+return fuzzysortNew()
+}) // UMD
+
+// TODO: (performance) wasm version!?
+
+// TODO: (performance) layout memory in an optimal way to go fast by avoiding cache misses
+
+// TODO: (performance) preparedCache is a memory leak
+
+// TODO: (like sublime) backslash === forwardslash
+
+// TODO: (performance) i have no idea how well optizmied the allowing typos algorithm is
diff --git a/help3xsl/get_bookmark.xsl b/help3xsl/get_bookmark.xsl
index b3e0ab48b..692ded0ff 100644
--- a/help3xsl/get_bookmark.xsl
+++ b/help3xsl/get_bookmark.xsl
@@ -63,13 +63,13 @@ xsltproc get_bookmark.xsl <file.xhp>
         <xsl:variable name="hrefhtml" select="substring-before($filename,'xhp')"/>
         <xsl:variable name="href" select="concat($target,$Language,'/',$hrefhtml,'html?DbPAR=',$app,'#', at id)"/>
         <xsl:for-each select="bookmark_value">
-        <xsl:text disable-output-escaping="yes"><![CDATA[<a target="_top" href="]]></xsl:text>
+        <xsl:text disable-output-escaping="yes"><![CDATA[{url:"]]></xsl:text>
         <xsl:value-of select="$href"/>
-        <xsl:text disable-output-escaping="yes"><![CDATA[" class="fuseshown ]]></xsl:text>
+        <xsl:text disable-output-escaping="yes"><![CDATA[", app:"]]></xsl:text>
         <xsl:value-of select="$app"/>
-        <xsl:text disable-output-escaping="yes"><![CDATA[">]]></xsl:text>
+        <xsl:text disable-output-escaping="yes"><![CDATA[", text:"]]></xsl:text>
             <xsl:call-template name="replace"><xsl:with-param name="text" select="."/></xsl:call-template>
-            <xsl:text disable-output-escaping="yes"><![CDATA[</a>\]]>&#xA;</xsl:text>
+            <xsl:text disable-output-escaping="yes"><![CDATA["},]]>&#xA;</xsl:text>
         </xsl:for-each>
     </xsl:for-each>
 </xsl:template>
@@ -80,16 +80,20 @@ xsltproc get_bookmark.xsl <file.xhp>
         <xsl:with-param name="string">
             <xsl:call-template name="apostrophe">
                 <xsl:with-param name="string">
-                    <xsl:choose>
-                        <xsl:when test="contains($text,';')">
-                            <xsl:value-of select="substring-before($text,';')"/>
-                            <xsl:text disable-output-escaping="yes"><![CDATA[ -- ]]></xsl:text>
-                            <xsl:value-of select="substring-after($text,';')"/>
-                        </xsl:when>
-                        <xsl:otherwise>
-                            <xsl:value-of select="$text"/>
-                        </xsl:otherwise>
-                    </xsl:choose>
+                    <xsl:call-template name="doublequote">
+                        <xsl:with-param name="string">
+                            <xsl:choose>
+                                <xsl:when test="contains($text,';')">
+                                    <xsl:value-of select="substring-before($text,';')"/>
+                                    <xsl:text disable-output-escaping="yes"><![CDATA[ -- ]]></xsl:text>
+                                    <xsl:value-of select="substring-after($text,';')"/>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:value-of select="$text"/>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:with-param>
+                    </xsl:call-template>
                 </xsl:with-param>
             </xsl:call-template>
         </xsl:with-param>
@@ -117,6 +121,26 @@ xsltproc get_bookmark.xsl <file.xhp>
     </xsl:choose>
 </xsl:template>
 
+<xsl:template name="doublequote">
+    <xsl:param name="string"/>
+    <xsl:variable name="dq">"</xsl:variable><!-- double quote -->
+    <xsl:choose>
+        <xsl:when test="contains($string,$dq)">
+            <xsl:variable name="newstr">
+                <xsl:value-of select="substring-before($string,$dq)"/>
+                <xsl:text disable-output-escaping="yes"><![CDATA[&]]>#34;</xsl:text>
+                <xsl:value-of select="substring-after($string,$dq)"/>
+            </xsl:variable>
+            <xsl:call-template name="doublequote">
+                <xsl:with-param name="string" select="$newstr"/>
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="$string"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
 <xsl:template name="brand" >
     <xsl:param name="string"/>
 
diff --git a/help3xsl/help.js b/help3xsl/help.js
index 76d47dd77..b67df2452 100644
--- a/help3xsl/help.js
+++ b/help3xsl/help.js
@@ -8,65 +8,62 @@
  */
 
 // Pagination and fuzzy search
-var fuseshown = document.getElementsByClassName("fuseshown");
-var indexkids = document.getElementsByClassName("index")[0].children;
-var liElements = Array.prototype.slice.call(fuseshown).map(function(elm) {
-    var item = elm;
-    var linktext = item.textContent;
-    var fuseObject = {
-        item: item,
-        linktext: linktext
-    };
-    return fuseObject;
-});
-var fuse = new Fuse(liElements, {
-    keys: ["linktext"],
-    distance: 60,
-    location: 0,
-    threshold: 0.2,
-    tokenize: true,
-    matchAllTokens: true,
-    maxPatternLength: 24,
-    minMatchCharLength: 2
-});
+var url = document.URL;
+var moduleRegex = new RegExp('text\\/(\\w+)\\/');
+var regexArray = moduleRegex.exec(url);
+// get the module name from the URL and remove the first character
+var currentModule = regexArray[1].toUpperCase().substring(1);
+var results = null;
+var fullLinkified = '';
 var modules = ['CALC', 'WRITER', 'IMPRESS', 'DRAW', 'BASE', 'MATH', 'CHART', 'BASIC', 'SHARED'];
-
+var indexkids = function() { document.getElementsByClassName("index")[0].children; };
+// if user is not on a shared category page, limit the index to the current module + shared
+if(currentModule !== 'HARED') {
+    bookmarks = bookmarks.filter(function(obj) {
+            return obj['app'] === currentModule || obj['app'] === 'SHARED';
+        });
+}
+bookmarks.forEach(function(obj) {
+            fullLinkified += '<a href="' + obj['url'] + '" class="' + obj['app'] + '">' + obj['text'] + '</a>';
+        });
+function fullList() {
+    document.getElementsByClassName("index")[0].innerHTML = fullLinkified;
+    Paginator(document.getElementsByClassName("index")[0]);
+    addIds();
+}
+// add id to the first items of each category in the index. CSS ::before rule adds the heading text
 function addIds() {
     for (var i = 0, len = indexkids.length; i < len; i++) {
         indexkids[i].removeAttribute("id");
     }
     modules.forEach(function(module) {
-        var fuseshownModule = document.getElementsByClassName("fuseshown " + module)[0];
-        if (typeof fuseshownModule !== 'undefined') {
-            fuseshownModule.setAttribute("id", module);
+        var hoduleHeader = document.getElementsByClassName(module)[0];
+        if (typeof moduleHeader !== 'undefined') {
+            hoduleHeader.setAttribute("id", module);
         }
     });
 }
+// render the unfiltered index list on page load
+fullList();
+Paginator(document.getElementsByClassName("index")[0]);
+addIds();
+// filter the index list based on search field input
 var search = document.getElementById('search-bar');
 var filter = function() {
     var target = search.value.trim();
     if (target.length < 1) {
-        liElements.forEach(function(obj) {
-            obj.item.classList.add('fuseshown');
-            obj.item.classList.remove('fusehidden');
-        });
-        Paginator(document.getElementsByClassName("index")[0]);
-        addIds();
+        fullList();
         return;
     }
-    var results = fuse.search(target);
-    liElements.forEach(function(obj) {
-        obj.item.classList.add('fusehidden');
-        obj.item.classList.remove('fuseshown');
-    });
-    results.forEach(function(obj) {
-        obj.item.classList.add('fuseshown');
-        obj.item.classList.remove('fusehidden');
+    results = fuzzysort.go(target, bookmarks, {threshold: -15000, key:'text'});
+    var filtered = '';
+    results.forEach(function(result) {
+        filtered += '<a href="' + result.obj['url'] + '" class="' + result.obj['app'] + '">' + result.obj['text'] + '</a>';
     });
+    document.getElementsByClassName("index")[0].innerHTML = filtered;
     Paginator(document.getElementsByClassName("index")[0]);
     addIds();
 };
-
 function debounce(fn, wait) {
     var timeout;
     return function() {
@@ -76,9 +73,7 @@ function debounce(fn, wait) {
         }, (wait || 150));
     };
 }
-Paginator(document.getElementsByClassName("index")[0]);
-search.addEventListener('keyup', debounce(filter, 300));
-addIds();
+search.addEventListener('keyup', debounce(filter, 100));
 // copy useful content to clipboard on mouse click
 var copyable = document.getElementsByClassName("input");
 for (var i = 0, len = copyable.length; i < len; i++) {
diff --git a/help3xsl/online_transform.xsl b/help3xsl/online_transform.xsl
index a7c3fd1e4..18cadf233 100644
--- a/help3xsl/online_transform.xsl
+++ b/help3xsl/online_transform.xsl
@@ -153,7 +153,7 @@
         <script type="text/javascript" src="{$target}help2.js"></script>
         <script type="text/javascript" src="{$target}languages.js"></script>
         <script type="text/javascript" src="{$target}{$lang}/langnames.js"></script>
-        <script type="text/javascript" src="{$target}fuse.js"></script>
+        <script type="text/javascript" src="{$target}fuzzysort.js"></script>
         <script type="text/javascript" src="{$target}paginathing.js"></script>
         <meta name="viewport" content="width=device-width,initial-scale=1"/>
     </head>
diff --git a/help3xsl/paginathing.js b/help3xsl/paginathing.js
index 3bb88f31d..2f3636022 100644
--- a/help3xsl/paginathing.js
+++ b/help3xsl/paginathing.js
@@ -75,7 +75,7 @@ var Paginator = function(element) {
     var startPage = 1;
     var currentPage = 1;
     var pageDivision = 0;
-    var totalItems = el.getElementsByClassName('fuseshown').length;
+    var totalItems = el.children.length;
     var limitPagination = options.limitPagination;
     pageDivision = Math.ceil(totalItems / options.perPage);
     // let's not display pagination leading nowhere
@@ -187,7 +187,7 @@ var Paginator = function(element) {
         var startAt = page === 1 ? 0 : (page - 1) * options.perPage;
         var endAt = page * options.perPage;
 
-        var domLi = el.getElementsByClassName("fuseshown");
+        var domLi = el.children;
 
         for (var i = 0, len = domLi.length; i < len; i++) {
             var item = domLi[i];
diff --git a/source/text/shared/help/browserhelp.xhp b/source/text/shared/help/browserhelp.xhp
index 0f29667c1..d5948206b 100644
--- a/source/text/shared/help/browserhelp.xhp
+++ b/source/text/shared/help/browserhelp.xhp
@@ -19,7 +19,7 @@
     <paragraph role="paragraph" id="par_id491525733955136" xml-lang="en-US"><variable id="module">Module</variable></paragraph>
     <paragraph role="paragraph" id="par_id531525734031068" xml-lang="en-US"><variable id="language">Language</variable></paragraph>
     <paragraph role="paragraph" id="par_id991525734084608" xml-lang="en-US"><variable id="contents">Contents</variable></paragraph>
-    <paragraph role="paragraph" id="par_id601525734140935" xml-lang="en-US"><variable id="index">Index</variable></paragraph>
+    <paragraph role="paragraph" id="par_id601525734140935" xml-lang="en-US"><variable id="index">Index of chosen module</variable></paragraph>
     <paragraph role="paragraph" id="par_id191525734190260" xml-lang="en-US"><variable id="donate">If this page has been helpful, you can support us!</variable></paragraph>
     <paragraph role="paragraph" id="par_id881525734289794" xml-lang="en-US"><variable id="LibreOfficeHelp">%PRODUCTNAME %PRODUCTVERSION Help</variable></paragraph>
     <paragraph role="paragraph" id="par_id421525736799965" xml-lang="en-US"><variable id="copyclip">Click on text to copy to clipboard</variable></paragraph>


More information about the Libreoffice-commits mailing list