diff --git a/docs/getting-started.md b/docs/getting-started.md
index 2fac05047..26d49104d 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -267,6 +267,10 @@ Furthermore, if `repo_url` points to a GitHub, BitBucket or GitLab repository,
the respective service logo will be shown next to the name of the repository.
Additionally, for GitHub, the number of stars and forks is shown.
+If the repository is hosted in a private environment, the service logo can be
+set explicitly by setting `extra.repo_icon` to `github`, `gitlab` or
+`bitbucket`.
+
!!! warning "Why is there an edit button at the top of every article?"
If the `repo_url` is set to a GitHub or BitBucket repository, and the
@@ -382,6 +386,7 @@ macro `t`:
"search.result.none": "No matching documents",
"search.result.one": "1 matching document",
"search.result.other": "# matching documents",
+ "search.tokenizer": "[\s\-]+",
"source.link.title": "Go to repository",
"toc.title": "Table of contents"
}[key] }}{% endmacro %}
@@ -398,6 +403,8 @@ section on [overriding partials][18] and the general guide on
#### Site search
+##### Language
+
Site search is implemented using [lunr.js][21], which includes stemmers for the
English language by default, while stemmers for other languages are included
with [lunr-languages][22], both of which are integrated with this theme. Support
diff --git a/material/assets/javascripts/application-9d8d07445e.js b/material/assets/javascripts/application-1d6ee6ee6c.js
similarity index 60%
rename from material/assets/javascripts/application-9d8d07445e.js
rename to material/assets/javascripts/application-1d6ee6ee6c.js
index 6be81b1a4..1cb350529 100644
--- a/material/assets/javascripts/application-9d8d07445e.js
+++ b/material/assets/javascripts/application-1d6ee6ee6c.js
@@ -1 +1 @@
-window.app=function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=35)}([function(e,t,n){"use strict";var r=n(23)("wks"),i=n(14),o=n(1).Symbol,a="function"==typeof o;(e.exports=function(e){return r[e]||(r[e]=a&&o[e]||(a?o:i)("Symbol."+e))}).store=r},function(e,t,n){"use strict";var r=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(e,t,n){"use strict";var r=n(10),i=n(25);e.exports=n(5)?function(e,t,n){return r.f(e,t,i(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";var r=n(11);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";var r=n(1),i=n(2),o=n(6),a=n(14)("src"),s=Function.toString,c=(""+s).split("toString");n(7).inspectSource=function(e){return s.call(e)},(e.exports=function(e,t,n,s){var u="function"==typeof n;u&&(o(n,"name")||i(n,"name",t)),e[t]!==n&&(u&&(o(n,a)||i(n,a,e[t]?""+e[t]:c.join(String(t)))),e===r?e[t]=n:s?e[t]?e[t]=n:i(e,t,n):(delete e[t],i(e,t,n)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[a]||s.call(this)})},function(e,t,n){"use strict";e.exports=!n(24)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t,n){"use strict";var r=e.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(e,t,n){"use strict";e.exports={}},function(e,t,n){"use strict";var r={}.toString;e.exports=function(e){return r.call(e).slice(8,-1)}},function(e,t,n){"use strict";var r=n(3),i=n(38),o=n(39),a=Object.defineProperty;t.f=n(5)?Object.defineProperty:function(e,t,n){if(r(e),t=o(t,!0),r(n),i)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};e.exports=function(e){return"object"===(void 0===e?"undefined":r(e))?null!==e:"function"==typeof e}},function(e,t,n){"use strict";var r=n(18);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){"use strict";var r=n(9),i=n(0)("toStringTag"),o="Arguments"==r(function(){return arguments}()),a=function(e,t){try{return e[t]}catch(e){}};e.exports=function(e){var t,n,s;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=a(t=Object(e),i))?n:o?r(t):"Object"==(s=r(t))&&"function"==typeof t.callee?"Arguments":s}},function(e,t,n){"use strict";var r=0,i=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+i).toString(36))}},function(e,t,n){"use strict";var r=n(11),i=n(1).document,o=r(i)&&r(i.createElement);e.exports=function(e){return o?i.createElement(e):{}}},function(e,t,n){"use strict";var r=Math.ceil,i=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?i:r)(e)}},function(e,t,n){"use strict";e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){"use strict";e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){"use strict";var r=n(47),i=n(17);e.exports=function(e){return r(i(e))}},function(e,t,n){"use strict";var r=n(23)("keys"),i=n(14);e.exports=function(e){return r[e]||(r[e]=i(e))}},function(e,t,n){"use strict";var r=n(10).f,i=n(6),o=n(0)("toStringTag");e.exports=function(e,t,n){e&&!i(e=n?e:e.prototype,o)&&r(e,o,{configurable:!0,value:t})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={createElement:function(e,t){var n=document.createElement(e);t&&Array.prototype.forEach.call(Object.keys(t),function(e){n.setAttribute(e,t[e])});for(var r=arguments.length,i=Array(r>2?r-2:0),o=2;o0?i(r(e),9007199254740991):0}},function(e,t,n){"use strict";e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){"use strict";e.exports=n(1).document&&document.documentElement},function(e,t,n){"use strict";var r,i,o,a=n(12),s=n(63),c=n(31),u=n(15),l=n(1),f=l.process,h=l.setImmediate,d=l.clearImmediate,p=l.MessageChannel,m=0,y={},v=function(){var e=+this;if(y.hasOwnProperty(e)){var t=y[e];delete y[e],t()}},g=function(e){v.call(e.data)};h&&d||(h=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return y[++m]=function(){s("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete y[e]},"process"==n(9)(f)?r=function(e){f.nextTick(a(v,e,1))}:p?(i=new p,o=i.port2,i.port1.onmessage=g,r=a(o.postMessage,o,1)):l.addEventListener&&"function"==typeof postMessage&&!l.importScripts?(r=function(e){l.postMessage(e+"","*")},l.addEventListener("message",g,!1)):r="onreadystatechange"in u("script")?function(e){c.appendChild(u("script")).onreadystatechange=function(){c.removeChild(this),v.call(e)}}:function(e){setTimeout(a(v,e,1),0)}),e.exports={set:h,clear:d}},function(e,t){(function(t){e.exports=t}).call(t,{})},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var n=0;n=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){"use strict";var r=n(16),i=n(17);e.exports=function(e){return function(t,n){var o,a,s=String(i(t)),c=r(n),u=s.length;return c<0||c>=u?e?"":void 0:(o=s.charCodeAt(c),o<55296||o>56319||c+1===u||(a=s.charCodeAt(c+1))<56320||a>57343?e?s.charAt(c):o:e?s.slice(c,c+2):a-56320+(o-55296<<10)+65536)}}},function(e,t,n){"use strict";var r=n(43),i=n(25),o=n(21),a={};n(2)(a,n(0)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:i(1,n)}),o(e,t+" Iterator")}},function(e,t,n){"use strict";var r=n(3),i=n(44),o=n(30),a=n(20)("IE_PROTO"),s=function(){},c=function(){var e,t=n(15)("iframe"),r=o.length;for(t.style.display="none",n(31).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write("
+
{% set languages = lang.t("search.languages").split(",") %}
{% if languages | length and languages[0] != "" %}
{% set path = base_url + "/assets/javascripts/lunr" %}
diff --git a/material/partials/language.html b/material/partials/language.html
index 106b380e0..24ca01861 100644
--- a/material/partials/language.html
+++ b/material/partials/language.html
@@ -11,7 +11,7 @@
"search.result.none": "No matching documents",
"search.result.one": "1 matching document",
"search.result.other": "# matching documents",
- "search.tokenizer": "",
+ "search.tokenizer": "[\s\-]+",
"source.link.title": "Go to repository",
"toc.title": "Table of contents"
}[key] }}{% endmacro %}
diff --git a/src/assets/javascripts/components/Material/Search/Result.jsx b/src/assets/javascripts/components/Material/Search/Result.jsx
index 2d2df03fd..c2c108561 100644
--- a/src/assets/javascripts/components/Material/Search/Result.jsx
+++ b/src/assets/javascripts/components/Material/Search/Result.jsx
@@ -23,6 +23,30 @@
import escape from "escape-string-regexp"
import lunr from "expose-loader?lunr!lunr"
+/* ----------------------------------------------------------------------------
+ * Functions
+ * ------------------------------------------------------------------------- */
+
+/**
+ * Truncate a string after the given number of character
+ *
+ * This is not a reasonable approach, since the summaries kind of suck. It
+ * would be better to create something more intelligent, highlighting the
+ * search occurrences and making a better summary out of it.
+ *
+ * @param {string} string - String to be truncated
+ * @param {number} n - Number of characters
+ * @return {string} Truncated string
+ */
+const truncate = (string, n) => {
+ let i = n
+ if (string.length > i) {
+ while (string[i] !== " " && --i > 0);
+ return `${string.substring(0, i)}...`
+ }
+ return string
+}
+
/* ----------------------------------------------------------------------------
* Class
* ------------------------------------------------------------------------- */
@@ -42,6 +66,7 @@ export default class Result {
* @property {Array} lang_ - Search languages
* @property {Object} message_ - Search result messages
* @property {Object} index_ - Search index
+ * @property {Array} stack_ - Search result stack
* @property {string} value_ - Last input value
*
* @param {(string|HTMLElement)} el - Selector or HTML element
@@ -81,26 +106,6 @@ export default class Result {
.map(lang => lang.trim())
}
- /**
- * Truncate a string after the given number of character
- *
- * This is not a reasonable approach, since the summaries kind of suck. It
- * would be better to create something more intelligent, highlighting the
- * search occurrences and making a better summary out of it.
- *
- * @param {string} string - String to be truncated
- * @param {number} n - Number of characters
- * @return {string} Truncated string
- */
- truncate_(string, n) {
- let i = n
- if (string.length > i) {
- while (string[i] !== " " && --i > 0);
- return `${string.substring(0, i)}...`
- }
- return string
- }
-
/**
* Update search results
*
@@ -147,7 +152,8 @@ export default class Result {
const docs = this.docs_,
lang = this.lang_
- /* Create index */
+ /* Create stack and index */
+ this.stack_ = []
this.index_ = lunr(function() {
/* Remove stemmer, as it cripples search experience */
@@ -157,7 +163,7 @@ export default class Result {
lunr.stopWordFilter
)
- /* Set up stemmers for search languages */
+ /* Set up alternate search languages */
if (lang.length === 1) {
this.use(lunr[lang[0]])
} else if (lang.length > 1) {
@@ -172,6 +178,16 @@ export default class Result {
/* Index documents */
docs.forEach(doc => this.add(doc))
})
+
+ /* Register event handler for lazy rendering */
+ const container = this.el_.parentNode
+ if (!(container instanceof HTMLElement))
+ throw new ReferenceError
+ container.addEventListener("scroll", () => {
+ while (this.stack_.length && container.scrollTop +
+ container.offsetHeight >= container.scrollHeight - 16)
+ this.stack_.splice(0, 10).forEach(render => render())
+ })
}
/* eslint-enable no-invalid-this */
@@ -208,7 +224,7 @@ export default class Result {
/* Append trailing wildcard to all terms for prefix querying */
.query(query => {
- this.value_.split(" ")
+ this.value_.toLowerCase().split(" ")
.filter(Boolean)
.forEach(term => {
query.term(term, { wildcard: lunr.Query.wildcard.TRAILING })
@@ -236,12 +252,13 @@ export default class Result {
const highlight = (_, separator, token) =>
`${separator}${token}`
- /* Render results */
+ /* Reset stack and render results */
+ this.stack_ = []
result.forEach((items, ref) => {
const doc = this.docs_.get(ref)
- /* Append search result */
- this.list_.appendChild(
+ /* Render article */
+ const article = (
@@ -256,29 +273,44 @@ export default class Result {
: {}}
- {items.map(item => {
- const section = this.docs_.get(item.ref)
- return (
-
-
-
- {{ __html: section.title.replace(match, highlight) }}
-
- {section.text.length ?
-
- {{ __html: this.truncate_(
- section.text.replace(match, highlight), 400)
- }}
-
: {}}
-
-
- )
- })}
)
+
+ /* Render sections for article */
+ const sections = items.map(item => {
+ return () => {
+ const section = this.docs_.get(item.ref)
+ article.appendChild(
+
+
+
+ {{ __html: section.title.replace(match, highlight) }}
+
+ {section.text.length ?
+
+ {{ __html: truncate(
+ section.text.replace(match, highlight), 400)
+ }}
+
: {}}
+
+
+ )
+ }
+ })
+
+ /* Push articles and section renderers onto stack */
+ this.stack_.push(() => this.list_.appendChild(article), ...sections)
})
+ /* Gradually add results as long as the height of the container grows */
+ const container = this.el_.parentNode
+ if (!(container instanceof HTMLElement))
+ throw new ReferenceError
+ while (this.stack_.length &&
+ container.offsetHeight >= container.scrollHeight - 16)
+ (this.stack_.shift())()
+
/* Bind click handlers for anchors */
const anchors = this.list_.querySelectorAll("[data-md-rel=anchor]")
Array.prototype.forEach.call(anchors, anchor => {
diff --git a/src/partials/language.html b/src/partials/language.html
index 5eb1b627a..c5d6add1b 100644
--- a/src/partials/language.html
+++ b/src/partials/language.html
@@ -34,7 +34,7 @@
"search.result.none": "No matching documents",
"search.result.one": "1 matching document",
"search.result.other": "# matching documents",
- "search.tokenizer": "",
+ "search.tokenizer": "[\s\-]+",
"source.link.title": "Go to repository",
"toc.title": "Table of contents"
}[key] }}{% endmacro %}