From dcc2c5f4049f17d02615b37c685b0a3eccfacca3 Mon Sep 17 00:00:00 2001 From: MAGICCC Date: Sat, 2 Oct 2021 20:01:42 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20mailcow/?= =?UTF-8?q?mailcow-dockerized-docs@d60802604f58b3c5adc88691e24e0775e247232?= =?UTF-8?q?9=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 36 +-- assets/javascripts/bundle.4fc53ad4.min.js | 29 -- assets/javascripts/bundle.c7d1b464.min.js | 29 ++ ....min.js.map => bundle.c7d1b464.min.js.map} | 6 +- ...f8263e09.min.js => search.e99e714c.min.js} | 6 +- ....min.js.map => search.e99e714c.min.js.map} | 2 +- assets/stylesheets/main.1c75ef36.min.css | 2 + assets/stylesheets/main.1c75ef36.min.css.map | 1 + assets/stylesheets/main.8b42a75e.min.css | 2 - assets/stylesheets/main.8b42a75e.min.css.map | 1 - b_n_r_accidental_deletion/index.html | 36 +-- b_n_r_backup/index.html | 36 +-- b_n_r_restore/index.html | 36 +-- client/client-android/index.html | 36 +-- client/client-apple/index.html | 36 +-- client/client-emclient/index.html | 36 +-- client/client-kontact/index.html | 36 +-- client/client-manual/index.html | 36 +-- client/client-outlook/index.html | 36 +-- client/client-thunderbird/index.html | 36 +-- client/client-windows/index.html | 36 +-- client/client-windowsphone/index.html | 36 +-- client/index.html | 36 +-- debug-admin_login_sogo/index.html | 36 +-- debug-asan_rspamd/index.html | 36 +-- debug-attach_service/index.html | 36 +-- debug-common_problems/index.html | 36 +-- debug-logs/index.html | 36 +-- debug-mysql_upgrade/index.html | 36 +-- debug-reset_pw/index.html | 36 +-- debug-reset_tls/index.html | 36 +-- debug-rm_volumes/index.html | 36 +-- debug/index.html | 36 +-- firststeps-disable_ipv6/index.html | 36 +-- firststeps-dmarc_reporting/index.html | 36 +-- firststeps-ip_bindings/index.html | 36 +-- firststeps-local_mta/index.html | 36 +-- firststeps-logging/index.html | 36 +-- firststeps-rp/index.html | 36 +-- firststeps-rspamd_ui/index.html | 36 +-- firststeps-snat/index.html | 36 +-- firststeps-ssl/index.html | 36 +-- firststeps-sync_jobs_migration/index.html | 36 +-- i_u_m_deinstall/index.html | 36 +-- i_u_m_install/index.html | 36 +-- i_u_m_migration/index.html | 36 +-- i_u_m_update/index.html | 36 +-- index.html | 36 +-- model-acl/index.html | 36 +-- model-passwd/index.html | 36 +-- model-sender_rcv/index.html | 36 +-- prerequisite-dns/index.html | 36 +-- prerequisite-system/index.html | 36 +-- restrictions_ip_accss/index.html | 36 +-- search/search_index.json | 2 +- sitemap.xml.gz | Bin 1045 -> 1045 bytes third_party-borgmatic/index.html | 36 +-- third_party-exchange_onprem/index.html | 36 +-- third_party-gitea/index.html | 36 +-- third_party-gogs/index.html | 40 +-- third_party-mailman3/index.html | 303 +++++++++--------- third_party-mailpiler_integration/index.html | 40 +-- third_party-nextcloud/index.html | 36 +-- third_party-portainer/index.html | 36 +-- third_party-roundcube/index.html | 36 +-- third_party-thunderbird/index.html | 51 ++- u_e-80_to_443/index.html | 36 +-- u_e-autodiscover_config/index.html | 36 +-- u_e-backup_restore-maildir/index.html | 36 +-- u_e-backup_restore-mysql/index.html | 36 +-- u_e-docker-cust_dockerfiles/index.html | 36 +-- u_e-docker-dc_bash_compl/index.html | 36 +-- u_e-dovecot-any_acl/index.html | 36 +-- u_e-dovecot-catchall_vacation/index.html | 36 +-- u_e-dovecot-expunge/index.html | 36 +-- u_e-dovecot-extra_conf/index.html | 36 +-- u_e-dovecot-fts/index.html | 36 +-- u_e-dovecot-idle_interval/index.html | 36 +-- u_e-dovecot-mail-crypt/index.html | 36 +-- u_e-dovecot-more/index.html | 36 +-- u_e-dovecot-public_folder/index.html | 36 +-- u_e-dovecot-static_master/index.html | 36 +-- u_e-dovecot-vmail-volume/index.html | 36 +-- u_e-fido2/index.html | 36 +-- u_e-mailcow_ui-bl_wl/index.html | 36 +-- u_e-mailcow_ui-config/index.html | 36 +-- u_e-mailcow_ui-css/index.html | 36 +-- u_e-mailcow_ui-pushover/index.html | 36 +-- u_e-mailcow_ui-spamalias/index.html | 36 +-- u_e-mailcow_ui-spamfilter/index.html | 36 +-- u_e-mailcow_ui-tagging/index.html | 36 +-- u_e-mailcow_ui-tfa/index.html | 36 +-- u_e-nginx/index.html | 36 +-- u_e-postfix-attachment_size/index.html | 36 +-- u_e-postfix-custom_transport/index.html | 36 +-- .../index.html | 36 +-- u_e-postfix-extra_cf/index.html | 36 +-- u_e-postfix-pflogsumm/index.html | 36 +-- u_e-postfix-postscreen_whitelist/index.html | 36 +-- u_e-postfix-relayhost/index.html | 36 +-- u_e-postfix-trust_networks/index.html | 36 +-- u_e-redis/index.html | 36 +-- u_e-reeanble-weak-protocols/index.html | 36 +-- u_e-rspamd/index.html | 36 +-- u_e-sogo/index.html | 36 +-- u_e-unbound-fwd/index.html | 36 +-- u_e-update-hooks/index.html | 36 +-- u_e-webmail-site/index.html | 36 +-- u_e-why_unbound/index.html | 36 +-- 109 files changed, 1944 insertions(+), 1954 deletions(-) delete mode 100644 assets/javascripts/bundle.4fc53ad4.min.js create mode 100644 assets/javascripts/bundle.c7d1b464.min.js rename assets/javascripts/{bundle.4fc53ad4.min.js.map => bundle.c7d1b464.min.js.map} (66%) rename assets/javascripts/workers/{search.f8263e09.min.js => search.e99e714c.min.js} (97%) rename assets/javascripts/workers/{search.f8263e09.min.js.map => search.e99e714c.min.js.map} (99%) create mode 100644 assets/stylesheets/main.1c75ef36.min.css create mode 100644 assets/stylesheets/main.1c75ef36.min.css.map delete mode 100644 assets/stylesheets/main.8b42a75e.min.css delete mode 100644 assets/stylesheets/main.8b42a75e.min.css.map diff --git a/404.html b/404.html index 8706d0e48..4d6c54a67 100644 --- a/404.html +++ b/404.html @@ -10,7 +10,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -2056,6 +2056,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2121,20 +2135,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2216,10 +2216,10 @@
    - + - + diff --git a/assets/javascripts/bundle.4fc53ad4.min.js b/assets/javascripts/bundle.4fc53ad4.min.js deleted file mode 100644 index 08ccbada5..000000000 --- a/assets/javascripts/bundle.4fc53ad4.min.js +++ /dev/null @@ -1,29 +0,0 @@ -(()=>{var ea=Object.create;var St=Object.defineProperty;var ta=Object.getOwnPropertyDescriptor;var ra=Object.getOwnPropertyNames,wt=Object.getOwnPropertySymbols,oa=Object.getPrototypeOf,ar=Object.prototype.hasOwnProperty,Kr=Object.prototype.propertyIsEnumerable;var Br=(e,t,r)=>t in e?St(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))ar.call(t,r)&&Br(e,r,t[r]);if(wt)for(var r of wt(t))Kr.call(t,r)&&Br(e,r,t[r]);return e};var na=e=>St(e,"__esModule",{value:!0});var ss=typeof require!="undefined"?require:e=>{throw new Error('Dynamic require of "'+e+'" is not supported')};var Jr=(e,t)=>{var r={};for(var o in e)ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&wt)for(var o of wt(e))t.indexOf(o)<0&&Kr.call(e,o)&&(r[o]=e[o]);return r};var Et=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var ia=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ra(t))!ar.call(e,o)&&o!=="default"&&St(e,o,{get:()=>t[o],enumerable:!(r=ta(t,o))||r.enumerable});return e},Ke=e=>ia(na(St(e!=null?ea(oa(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var Gr=Et((sr,Yr)=>{(function(e,t){typeof sr=="object"&&typeof Yr!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(sr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(w){return!!(w&&w!==document&&w.nodeName!=="HTML"&&w.nodeName!=="BODY"&&"classList"in w&&"contains"in w.classList)}function c(w){var We=w.type,Oe=w.tagName;return!!(Oe==="INPUT"&&a[We]&&!w.readOnly||Oe==="TEXTAREA"&&!w.readOnly||w.isContentEditable)}function l(w){w.classList.contains("focus-visible")||(w.classList.add("focus-visible"),w.setAttribute("data-focus-visible-added",""))}function p(w){!w.hasAttribute("data-focus-visible-added")||(w.classList.remove("focus-visible"),w.removeAttribute("data-focus-visible-added"))}function m(w){w.metaKey||w.altKey||w.ctrlKey||(s(r.activeElement)&&l(r.activeElement),o=!0)}function u(w){o=!1}function b(w){!s(w.target)||(o||c(w.target))&&l(w.target)}function v(w){!s(w.target)||(w.target.classList.contains("focus-visible")||w.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),p(w.target))}function d(w){document.visibilityState==="hidden"&&(n&&(o=!0),W())}function W(){document.addEventListener("mousemove",j),document.addEventListener("mousedown",j),document.addEventListener("mouseup",j),document.addEventListener("pointermove",j),document.addEventListener("pointerdown",j),document.addEventListener("pointerup",j),document.addEventListener("touchmove",j),document.addEventListener("touchstart",j),document.addEventListener("touchend",j)}function z(){document.removeEventListener("mousemove",j),document.removeEventListener("mousedown",j),document.removeEventListener("mouseup",j),document.removeEventListener("pointermove",j),document.removeEventListener("pointerdown",j),document.removeEventListener("pointerup",j),document.removeEventListener("touchmove",j),document.removeEventListener("touchstart",j),document.removeEventListener("touchend",j)}function j(w){w.target.nodeName&&w.target.nodeName.toLowerCase()==="html"||(o=!1,z())}document.addEventListener("keydown",m,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",d,!0),W(),r.addEventListener("focus",b,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var go=Et((ls,_t)=>{/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */var Xr,Zr,eo,to,ro,oo,no,io,ao,Tt,cr,so,co,lo,Be,po,uo,fo,mo,ho,bo,vo,xo,Ot;(function(e){var t=typeof global=="object"?global:typeof self=="object"?self:typeof this=="object"?this:{};typeof define=="function"&&define.amd?define("tslib",["exports"],function(o){e(r(t,r(o)))}):typeof _t=="object"&&typeof _t.exports=="object"?e(r(t,r(_t.exports))):e(r(t));function r(o,n){return o!==t&&(typeof Object.create=="function"?Object.defineProperty(o,"__esModule",{value:!0}):o.__esModule=!0),function(i,a){return o[i]=n?n(i,a):a}}})(function(e){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(o,n){o.__proto__=n}||function(o,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(o[i]=n[i])};Xr=function(o,n){if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");t(o,n);function i(){this.constructor=o}o.prototype=n===null?Object.create(n):(i.prototype=n.prototype,new i)},Zr=Object.assign||function(o){for(var n,i=1,a=arguments.length;i=0;p--)(l=o[p])&&(c=(s<3?l(c):s>3?l(n,i,c):l(n,i))||c);return s>3&&c&&Object.defineProperty(n,i,c),c},ro=function(o,n){return function(i,a){n(i,a,o)}},oo=function(o,n){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(o,n)},no=function(o,n,i,a){function s(c){return c instanceof i?c:new i(function(l){l(c)})}return new(i||(i=Promise))(function(c,l){function p(b){try{u(a.next(b))}catch(v){l(v)}}function m(b){try{u(a.throw(b))}catch(v){l(v)}}function u(b){b.done?c(b.value):s(b.value).then(p,m)}u((a=a.apply(o,n||[])).next())})},io=function(o,n){var i={label:0,sent:function(){if(c[0]&1)throw c[1];return c[1]},trys:[],ops:[]},a,s,c,l;return l={next:p(0),throw:p(1),return:p(2)},typeof Symbol=="function"&&(l[Symbol.iterator]=function(){return this}),l;function p(u){return function(b){return m([u,b])}}function m(u){if(a)throw new TypeError("Generator is already executing.");for(;i;)try{if(a=1,s&&(c=u[0]&2?s.return:u[0]?s.throw||((c=s.return)&&c.call(s),0):s.next)&&!(c=c.call(s,u[1])).done)return c;switch(s=0,c&&(u=[u[0]&2,c.value]),u[0]){case 0:case 1:c=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,s=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(c=i.trys,!(c=c.length>0&&c[c.length-1])&&(u[0]===6||u[0]===2)){i=0;continue}if(u[0]===3&&(!c||u[1]>c[0]&&u[1]=o.length&&(o=void 0),{value:o&&o[a++],done:!o}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")},cr=function(o,n){var i=typeof Symbol=="function"&&o[Symbol.iterator];if(!i)return o;var a=i.call(o),s,c=[],l;try{for(;(n===void 0||n-- >0)&&!(s=a.next()).done;)c.push(s.value)}catch(p){l={error:p}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(l)throw l.error}}return c},so=function(){for(var o=[],n=0;n1||p(d,W)})})}function p(d,W){try{m(a[d](W))}catch(z){v(c[0][3],z)}}function m(d){d.value instanceof Be?Promise.resolve(d.value.v).then(u,b):v(c[0][2],d)}function u(d){p("next",d)}function b(d){p("throw",d)}function v(d,W){d(W),c.shift(),c.length&&p(c[0][0],c[0][1])}},uo=function(o){var n,i;return n={},a("next"),a("throw",function(s){throw s}),a("return"),n[Symbol.iterator]=function(){return this},n;function a(s,c){n[s]=o[s]?function(l){return(i=!i)?{value:Be(o[s](l)),done:s==="return"}:c?c(l):l}:c}},fo=function(o){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n=o[Symbol.asyncIterator],i;return n?n.call(o):(o=typeof Tt=="function"?Tt(o):o[Symbol.iterator](),i={},a("next"),a("throw"),a("return"),i[Symbol.asyncIterator]=function(){return this},i);function a(c){i[c]=o[c]&&function(l){return new Promise(function(p,m){l=o[c](l),s(p,m,l.done,l.value)})}}function s(c,l,p,m){Promise.resolve(m).then(function(u){c({value:u,done:p})},l)}},mo=function(o,n){return Object.defineProperty?Object.defineProperty(o,"raw",{value:n}):o.raw=n,o};var r=Object.create?function(o,n){Object.defineProperty(o,"default",{enumerable:!0,value:n})}:function(o,n){o.default=n};ho=function(o){if(o&&o.__esModule)return o;var n={};if(o!=null)for(var i in o)i!=="default"&&Object.prototype.hasOwnProperty.call(o,i)&&Ot(n,o,i);return r(n,o),n},bo=function(o){return o&&o.__esModule?o:{default:o}},vo=function(o,n){if(!n.has(o))throw new TypeError("attempted to get private field on non-instance");return n.get(o)},xo=function(o,n,i){if(!n.has(o))throw new TypeError("attempted to set private field on non-instance");return n.set(o,i),i},e("__extends",Xr),e("__assign",Zr),e("__rest",eo),e("__decorate",to),e("__param",ro),e("__metadata",oo),e("__awaiter",no),e("__generator",io),e("__exportStar",ao),e("__createBinding",Ot),e("__values",Tt),e("__read",cr),e("__spread",so),e("__spreadArrays",co),e("__spreadArray",lo),e("__await",Be),e("__asyncGenerator",po),e("__asyncDelegator",uo),e("__asyncValues",fo),e("__makeTemplateObject",mo),e("__importStar",ho),e("__importDefault",bo),e("__classPrivateFieldGet",vo),e("__classPrivateFieldSet",xo)})});var Fr=Et((xt,Rr)=>{/*! - * clipboard.js v2.0.8 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */(function(t,r){typeof xt=="object"&&typeof Rr=="object"?Rr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof xt=="object"?xt.ClipboardJS=r():t.ClipboardJS=r()})(xt,function(){return function(){var e={134:function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var a=i(279),s=i.n(a),c=i(370),l=i.n(c),p=i(817),m=i.n(p);function u(O){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?u=function(h){return typeof h}:u=function(h){return h&&typeof Symbol=="function"&&h.constructor===Symbol&&h!==Symbol.prototype?"symbol":typeof h},u(O)}function b(O,x){if(!(O instanceof x))throw new TypeError("Cannot call a class as a function")}function v(O,x){for(var h=0;h0&&arguments[0]!==void 0?arguments[0]:{};this.action=h.action,this.container=h.container,this.emitter=h.emitter,this.target=h.target,this.text=h.text,this.trigger=h.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"createFakeElement",value:function(){var h=document.documentElement.getAttribute("dir")==="rtl";this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[h?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return this.fakeElem.style.top="".concat(k,"px"),this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.fakeElem}},{key:"selectFake",value:function(){var h=this,k=this.createFakeElement();this.fakeHandlerCallback=function(){return h.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.container.appendChild(k),this.selectedText=m()(k),this.copyText(),this.removeFake()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=m()(this.target),this.copyText()}},{key:"copyText",value:function(){var h;try{h=document.execCommand(this.action)}catch(k){h=!1}this.handleResult(h)}},{key:"handleResult",value:function(h){this.emitter.emit(h?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var h=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"copy";if(this._action=h,this._action!=="copy"&&this._action!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(h){if(h!==void 0)if(h&&u(h)==="object"&&h.nodeType===1){if(this.action==="copy"&&h.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(this.action==="cut"&&(h.hasAttribute("readonly")||h.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`);this._target=h}else throw new Error('Invalid "target" value, use a valid Element')},get:function(){return this._target}}]),O}(),z=W;function j(O){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?j=function(h){return typeof h}:j=function(h){return h&&typeof Symbol=="function"&&h.constructor===Symbol&&h!==Symbol.prototype?"symbol":typeof h},j(O)}function w(O,x){if(!(O instanceof x))throw new TypeError("Cannot call a class as a function")}function We(O,x){for(var h=0;h0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof P.action=="function"?P.action:this.defaultAction,this.target=typeof P.target=="function"?P.target:this.defaultTarget,this.text=typeof P.text=="function"?P.text:this.defaultText,this.container=j(P.container)==="object"?P.container:document.body}},{key:"listenClick",value:function(P){var ee=this;this.listener=l()(P,"click",function(lt){return ee.onClick(lt)})}},{key:"onClick",value:function(P){var ee=P.delegateTarget||P.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new z({action:this.action(ee),target:this.target(ee),text:this.text(ee),container:this.container,trigger:ee,emitter:this})}},{key:"defaultAction",value:function(P){return ir("action",P)}},{key:"defaultTarget",value:function(P){var ee=ir("target",P);if(ee)return document.querySelector(ee)}},{key:"defaultText",value:function(P){return ir("text",P)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var P=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],ee=typeof P=="string"?[P]:P,lt=!!document.queryCommandSupported;return ee.forEach(function(Zi){lt=lt&&!!document.queryCommandSupported(Zi)}),lt}}]),h}(s()),Xi=Gi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,c){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(c))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(p,m,u,b,v){var d=l.apply(this,arguments);return p.addEventListener(u,d,v),{destroy:function(){p.removeEventListener(u,d,v)}}}function c(p,m,u,b,v){return typeof p.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof p=="string"&&(p=document.querySelectorAll(p)),Array.prototype.map.call(p,function(d){return s(d,m,u,b,v)}))}function l(p,m,u,b){return function(v){v.delegateTarget=a(v.target,m),v.delegateTarget&&b.call(p,v)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function c(u,b,v){if(!u&&!b&&!v)throw new Error("Missing required arguments");if(!a.string(b))throw new TypeError("Second argument must be a String");if(!a.fn(v))throw new TypeError("Third argument must be a Function");if(a.node(u))return l(u,b,v);if(a.nodeList(u))return p(u,b,v);if(a.string(u))return m(u,b,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function l(u,b,v){return u.addEventListener(b,v),{destroy:function(){u.removeEventListener(b,v)}}}function p(u,b,v){return Array.prototype.forEach.call(u,function(d){d.addEventListener(b,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(d){d.removeEventListener(b,v)})}}}function m(u,b,v){return s(document.body,u,b,v)}o.exports=c},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),l=document.createRange();l.selectNodeContents(i),c.removeAllRanges(),c.addRange(l),a=c.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var c=this;function l(){c.off(i,l),a.apply(s,arguments)}return l._=a,this.on(i,l,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),c=0,l=s.length;for(c;c{/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */"use strict";var Qa=/["'&<>]/;gi.exports=Ka;function Ka(e){var t=""+e,r=Qa.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=o.hasError,i=o.isStopped,a=o.observers;return n||i?lr:(a.push(r),new ie(function(){return _e(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new A;return r.source=this,r},t.create=function(r,o){return new jo(r,o)},t}(A);var jo=function(e){G(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:lr},t}(T);var ft={now:function(){return(ft.delegate||Date).now()},delegate:void 0};var mt=function(e){G(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=ft);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,c=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Ge.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){if(n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);r.actions.length===0&&(Ge.cancelAnimationFrame(o),r._scheduled=void 0)},t}(jt);var Io=function(e){G(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0,this._scheduled=void 0;var o=this.actions,n,i=-1;r=r||o.shift();var a=o.length;do if(n=r.execute(r.state,r.delay))break;while(++i=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,c=s===void 0?!0:s;return function(l){var p=null,m=null,u=null,b=0,v=!1,d=!1,W=function(){m==null||m.unsubscribe(),m=null},z=function(){W(),p=u=null,v=d=!1},j=function(){var w=p;z(),w==null||w.unsubscribe()};return g(function(w,We){b++,!d&&!v&&W();var Oe=u=u!=null?u:r();We.add(function(){b--,b===0&&!d&&!v&&(m=Or(j,c))}),Oe.subscribe(We),p||(p=new ut({next:function(Qe){return Oe.next(Qe)},error:function(Qe){d=!0,W(),m=Or(z,n,Qe),Oe.error(Qe)},complete:function(){v=!0,W(),m=Or(z,a),Oe.complete()}}),Se(w).subscribe(p))})(l)}}function Or(e,t){for(var r=[],o=2;ot==="focus"),N(e===De()))}var nn=new T,Ha=Me(()=>R(new ResizeObserver(e=>{for(let t of e)nn.next(t)}))).pipe(M(e=>J.pipe(N(e)).pipe(I(()=>e.disconnect()))),Z(1));function Re(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function Fe(e){return Ha.pipe(L(t=>t.observe(e)),M(t=>nn.pipe(_(({target:r})=>r===e),I(()=>t.unobserve(e)),f(()=>Re(e)))),N(Re(e)))}function an(e){return{x:e.scrollLeft,y:e.scrollTop}}function ja(e){return U(E(e,"scroll"),E(window,"resize")).pipe(f(()=>an(e)),N(an(e)))}function sn(e,t=16){return ja(e).pipe(f(({y:r})=>{let o=Re(e),n=Bt(e);return r>=n.height-o.height-t}),D())}function cn(e){if(e instanceof HTMLInputElement)e.select();else throw new Error("Not implemented")}var Jt={drawer:be("[data-md-toggle=drawer]"),search:be("[data-md-toggle=search]")};function ln(e){return Jt[e].checked}function Ie(e,t){Jt[e].checked!==t&&Jt[e].click()}function Yt(e){let t=Jt[e];return E(t,"change").pipe(f(()=>t.checked),N(t.checked))}function Ra(e){switch(e.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return!0;default:return e.isContentEditable}}function pn(){return E(window,"keydown").pipe(_(e=>!(e.metaKey||e.ctrlKey)),f(e=>({mode:ln("search")?"search":"global",type:e.key,claim(){e.preventDefault(),e.stopPropagation()}})),_(({mode:e})=>{if(e==="global"){let t=De();if(typeof t!="undefined")return!Ra(t)}return!0}),pe())}function Pe(){return new URL(location.href)}function un(e){location.href=e.href}function fn(){return new T}function mn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)mn(e,r)}function F(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="boolean"?o.setAttribute(n,t[n]):t[n]&&o.setAttribute(n,"");for(let n of r)mn(o,n);return o}function dn(e,t){let r=t;if(e.length>r){for(;e[r]!==" "&&--r>0;);return`${e.substring(0,r)}...`}return e}function Gt(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function hn(){return location.hash.substring(1)}function bn(e){let t=F("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return E(window,"hashchange").pipe(f(hn),N(hn()),_(e=>e.length>0),Z(1))}function vn(){return Fa().pipe(f(e=>ae(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function vt(e){let t=matchMedia(e);return Qt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function xn(){return E(window,"beforeprint").pipe(oe(void 0))}function Hr(e,t){return e.pipe(M(r=>r?t():J))}function Xt(e,t={credentials:"same-origin"}){return Se(fetch(`${e}`,t)).pipe(_(r=>r.status===200))}function Ee(e,t){return Xt(e,t).pipe(M(r=>r.json()),Z(1))}function gn(e,t){let r=new DOMParser;return Xt(e,t).pipe(M(o=>o.text()),f(o=>r.parseFromString(o,"text/xml")),Z(1))}function yn(){return{x:Math.max(0,pageXOffset),y:Math.max(0,pageYOffset)}}function jr({x:e,y:t}){window.scrollTo(e||0,t||0)}function Sn(){return U(E(window,"scroll",{passive:!0}),E(window,"resize",{passive:!0})).pipe(f(yn),N(yn()))}function wn(){return{width:innerWidth,height:innerHeight}}function En(){return E(window,"resize",{passive:!0}).pipe(f(wn),N(wn()))}function Tn(){return K([Sn(),En()]).pipe(f(([e,t])=>({offset:e,size:t})),Z(1))}function Zt(e,{viewport$:t,header$:r}){let o=t.pipe(V("size")),n=K([o,r]).pipe(f(()=>({x:e.offsetLeft,y:e.offsetTop})));return K([r,t,n]).pipe(f(([{height:i},{offset:a,size:s},{x:c,y:l}])=>({offset:{x:a.x-c,y:a.y-l+i},size:s})))}function On(e,{tx$:t}){let r=E(e,"message").pipe(f(({data:o})=>o));return t.pipe(kr(()=>r,{leading:!0,trailing:!0}),L(o=>e.postMessage(o)),Mr(r),pe())}var Ia=be("#__config"),it=JSON.parse(Ia.textContent);it.base=`${new URL(it.base,Pe())}`;function se(){return it}function fe(e){return it.features.includes(e)}function Y(e,t){return typeof t!="undefined"?it.translations[e].replace("#",t.toString()):it.translations[e]}function Te(e,t=document){return be(`[data-md-component=${e}]`,t)}function ne(e,t=document){return B(`[data-md-component=${e}]`,t)}var ii=Ke(Fr());function er(e,t=0){e.setAttribute("tabindex",t.toString())}function gt(e){e.removeAttribute("tabindex")}function _n(e,t){e.setAttribute("data-md-state","lock"),e.style.top=`-${t}px`}function Mn(e){let t=-1*parseInt(e.style.top,10);e.removeAttribute("data-md-state"),e.style.top="",t&&window.scrollTo(0,t)}function An(e,t){e.setAttribute("data-md-state",t)}function Ln(e){e.removeAttribute("data-md-state")}function kn(e,t){e.classList.toggle("md-nav__link--active",t)}function Cn(e){e.classList.remove("md-nav__link--active")}function Hn(e,t){e.firstElementChild.innerHTML=t}function jn(e,t){e.setAttribute("data-md-state",t)}function Rn(e){e.removeAttribute("data-md-state")}function Fn(e,t){e.setAttribute("data-md-state",t)}function In(e){e.removeAttribute("data-md-state")}function Pn(e,t){e.setAttribute("data-md-state",t)}function $n(e){e.removeAttribute("data-md-state")}function Wn(e,t){e.placeholder=t}function Un(e){e.placeholder=Y("search.placeholder")}function Vn(e,t){switch(t){case 0:e.textContent=Y("search.result.none");break;case 1:e.textContent=Y("search.result.one");break;default:e.textContent=Y("search.result.other",Gt(t))}}function Ir(e){e.textContent=Y("search.result.placeholder")}function Nn(e,t){e.appendChild(t)}function Dn(e){e.innerHTML=""}function zn(e,t){e.style.top=`${t}px`}function qn(e){e.style.top=""}function Qn(e,t){let r=e.firstElementChild;r.style.height=`${t-2*r.offsetTop}px`}function Kn(e){let t=e.firstElementChild;t.style.height=""}function Bn(e,t){e.lastElementChild.appendChild(t)}function Jn(e,t){e.lastElementChild.setAttribute("data-md-state",t)}function Yn(e,t){e.setAttribute("data-md-state",t)}function Pr(e){e.removeAttribute("data-md-state")}function Gn(e,t){e.setAttribute("data-md-state",t)}function $r(e){e.removeAttribute("data-md-state")}function Xn(e,t){e.style.top=`${t}px`}function Zn(e){e.style.top=""}function ei(e){return F("button",{class:"md-clipboard md-icon",title:Y("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var qe;(function(r){r[r.TEASER=1]="TEASER",r[r.PARENT=2]="PARENT"})(qe||(qe={}));function Wr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(a=>!e.terms[a]).map(a=>[F("del",null,a)," "]).flat().slice(0,-1),i=new URL(e.location);return fe("search.highlight")&&i.searchParams.set("h",Object.entries(e.terms).filter(([,a])=>a).reduce((a,[s])=>`${a} ${s}`.trim(),"")),F("a",{href:`${i}`,class:"md-search-result__link",tabIndex:-1},F("article",{class:["md-search-result__article",...r?["md-search-result__article--document"]:[]].join(" "),"data-md-score":e.score.toFixed(2)},r>0&&F("div",{class:"md-search-result__icon md-icon"}),F("h1",{class:"md-search-result__title"},e.title),o>0&&e.text.length>0&&F("p",{class:"md-search-result__teaser"},dn(e.text,320)),o>0&&n.length>0&&F("p",{class:"md-search-result__terms"},Y("search.result.term.missing"),": ",n)))}function ti(e){let t=e[0].score,r=[...e],o=r.findIndex(l=>!l.location.includes("#")),[n]=r.splice(o,1),i=r.findIndex(l=>l.scoreWr(l,1)),...s.length?[F("details",{class:"md-search-result__more"},F("summary",{tabIndex:-1},s.length>0&&s.length===1?Y("search.result.more.one"):Y("search.result.more.other",s.length)),s.map(l=>Wr(l,1)))]:[]];return F("li",{class:"md-search-result__item"},c)}function ri(e){return F("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>F("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?Gt(r):r)))}function oi(e){return F("div",{class:"md-typeset__scrollwrap"},F("div",{class:"md-typeset__table"},e))}function Pa(e){let t=se(),r=new URL(`../${e.version}/`,t.base);return F("li",{class:"md-version__item"},F("a",{href:r.toString(),class:"md-version__link"},e.title))}function ni(e){let t=se(),[,r]=t.base.match(/([^/]+)\/?$/),o=e.find(({version:n,aliases:i})=>n===r||i.includes(r))||e[0];return F("div",{class:"md-version"},F("button",{class:"md-version__current","aria-label":Y("select.version.title")},o.title),F("ul",{class:"md-version__list"},e.map(Pa)))}var $a=0;function Wa(e,{viewport$:t}){let r=R(e).pipe(M(o=>{let n=o.closest("[data-tabs]");return n instanceof HTMLElement?U(...B("input",n).map(i=>E(i,"change"))):J}));return U(t.pipe(V("size")),r).pipe(f(()=>{let o=Re(e);return{scroll:Bt(e).width>o.width}}),V("scroll"))}function ai(e,t){let r=new T;if(r.pipe(ue(vt("(hover)"))).subscribe(([{scroll:o},n])=>{o&&n?er(e):gt(e)}),ii.default.isSupported()){let o=e.closest("pre");o.id=`__code_${$a++}`,o.insertBefore(ei(o.id),e)}return Wa(e,t).pipe(L(o=>r.next(o)),I(()=>r.complete()),f(o=>$({ref:e},o)))}function Ua(e,{target$:t,print$:r}){return t.pipe(f(o=>o.closest("details:not([open])")),_(o=>e===o),oe({scroll:!0}),Ne(r.pipe(oe({}))))}function si(e,t){let r=new T;return r.subscribe(({scroll:o})=>{e.setAttribute("open",""),o&&e.scrollIntoView()}),Ua(e,t).pipe(L(o=>r.next(o)),I(()=>r.complete()),oe({ref:e}))}var ci=F("table");function li(e){return ze(e,ci),ze(ci,oi(e)),R({ref:e})}function pi(e,{target$:t,viewport$:r,print$:o}){return U(...B("pre > code",e).map(n=>ai(n,{viewport$:r})),...B("table:not([class])",e).map(n=>li(n)),...B("details",e).map(n=>si(n,{target$:t,print$:o})))}function Va(e,{alert$:t}){return t.pipe(M(r=>U(R(!0),R(!1).pipe(Ae(2e3))).pipe(f(o=>({message:r,open:o})))))}function ui(e,t){let r=new T;return r.pipe(Q(X)).subscribe(({message:o,open:n})=>{Hn(e,o),n?jn(e,"open"):Rn(e)}),Va(e,t).pipe(L(o=>r.next(o)),I(()=>r.complete()),f(o=>$({ref:e},o)))}function Na({viewport$:e}){if(!fe("header.autohide"))return R(!1);let t=e.pipe(f(({offset:{y:n}})=>n),ge(2,1),f(([n,i])=>[nMath.abs(i-n.y)>100),f(([,[n]])=>n),D()),o=Yt("search");return K([e,o]).pipe(f(([{offset:n},i])=>n.y>400&&!i),D(),M(n=>n?r:R(!1)),N(!1))}function fi(e,t){return Me(()=>{let r=getComputedStyle(e);return R(r.position==="sticky"||r.position==="-webkit-sticky")}).pipe(ot(Fe(e),Na(t)),f(([r,{height:o},n])=>({height:r?o:0,sticky:r,hidden:n})),D((r,o)=>r.sticky===o.sticky&&r.height===o.height&&r.hidden===o.hidden),Z(1))}function mi(e,{header$:t,main$:r}){let o=new T;return o.pipe(V("active"),ot(t),Q(X)).subscribe(([{active:n},{hidden:i}])=>{n?Fn(e,i?"hidden":"shadow"):In(e)}),r.subscribe(n=>o.next(n)),t.pipe(f(n=>$({ref:e},n)))}function Da(e,{viewport$:t,header$:r}){return Zt(e,{header$:r,viewport$:t}).pipe(f(({offset:{y:o}})=>{let{height:n}=Re(e);return{active:o>=n}}),V("active"))}function di(e,t){let r=new T;r.pipe(Q(X)).subscribe(({active:n})=>{n?Pn(e,"active"):$n(e)});let o=ae("article h1");return typeof o=="undefined"?J:Da(o,t).pipe(L(n=>r.next(n)),I(()=>r.complete()),f(n=>$({ref:e},n)))}function hi(e,{viewport$:t,header$:r}){let o=r.pipe(f(({height:i})=>i),D()),n=o.pipe(M(()=>Fe(e).pipe(f(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),V("bottom"))));return K([o,n,t]).pipe(f(([i,{top:a,bottom:s},{offset:{y:c},size:{height:l}}])=>(l=Math.max(0,l-Math.max(0,a-c,i)-Math.max(0,l+c-s)),{offset:a-i,height:l,active:a-i<=c})),D((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function za(e){let t=localStorage.getItem(__prefix("__palette")),r=JSON.parse(t)||{index:e.findIndex(n=>matchMedia(n.getAttribute("data-md-color-media")).matches)},o=R(...e).pipe(re(n=>E(n,"change").pipe(oe(n))),N(e[Math.max(0,r.index)]),f(n=>({index:e.indexOf(n),color:{scheme:n.getAttribute("data-md-color-scheme"),primary:n.getAttribute("data-md-color-primary"),accent:n.getAttribute("data-md-color-accent")}})),Z(1));return o.subscribe(n=>{localStorage.setItem(__prefix("__palette"),JSON.stringify(n))}),o}function bi(e){let t=new T;t.subscribe(o=>{for(let[n,i]of Object.entries(o.color))typeof i=="string"&&document.body.setAttribute(`data-md-color-${n}`,i);for(let n=0;nt.next(o)),I(()=>t.complete()),f(o=>$({ref:e},o)))}var Ur=Ke(Fr());function vi({alert$:e}){Ur.default.isSupported()&&new A(t=>{new Ur.default("[data-clipboard-target], [data-clipboard-text]").on("success",r=>t.next(r))}).subscribe(()=>e.next(Y("clipboard.copied")))}function qa(e){if(e.length<2)return e;let[t,r]=e.sort((i,a)=>i.length-a.length).map(i=>i.replace(/[^/]+$/,"")),o=0;if(t===r)o=t.length;else for(;t.charCodeAt(o)===r.charCodeAt(o);)o++;let n=se();return e.map(i=>i.replace(t.slice(0,o),n.base))}function xi({document$:e,location$:t,viewport$:r}){let o=se();if(location.protocol==="file:")return;"scrollRestoration"in history&&(history.scrollRestoration="manual",E(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}));let n=ae("link[rel=icon]");typeof n!="undefined"&&(n.href=n.href);let i=gn(new URL("sitemap.xml",o.base)).pipe(f(l=>qa(B("loc",l).map(p=>p.textContent))),M(l=>E(document.body,"click").pipe(_(p=>!p.metaKey&&!p.ctrlKey),M(p=>{if(p.target instanceof Element){let m=p.target.closest("a");if(m&&!m.target){let u=new URL(m.href);if(u.search="",u.hash="",u.pathname!==location.pathname&&l.includes(u.toString()))return p.preventDefault(),R({url:new URL(m.href)})}}return J}))),pe()),a=E(window,"popstate").pipe(_(l=>l.state!==null),f(l=>({url:new URL(location.href),offset:l.state})),pe());U(i,a).pipe(D((l,p)=>l.url.href===p.url.href),f(({url:l})=>l)).subscribe(t);let s=t.pipe(V("pathname"),M(l=>Xt(l.href).pipe(rt(()=>(un(l),J)))),pe());i.pipe(nt(s)).subscribe(({url:l})=>{history.pushState({},"",`${l}`)});let c=new DOMParser;s.pipe(M(l=>l.text()),f(l=>c.parseFromString(l,"text/html"))).subscribe(e),e.pipe(Kt(1)).subscribe(l=>{for(let p of["title","link[rel=canonical]","meta[name=author]","meta[name=description]","[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=logo], .md-logo","[data-md-component=skip]",...fe("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let m=ae(p),u=ae(p,l);typeof m!="undefined"&&typeof u!="undefined"&&ze(m,u)}}),e.pipe(Kt(1),f(()=>Te("container")),M(l=>R(...B("script",l))),yr(l=>{let p=F("script");if(l.src){for(let m of l.getAttributeNames())p.setAttribute(m,l.getAttribute(m));return ze(l,p),new A(m=>{p.onload=()=>m.complete()})}else return p.textContent=l.textContent,ze(l,p),xe})).subscribe(),U(i,a).pipe(nt(e)).subscribe(({url:l,offset:p})=>{l.hash&&!p?bn(l.hash):jr(p||{y:0})}),r.pipe(_r(i),Sr(250),V("offset")).subscribe(({offset:l})=>{history.replaceState(l,"")}),U(i,a).pipe(ge(2,1),_(([l,p])=>l.url.pathname===p.url.pathname),f(([,l])=>l)).subscribe(({offset:l})=>{jr(l||{y:0})})}var Ba=Ke(Vr());var yi=Ke(Vr());function Nr(e,t){let r=new RegExp(e.separator,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator})(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(t?(0,yi.default)(a):a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Si(e){return e.split(/"([^"]+)"/g).map((t,r)=>r&1?t.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):t).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").trim()}var Le;(function(n){n[n.SETUP=0]="SETUP",n[n.READY=1]="READY",n[n.QUERY=2]="QUERY",n[n.RESULT=3]="RESULT"})(Le||(Le={}));function at(e){return e.type===1}function wi(e){return e.type===2}function st(e){return e.type===3}function Ja({config:e,docs:t,index:r}){e.lang.length===1&&e.lang[0]==="en"&&(e.lang=[Y("search.config.lang")]),e.separator==="[\\s\\-]+"&&(e.separator=Y("search.config.separator"));let n={pipeline:Y("search.config.pipeline").split(/\s*,\s*/).filter(Boolean),suggestions:fe("search.suggest")};return{config:e,docs:t,index:r,options:n}}function Ei(e,t){let r=se(),o=new Worker(e),n=new T,i=On(o,{tx$:n}).pipe(f(a=>{if(st(a))for(let s of a.data.items)for(let c of s)c.location=`${new URL(c.location,r.base)}`;return a}),pe());return Se(t).pipe(f(a=>({type:Le.SETUP,data:Ja(a)}))).subscribe(n.next.bind(n)),{tx$:n,rx$:i}}function Ti(){let e=se();Ee(new URL("../versions.json",e.base)).subscribe(t=>{be(".md-header__topic").appendChild(ni(t))})}function Ya(e,{rx$:t}){let r=(__search==null?void 0:__search.transform)||Si,o=on(e),n=U(E(e,"keyup"),E(e,"focus").pipe(Ae(1))).pipe(f(()=>r(e.value)),D()),i=Pe();return i.searchParams.has("q")&&(Ie("search",!0),t.pipe(_(at),he(1)).subscribe(()=>{e.value=i.searchParams.get("q"),ye(e)})),K([n,o]).pipe(f(([a,s])=>({value:a,focus:s})))}function Oi(e,{tx$:t,rx$:r}){let o=new T;return o.pipe(V("value"),f(({value:n})=>({type:Le.QUERY,data:n}))).subscribe(t.next.bind(t)),o.pipe(V("focus")).subscribe(({focus:n})=>{n?(Ie("search",n),Wn(e,"")):Un(e)}),E(e.form,"reset").pipe(Ar(o.pipe(Er(1)))).subscribe(()=>ye(e)),Ya(e,{tx$:t,rx$:r}).pipe(L(n=>o.next(n)),I(()=>o.complete()),f(n=>$({ref:e},n)))}function _i(e,{rx$:t},{query$:r}){let o=new T,n=sn(e.parentElement).pipe(_(Boolean)),i=be(":scope > :first-child",e),a=be(":scope > :last-child",e);return t.pipe(_(at),he(1)).subscribe(()=>{Ir(i)}),o.pipe(Q(X),ue(r)).subscribe(([{items:c},{value:l}])=>{l?Vn(i,c.length):Ir(i)}),o.pipe(Q(X),L(()=>Dn(a)),M(({items:c})=>U(R(...c.slice(0,10)),R(...c.slice(10)).pipe(ge(4),Cr(n),M(([l])=>R(...l)))))).subscribe(c=>{Nn(a,ti(c))}),t.pipe(_(st),f(({data:c})=>c)).pipe(L(c=>o.next(c)),I(()=>o.complete()),f(c=>$({ref:e},c)))}function Ga(e,{query$:t}){return t.pipe(f(({value:r})=>{let o=Pe();return o.hash="",o.searchParams.delete("h"),o.searchParams.set("q",r),{url:o}}))}function Mi(e,t){let r=new T;return r.subscribe(({url:o})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${o}`}),E(e,"click").subscribe(o=>o.preventDefault()),Ga(e,t).pipe(L(o=>r.next(o)),I(()=>r.complete()),f(o=>$({ref:e},o)))}function Ai(e,{rx$:t},{keyboard$:r}){let o=new T,n=Te("search-query"),i=E(n,"keydown").pipe(Q(Ce),f(()=>n.value),D());return o.pipe(ot(i),f(([{suggestions:s},c])=>{let l=c.split(/([\s-]+)/);if((s==null?void 0:s.length)&&l[l.length-1]){let p=s[s.length-1];p.startsWith(l[l.length-1])&&(l[l.length-1]=p)}else l.length=0;return l})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(_(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(_(st),f(({data:s})=>s)).pipe(L(s=>o.next(s)),I(()=>o.complete()),f(()=>({ref:e})))}function Li(e,{index$:t,keyboard$:r}){let o=se();try{let n=(__search==null?void 0:__search.worker)||o.search,i=Ei(n,t),a=Te("search-query",e),s=Te("search-result",e),{tx$:c,rx$:l}=i;c.pipe(_(wi),nt(l.pipe(_(at),he(1)))).subscribe(c.next.bind(c)),r.pipe(_(({mode:u})=>u==="search")).subscribe(u=>{let b=De();switch(u.type){case"Enter":if(b===a){let v=new Map;for(let d of B(":first-child [href]",s)){let W=d.firstElementChild;v.set(d,parseFloat(W.getAttribute("data-md-score")))}if(v.size){let[[d]]=[...v].sort(([,W],[,z])=>z-W);d.click()}u.claim()}break;case"Escape":case"Tab":Ie("search",!1),ye(a,!1);break;case"ArrowUp":case"ArrowDown":if(typeof b=="undefined")ye(a);else{let v=[a,...B(":not(details) > [href], summary, details[open] [href]",s)],d=Math.max(0,(Math.max(0,v.indexOf(b))+v.length+(u.type==="ArrowUp"?-1:1))%v.length);ye(v[d])}u.claim();break;default:a!==De()&&ye(a)}}),r.pipe(_(({mode:u})=>u==="global")).subscribe(u=>{switch(u.type){case"f":case"s":case"/":ye(a),cn(a),u.claim();break}});let p=Oi(a,i),m=_i(s,i,{query$:p});return U(p,m).pipe(Ne(...ne("search-share",e).map(u=>Mi(u,{query$:p})),...ne("search-suggest",e).map(u=>Ai(u,i,{keyboard$:r}))))}catch(n){return e.hidden=!0,J}}function ki(e,{index$:t,location$:r}){return K([t,r.pipe(N(Pe()),_(o=>o.searchParams.has("h")))]).pipe(f(([o,n])=>Nr(o.config,!0)(n.searchParams.get("h"))),f(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)==null?void 0:a.offsetHeight){let c=s.textContent,l=o(c);l.length>c.length&&n.set(s,l)}for(let[s,c]of n){let{childNodes:l}=F("span",null,c);s.replaceWith(...Array.from(l))}return{ref:e,nodes:n}}))}function Xa(e,{viewport$:t,main$:r}){let o=e.parentElement.offsetTop-e.parentElement.parentElement.offsetTop;return K([r,t]).pipe(f(([{offset:n,height:i},{offset:{y:a}}])=>(i=i+Math.min(o,Math.max(0,a-n))-o,{height:i,locked:a>=n+o})),D((n,i)=>n.height===i.height&&n.locked===i.locked))}function Dr(e,o){var n=o,{header$:t}=n,r=Jr(n,["header$"]);let i=new T;return i.pipe(Q(X),ue(t)).subscribe({next([{height:a},{height:s}]){Qn(e,a),zn(e,s)},complete(){qn(e),Kn(e)}}),Xa(e,r).pipe(L(a=>i.next(a)),I(()=>i.complete()),f(a=>$({ref:e},a)))}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return bt(Ee(`${r}/releases/latest`).pipe(f(o=>({version:o.tag_name})),Ve({})),Ee(r).pipe(f(o=>({stars:o.stargazers_count,forks:o.forks_count})),Ve({}))).pipe(f(([o,n])=>$($({},o),n)))}else{let r=`https://api.github.com/repos/${e}`;return Ee(r).pipe(f(o=>({repositories:o.public_repos})),Ve({}))}}function Hi(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ee(r).pipe(f(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Ve({}))}function ji(e){let[t]=e.match(/(git(?:hub|lab))/i)||[];switch(t.toLowerCase()){case"github":let[,r,o]=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);return Ci(r,o);case"gitlab":let[,n,i]=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i);return Hi(n,i);default:return J}}var Za;function es(e){return Za||(Za=Me(()=>{let t=sessionStorage.getItem(__prefix("__source"));if(t)return R(JSON.parse(t));{let r=ji(e.href);return r.subscribe(o=>{try{sessionStorage.setItem(__prefix("__source"),JSON.stringify(o))}catch(n){}}),r}}).pipe(rt(()=>J),_(t=>Object.keys(t).length>0),f(t=>({facts:t})),Z(1)))}function Ri(e){let t=new T;return t.subscribe(({facts:r})=>{Bn(e,ri(r)),Jn(e,"done")}),es(e).pipe(L(r=>t.next(r)),I(()=>t.complete()),f(r=>$({ref:e},r)))}function ts(e,{viewport$:t,header$:r}){return Fe(document.body).pipe(M(()=>Zt(e,{header$:r,viewport$:t})),f(({offset:{y:o}})=>({hidden:o>=10})),V("hidden"))}function Fi(e,t){let r=new T;return r.pipe(Q(X)).subscribe({next({hidden:o}){o?Yn(e,"hidden"):Pr(e)},complete(){Pr(e)}}),(fe("navigation.tabs.sticky")?R({hidden:!1}):ts(e,t)).pipe(L(o=>r.next(o)),I(()=>r.complete()),f(o=>$({ref:e},o)))}function rs(e,{viewport$:t,header$:r}){let o=new Map;for(let a of e){let s=decodeURIComponent(a.hash.substring(1)),c=ae(`[id="${s}"]`);typeof c!="undefined"&&o.set(a,c)}let n=r.pipe(f(a=>24+a.height));return Fe(document.body).pipe(V("height"),f(()=>{let a=[];return[...o].reduce((s,[c,l])=>{for(;a.length&&o.get(a[a.length-1]).tagName>=l.tagName;)a.pop();let p=l.offsetTop;for(;!p&&l.parentElement;)l=l.parentElement,p=l.offsetTop;return s.set([...a=[...a,c]].reverse(),p)},new Map)}),f(a=>new Map([...a].sort(([,s],[,c])=>s-c))),M(a=>K([n,t]).pipe(Tr(([s,c],[l,{offset:{y:p}}])=>{for(;c.length;){let[,m]=c[0];if(m-l=p)c=[s.pop(),...c];else break}return[s,c]},[[],[...a]]),D((s,c)=>s[0]===c[0]&&s[1]===c[1])))).pipe(f(([a,s])=>({prev:a.map(([c])=>c),next:s.map(([c])=>c)})),N({prev:[],next:[]}),ge(2,1),f(([a,s])=>a.prev.length{for(let[a]of i)Cn(a),Ln(a);for(let[a,[s]]of n.entries())kn(s,a===n.length-1),An(s,"blur")});let o=B("[href^=\\#]",e);return rs(o,t).pipe(L(n=>r.next(n)),I(()=>r.complete()),f(n=>$({ref:e},n)))}function os(e,{viewport$:t,main$:r}){let o=t.pipe(f(({offset:{y:i}})=>i),ge(2,1),f(([i,a])=>i>a&&a),D()),n=r.pipe(V("active"));return K([n,o]).pipe(f(([{active:i},a])=>({hidden:!(i&&a)})),D((i,a)=>i.hidden===a.hidden))}function Pi(e,{viewport$:t,header$:r,main$:o}){let n=new T;return n.pipe(Q(X),ue(r.pipe(V("height")))).subscribe({next([{hidden:i},{height:a}]){Xn(e,a+16),i?(Gn(e,"hidden"),ye(e,!1),er(e,-1)):($r(e),gt(e))},complete(){Zn(e),$r(e),gt(e)}}),os(e,{viewport$:t,header$:r,main$:o}).pipe(L(i=>n.next(i)),I(()=>n.complete()),f(i=>$({ref:e},i)))}function $i({document$:e,tablet$:t}){e.pipe(M(()=>R(...B("[data-md-state=indeterminate]"))),L(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>E(r,"change").pipe(Lr(()=>r.hasAttribute("data-md-state")),oe(r))),ue(t)).subscribe(([r,o])=>{r.removeAttribute("data-md-state"),o&&(r.checked=!1)})}function ns(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Wi({document$:e}){e.pipe(M(()=>R(...B("[data-md-scrollfix]"))),L(t=>t.removeAttribute("data-md-scrollfix")),_(ns),re(t=>E(t,"touchstart").pipe(oe(t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Ui({viewport$:e,tablet$:t}){K([Yt("search"),t]).pipe(f(([r,o])=>r&&!o),M(r=>R(r).pipe(Ae(r?400:100),Q(X))),ue(e)).subscribe(([r,{offset:{y:o}}])=>{r?_n(document.body,o):Mn(document.body)})}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=rn(),tr=fn(),zr=vn(),qr=pn(),me=Tn(),rr=vt("(min-width: 960px)"),Vi=vt("(min-width: 1220px)"),Ni=xn(),Di=se(),zi=document.forms.namedItem("search")?(__search==null?void 0:__search.index)||Ee(new URL("search/search_index.json",Di.base)):J,Qr=new T;vi({alert$:Qr});fe("navigation.instant")&&xi({document$:ct,location$:tr,viewport$:me});var Qi;((Qi=Di.version)==null?void 0:Qi.provider)==="mike"&&Ti();U(tr,zr).pipe(Ae(125)).subscribe(()=>{Ie("drawer",!1),Ie("search",!1)});qr.pipe(_(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ae("[href][rel=prev]");typeof t!="undefined"&&t.click();break;case"n":case".":let r=ae("[href][rel=next]");typeof r!="undefined"&&r.click();break}});$i({document$:ct,tablet$:rr});Wi({document$:ct});Ui({viewport$:me,tablet$:rr});var $e=fi(Te("header"),{viewport$:me}),or=ct.pipe(f(()=>Te("main")),M(e=>hi(e,{viewport$:me,header$:$e})),Z(1)),is=U(...ne("dialog").map(e=>ui(e,{alert$:Qr})),...ne("header").map(e=>mi(e,{viewport$:me,header$:$e,main$:or})),...ne("palette").map(e=>bi(e)),...ne("search").map(e=>Li(e,{index$:zi,keyboard$:qr})),...ne("source").map(e=>Ri(e))),as=Me(()=>U(...ne("content").map(e=>pi(e,{target$:zr,viewport$:me,print$:Ni})),...ne("content").map(e=>fe("search.highlight")?ki(e,{index$:zi,location$:tr}):J),...ne("header-title").map(e=>di(e,{viewport$:me,header$:$e})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Hr(Vi,()=>Dr(e,{viewport$:me,header$:$e,main$:or})):Hr(rr,()=>Dr(e,{viewport$:me,header$:$e,main$:or}))),...ne("tabs").map(e=>Fi(e,{viewport$:me,header$:$e})),...ne("toc").map(e=>Ii(e,{viewport$:me,header$:$e})),...ne("top").map(e=>Pi(e,{viewport$:me,header$:$e,main$:or})))),qi=ct.pipe(M(()=>as),Ne(is),Z(1));qi.subscribe();window.document$=ct;window.location$=tr;window.target$=zr;window.keyboard$=qr;window.viewport$=me;window.tablet$=rr;window.screen$=Vi;window.print$=Ni;window.alert$=Qr;window.component$=qi;})(); -//# sourceMappingURL=bundle.4fc53ad4.min.js.map - diff --git a/assets/javascripts/bundle.c7d1b464.min.js b/assets/javascripts/bundle.c7d1b464.min.js new file mode 100644 index 000000000..4cfea58f9 --- /dev/null +++ b/assets/javascripts/bundle.c7d1b464.min.js @@ -0,0 +1,29 @@ +(()=>{var ta=Object.create;var St=Object.defineProperty;var ra=Object.getOwnPropertyDescriptor;var oa=Object.getOwnPropertyNames,wt=Object.getOwnPropertySymbols,na=Object.getPrototypeOf,ar=Object.prototype.hasOwnProperty,Kr=Object.prototype.propertyIsEnumerable;var Br=(e,t,r)=>t in e?St(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))ar.call(t,r)&&Br(e,r,t[r]);if(wt)for(var r of wt(t))Kr.call(t,r)&&Br(e,r,t[r]);return e};var ia=e=>St(e,"__esModule",{value:!0});var Jr=(e,t)=>{var r={};for(var o in e)ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&wt)for(var o of wt(e))t.indexOf(o)<0&&Kr.call(e,o)&&(r[o]=e[o]);return r};var Et=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var aa=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of oa(t))!ar.call(e,o)&&o!=="default"&&St(e,o,{get:()=>t[o],enumerable:!(r=ra(t,o))||r.enumerable});return e},Ke=e=>aa(ia(St(e!=null?ta(na(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var Gr=Et((sr,Yr)=>{(function(e,t){typeof sr=="object"&&typeof Yr!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(sr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(E){return!!(E&&E!==document&&E.nodeName!=="HTML"&&E.nodeName!=="BODY"&&"classList"in E&&"contains"in E.classList)}function c(E){var We=E.type,Oe=E.tagName;return!!(Oe==="INPUT"&&a[We]&&!E.readOnly||Oe==="TEXTAREA"&&!E.readOnly||E.isContentEditable)}function l(E){E.classList.contains("focus-visible")||(E.classList.add("focus-visible"),E.setAttribute("data-focus-visible-added",""))}function p(E){!E.hasAttribute("data-focus-visible-added")||(E.classList.remove("focus-visible"),E.removeAttribute("data-focus-visible-added"))}function m(E){E.metaKey||E.altKey||E.ctrlKey||(s(r.activeElement)&&l(r.activeElement),o=!0)}function u(E){o=!1}function b(E){!s(E.target)||(o||c(E.target))&&l(E.target)}function v(E){!s(E.target)||(E.target.classList.contains("focus-visible")||E.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),p(E.target))}function d(E){document.visibilityState==="hidden"&&(n&&(o=!0),U())}function U(){document.addEventListener("mousemove",R),document.addEventListener("mousedown",R),document.addEventListener("mouseup",R),document.addEventListener("pointermove",R),document.addEventListener("pointerdown",R),document.addEventListener("pointerup",R),document.addEventListener("touchmove",R),document.addEventListener("touchstart",R),document.addEventListener("touchend",R)}function q(){document.removeEventListener("mousemove",R),document.removeEventListener("mousedown",R),document.removeEventListener("mouseup",R),document.removeEventListener("pointermove",R),document.removeEventListener("pointerdown",R),document.removeEventListener("pointerup",R),document.removeEventListener("touchmove",R),document.removeEventListener("touchstart",R),document.removeEventListener("touchend",R)}function R(E){E.target.nodeName&&E.target.nodeName.toLowerCase()==="html"||(o=!1,q())}document.addEventListener("keydown",m,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",d,!0),U(),r.addEventListener("focus",b,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var go=Et((ps,_t)=>{/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */var Xr,Zr,eo,to,ro,oo,no,io,ao,Tt,cr,so,co,lo,Be,po,uo,fo,mo,ho,bo,vo,xo,Ot;(function(e){var t=typeof global=="object"?global:typeof self=="object"?self:typeof this=="object"?this:{};typeof define=="function"&&define.amd?define("tslib",["exports"],function(o){e(r(t,r(o)))}):typeof _t=="object"&&typeof _t.exports=="object"?e(r(t,r(_t.exports))):e(r(t));function r(o,n){return o!==t&&(typeof Object.create=="function"?Object.defineProperty(o,"__esModule",{value:!0}):o.__esModule=!0),function(i,a){return o[i]=n?n(i,a):a}}})(function(e){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(o,n){o.__proto__=n}||function(o,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(o[i]=n[i])};Xr=function(o,n){if(typeof n!="function"&&n!==null)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");t(o,n);function i(){this.constructor=o}o.prototype=n===null?Object.create(n):(i.prototype=n.prototype,new i)},Zr=Object.assign||function(o){for(var n,i=1,a=arguments.length;i=0;p--)(l=o[p])&&(c=(s<3?l(c):s>3?l(n,i,c):l(n,i))||c);return s>3&&c&&Object.defineProperty(n,i,c),c},ro=function(o,n){return function(i,a){n(i,a,o)}},oo=function(o,n){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(o,n)},no=function(o,n,i,a){function s(c){return c instanceof i?c:new i(function(l){l(c)})}return new(i||(i=Promise))(function(c,l){function p(b){try{u(a.next(b))}catch(v){l(v)}}function m(b){try{u(a.throw(b))}catch(v){l(v)}}function u(b){b.done?c(b.value):s(b.value).then(p,m)}u((a=a.apply(o,n||[])).next())})},io=function(o,n){var i={label:0,sent:function(){if(c[0]&1)throw c[1];return c[1]},trys:[],ops:[]},a,s,c,l;return l={next:p(0),throw:p(1),return:p(2)},typeof Symbol=="function"&&(l[Symbol.iterator]=function(){return this}),l;function p(u){return function(b){return m([u,b])}}function m(u){if(a)throw new TypeError("Generator is already executing.");for(;i;)try{if(a=1,s&&(c=u[0]&2?s.return:u[0]?s.throw||((c=s.return)&&c.call(s),0):s.next)&&!(c=c.call(s,u[1])).done)return c;switch(s=0,c&&(u=[u[0]&2,c.value]),u[0]){case 0:case 1:c=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,s=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(c=i.trys,!(c=c.length>0&&c[c.length-1])&&(u[0]===6||u[0]===2)){i=0;continue}if(u[0]===3&&(!c||u[1]>c[0]&&u[1]=o.length&&(o=void 0),{value:o&&o[a++],done:!o}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")},cr=function(o,n){var i=typeof Symbol=="function"&&o[Symbol.iterator];if(!i)return o;var a=i.call(o),s,c=[],l;try{for(;(n===void 0||n-- >0)&&!(s=a.next()).done;)c.push(s.value)}catch(p){l={error:p}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(l)throw l.error}}return c},so=function(){for(var o=[],n=0;n1||p(d,U)})})}function p(d,U){try{m(a[d](U))}catch(q){v(c[0][3],q)}}function m(d){d.value instanceof Be?Promise.resolve(d.value.v).then(u,b):v(c[0][2],d)}function u(d){p("next",d)}function b(d){p("throw",d)}function v(d,U){d(U),c.shift(),c.length&&p(c[0][0],c[0][1])}},uo=function(o){var n,i;return n={},a("next"),a("throw",function(s){throw s}),a("return"),n[Symbol.iterator]=function(){return this},n;function a(s,c){n[s]=o[s]?function(l){return(i=!i)?{value:Be(o[s](l)),done:s==="return"}:c?c(l):l}:c}},fo=function(o){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n=o[Symbol.asyncIterator],i;return n?n.call(o):(o=typeof Tt=="function"?Tt(o):o[Symbol.iterator](),i={},a("next"),a("throw"),a("return"),i[Symbol.asyncIterator]=function(){return this},i);function a(c){i[c]=o[c]&&function(l){return new Promise(function(p,m){l=o[c](l),s(p,m,l.done,l.value)})}}function s(c,l,p,m){Promise.resolve(m).then(function(u){c({value:u,done:p})},l)}},mo=function(o,n){return Object.defineProperty?Object.defineProperty(o,"raw",{value:n}):o.raw=n,o};var r=Object.create?function(o,n){Object.defineProperty(o,"default",{enumerable:!0,value:n})}:function(o,n){o.default=n};ho=function(o){if(o&&o.__esModule)return o;var n={};if(o!=null)for(var i in o)i!=="default"&&Object.prototype.hasOwnProperty.call(o,i)&&Ot(n,o,i);return r(n,o),n},bo=function(o){return o&&o.__esModule?o:{default:o}},vo=function(o,n){if(!n.has(o))throw new TypeError("attempted to get private field on non-instance");return n.get(o)},xo=function(o,n,i){if(!n.has(o))throw new TypeError("attempted to set private field on non-instance");return n.set(o,i),i},e("__extends",Xr),e("__assign",Zr),e("__rest",eo),e("__decorate",to),e("__param",ro),e("__metadata",oo),e("__awaiter",no),e("__generator",io),e("__exportStar",ao),e("__createBinding",Ot),e("__values",Tt),e("__read",cr),e("__spread",so),e("__spreadArrays",co),e("__spreadArray",lo),e("__await",Be),e("__asyncGenerator",po),e("__asyncDelegator",uo),e("__asyncValues",fo),e("__makeTemplateObject",mo),e("__importStar",ho),e("__importDefault",bo),e("__classPrivateFieldGet",vo),e("__classPrivateFieldSet",xo)})});var Fr=Et((xt,Rr)=>{/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof xt=="object"&&typeof Rr=="object"?Rr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof xt=="object"?xt.ClipboardJS=r():t.ClipboardJS=r()})(xt,function(){return function(){var e={134:function(o,n,i){"use strict";i.d(n,{default:function(){return Zi}});var a=i(279),s=i.n(a),c=i(370),l=i.n(c),p=i(817),m=i.n(p);function u(O){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?u=function(h){return typeof h}:u=function(h){return h&&typeof Symbol=="function"&&h.constructor===Symbol&&h!==Symbol.prototype?"symbol":typeof h},u(O)}function b(O,x){if(!(O instanceof x))throw new TypeError("Cannot call a class as a function")}function v(O,x){for(var h=0;h0&&arguments[0]!==void 0?arguments[0]:{};this.action=h.action,this.container=h.container,this.emitter=h.emitter,this.target=h.target,this.text=h.text,this.trigger=h.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"createFakeElement",value:function(){var h=document.documentElement.getAttribute("dir")==="rtl";this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[h?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return this.fakeElem.style.top="".concat(k,"px"),this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.fakeElem}},{key:"selectFake",value:function(){var h=this,k=this.createFakeElement();this.fakeHandlerCallback=function(){return h.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.container.appendChild(k),this.selectedText=m()(k),this.copyText(),this.removeFake()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=m()(this.target),this.copyText()}},{key:"copyText",value:function(){var h;try{h=document.execCommand(this.action)}catch(k){h=!1}this.handleResult(h)}},{key:"handleResult",value:function(h){this.emitter.emit(h?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var h=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"copy";if(this._action=h,this._action!=="copy"&&this._action!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(h){if(h!==void 0)if(h&&u(h)==="object"&&h.nodeType===1){if(this.action==="copy"&&h.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(this.action==="cut"&&(h.hasAttribute("readonly")||h.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`);this._target=h}else throw new Error('Invalid "target" value, use a valid Element')},get:function(){return this._target}}]),O}(),q=U;function R(O){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(h){return typeof h}:R=function(h){return h&&typeof Symbol=="function"&&h.constructor===Symbol&&h!==Symbol.prototype?"symbol":typeof h},R(O)}function E(O,x){if(!(O instanceof x))throw new TypeError("Cannot call a class as a function")}function We(O,x){for(var h=0;h0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof $.action=="function"?$.action:this.defaultAction,this.target=typeof $.target=="function"?$.target:this.defaultTarget,this.text=typeof $.text=="function"?$.text:this.defaultText,this.container=R($.container)==="object"?$.container:document.body}},{key:"listenClick",value:function($){var te=this;this.listener=l()($,"click",function(lt){return te.onClick(lt)})}},{key:"onClick",value:function($){var te=$.delegateTarget||$.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new q({action:this.action(te),target:this.target(te),text:this.text(te),container:this.container,trigger:te,emitter:this})}},{key:"defaultAction",value:function($){return ir("action",$)}},{key:"defaultTarget",value:function($){var te=ir("target",$);if(te)return document.querySelector(te)}},{key:"defaultText",value:function($){return ir("text",$)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var $=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],te=typeof $=="string"?[$]:$,lt=!!document.queryCommandSupported;return te.forEach(function(ea){lt=lt&&!!document.queryCommandSupported(ea)}),lt}}]),h}(s()),Zi=Xi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,c){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(c))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(p,m,u,b,v){var d=l.apply(this,arguments);return p.addEventListener(u,d,v),{destroy:function(){p.removeEventListener(u,d,v)}}}function c(p,m,u,b,v){return typeof p.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof p=="string"&&(p=document.querySelectorAll(p)),Array.prototype.map.call(p,function(d){return s(d,m,u,b,v)}))}function l(p,m,u,b){return function(v){v.delegateTarget=a(v.target,m),v.delegateTarget&&b.call(p,v)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function c(u,b,v){if(!u&&!b&&!v)throw new Error("Missing required arguments");if(!a.string(b))throw new TypeError("Second argument must be a String");if(!a.fn(v))throw new TypeError("Third argument must be a Function");if(a.node(u))return l(u,b,v);if(a.nodeList(u))return p(u,b,v);if(a.string(u))return m(u,b,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function l(u,b,v){return u.addEventListener(b,v),{destroy:function(){u.removeEventListener(b,v)}}}function p(u,b,v){return Array.prototype.forEach.call(u,function(d){d.addEventListener(b,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(d){d.removeEventListener(b,v)})}}}function m(u,b,v){return s(document.body,u,b,v)}o.exports=c},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),l=document.createRange();l.selectNodeContents(i),c.removeAllRanges(),c.addRange(l),a=c.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var c=this;function l(){c.off(i,l),a.apply(s,arguments)}return l._=a,this.on(i,l,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),c=0,l=s.length;for(c;c{/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */"use strict";var Ba=/["'&<>]/;yi.exports=Ja;function Ja(e){var t=""+e,r=Ba.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=o.hasError,i=o.isStopped,a=o.observers;return n||i?lr:(a.push(r),new ie(function(){return _e(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new A;return r.source=this,r},t.create=function(r,o){return new jo(r,o)},t}(A);var jo=function(e){G(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:lr},t}(T);var ft={now:function(){return(ft.delegate||Date).now()},delegate:void 0};var mt=function(e){G(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=ft);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,c=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Ge.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){if(n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);r.actions.length===0&&(Ge.cancelAnimationFrame(o),r._scheduled=void 0)},t}(jt);var Io=function(e){G(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0,this._scheduled=void 0;var o=this.actions,n,i=-1;r=r||o.shift();var a=o.length;do if(n=r.execute(r.state,r.delay))break;while(++i=2,!0))}function ue(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,c=s===void 0?!0:s;return function(l){var p=null,m=null,u=null,b=0,v=!1,d=!1,U=function(){m==null||m.unsubscribe(),m=null},q=function(){U(),p=u=null,v=d=!1},R=function(){var E=p;q(),E==null||E.unsubscribe()};return g(function(E,We){b++,!d&&!v&&U();var Oe=u=u!=null?u:r();We.add(function(){b--,b===0&&!d&&!v&&(m=Or(R,c))}),Oe.subscribe(We),p||(p=new ut({next:function(Qe){return Oe.next(Qe)},error:function(Qe){d=!0,U(),m=Or(q,n,Qe),Oe.error(Qe)},complete:function(){v=!0,U(),m=Or(q,a),Oe.complete()}}),Se(E).subscribe(p))})(l)}}function Or(e,t){for(var r=[],o=2;ot==="focus"),N(e===De()))}var nn=new T,ja=Me(()=>F(new ResizeObserver(e=>{for(let t of e)nn.next(t)}))).pipe(M(e=>K.pipe(N(e)).pipe(j(()=>e.disconnect()))),ee(1));function Re(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function Fe(e){return ja.pipe(L(t=>t.observe(e)),M(t=>nn.pipe(_(({target:r})=>r===e),j(()=>t.unobserve(e)),f(()=>Re(e)))),N(Re(e)))}function an(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ra(e){return W(w(e,"scroll"),w(window,"resize")).pipe(f(()=>an(e)),N(an(e)))}function sn(e,t=16){return Ra(e).pipe(f(({y:r})=>{let o=Re(e),n=Bt(e);return r>=n.height-o.height-t}),D())}function cn(e){if(e instanceof HTMLInputElement)e.select();else throw new Error("Not implemented")}var Jt={drawer:se("[data-md-toggle=drawer]"),search:se("[data-md-toggle=search]")};function ln(e){return Jt[e].checked}function Ie(e,t){Jt[e].checked!==t&&Jt[e].click()}function Yt(e){let t=Jt[e];return w(t,"change").pipe(f(()=>t.checked),N(t.checked))}function Fa(e){switch(e.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return!0;default:return e.isContentEditable}}function pn(){return w(window,"keydown").pipe(_(e=>!(e.metaKey||e.ctrlKey)),f(e=>({mode:ln("search")?"search":"global",type:e.key,claim(){e.preventDefault(),e.stopPropagation()}})),_(({mode:e})=>{if(e==="global"){let t=De();if(typeof t!="undefined")return!Fa(t)}return!0}),ue())}function Pe(){return new URL(location.href)}function un(e){location.href=e.href}function fn(){return new T}function mn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)mn(e,r)}function I(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="boolean"?o.setAttribute(n,t[n]):t[n]&&o.setAttribute(n,"");for(let n of r)mn(o,n);return o}function dn(e,t){let r=t;if(e.length>r){for(;e[r]!==" "&&--r>0;);return`${e.substring(0,r)}...`}return e}function Gt(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function hn(){return location.hash.substring(1)}function bn(e){let t=I("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Ia(){return w(window,"hashchange").pipe(f(hn),N(hn()),_(e=>e.length>0),ee(1))}function vn(){return Ia().pipe(f(e=>ae(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function vt(e){let t=matchMedia(e);return Qt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function xn(){return w(window,"beforeprint").pipe(Z(void 0))}function Hr(e,t){return e.pipe(M(r=>r?t():K))}function Xt(e,t={credentials:"same-origin"}){return Se(fetch(`${e}`,t)).pipe(_(r=>r.status===200))}function Ee(e,t){return Xt(e,t).pipe(M(r=>r.json()),ee(1))}function gn(e,t){let r=new DOMParser;return Xt(e,t).pipe(M(o=>o.text()),f(o=>r.parseFromString(o,"text/xml")),ee(1))}function yn(){return{x:Math.max(0,pageXOffset),y:Math.max(0,pageYOffset)}}function jr({x:e,y:t}){window.scrollTo(e||0,t||0)}function Sn(){return W(w(window,"scroll",{passive:!0}),w(window,"resize",{passive:!0})).pipe(f(yn),N(yn()))}function wn(){return{width:innerWidth,height:innerHeight}}function En(){return w(window,"resize",{passive:!0}).pipe(f(wn),N(wn()))}function Tn(){return J([Sn(),En()]).pipe(f(([e,t])=>({offset:e,size:t})),ee(1))}function Zt(e,{viewport$:t,header$:r}){let o=t.pipe(V("size")),n=J([o,r]).pipe(f(()=>({x:e.offsetLeft,y:e.offsetTop})));return J([r,t,n]).pipe(f(([{height:i},{offset:a,size:s},{x:c,y:l}])=>({offset:{x:a.x-c,y:a.y-l+i},size:s})))}function On(e,{tx$:t}){let r=w(e,"message").pipe(f(({data:o})=>o));return t.pipe(kr(()=>r,{leading:!0,trailing:!0}),L(o=>e.postMessage(o)),Mr(r),ue())}var Pa=se("#__config"),it=JSON.parse(Pa.textContent);it.base=`${new URL(it.base,Pe())}`;function ce(){return it}function me(e){return it.features.includes(e)}function Y(e,t){return typeof t!="undefined"?it.translations[e].replace("#",t.toString()):it.translations[e]}function Te(e,t=document){return se(`[data-md-component=${e}]`,t)}function ne(e,t=document){return z(`[data-md-component=${e}]`,t)}var ii=Ke(Fr());function er(e,t=0){e.setAttribute("tabindex",t.toString())}function gt(e){e.removeAttribute("tabindex")}function _n(e,t){e.setAttribute("data-md-state","lock"),e.style.top=`-${t}px`}function Mn(e){let t=-1*parseInt(e.style.top,10);e.removeAttribute("data-md-state"),e.style.top="",t&&window.scrollTo(0,t)}function An(e,t){e.setAttribute("data-md-state",t)}function Ln(e){e.removeAttribute("data-md-state")}function kn(e,t){e.classList.toggle("md-nav__link--active",t)}function Cn(e){e.classList.remove("md-nav__link--active")}function Hn(e,t){e.firstElementChild.innerHTML=t}function jn(e,t){e.setAttribute("data-md-state",t)}function Rn(e){e.removeAttribute("data-md-state")}function Fn(e,t){e.setAttribute("data-md-state",t)}function In(e){e.removeAttribute("data-md-state")}function Pn(e,t){e.setAttribute("data-md-state",t)}function $n(e){e.removeAttribute("data-md-state")}function Wn(e,t){e.placeholder=t}function Un(e){e.placeholder=Y("search.placeholder")}function Vn(e,t){switch(t){case 0:e.textContent=Y("search.result.none");break;case 1:e.textContent=Y("search.result.one");break;default:e.textContent=Y("search.result.other",Gt(t))}}function Ir(e){e.textContent=Y("search.result.placeholder")}function Nn(e,t){e.appendChild(t)}function Dn(e){e.innerHTML=""}function zn(e,t){e.style.top=`${t}px`}function qn(e){e.style.top=""}function Qn(e,t){let r=e.firstElementChild;r.style.height=`${t-2*r.offsetTop}px`}function Kn(e){let t=e.firstElementChild;t.style.height=""}function Bn(e,t){e.lastElementChild.appendChild(t)}function Jn(e,t){e.lastElementChild.setAttribute("data-md-state",t)}function Yn(e,t){e.setAttribute("data-md-state",t)}function Pr(e){e.removeAttribute("data-md-state")}function Gn(e,t){e.setAttribute("data-md-state",t)}function $r(e){e.removeAttribute("data-md-state")}function Xn(e,t){e.style.top=`${t}px`}function Zn(e){e.style.top=""}function ei(e){return I("button",{class:"md-clipboard md-icon",title:Y("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var qe;(function(r){r[r.TEASER=1]="TEASER",r[r.PARENT=2]="PARENT"})(qe||(qe={}));function Wr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(a=>!e.terms[a]).map(a=>[I("del",null,a)," "]).flat().slice(0,-1),i=new URL(e.location);return me("search.highlight")&&i.searchParams.set("h",Object.entries(e.terms).filter(([,a])=>a).reduce((a,[s])=>`${a} ${s}`.trim(),"")),I("a",{href:`${i}`,class:"md-search-result__link",tabIndex:-1},I("article",{class:["md-search-result__article",...r?["md-search-result__article--document"]:[]].join(" "),"data-md-score":e.score.toFixed(2)},r>0&&I("div",{class:"md-search-result__icon md-icon"}),I("h1",{class:"md-search-result__title"},e.title),o>0&&e.text.length>0&&I("p",{class:"md-search-result__teaser"},dn(e.text,320)),o>0&&n.length>0&&I("p",{class:"md-search-result__terms"},Y("search.result.term.missing"),": ",n)))}function ti(e){let t=e[0].score,r=[...e],o=r.findIndex(l=>!l.location.includes("#")),[n]=r.splice(o,1),i=r.findIndex(l=>l.scoreWr(l,1)),...s.length?[I("details",{class:"md-search-result__more"},I("summary",{tabIndex:-1},s.length>0&&s.length===1?Y("search.result.more.one"):Y("search.result.more.other",s.length)),s.map(l=>Wr(l,1)))]:[]];return I("li",{class:"md-search-result__item"},c)}function ri(e){return I("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>I("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?Gt(r):r)))}function oi(e){return I("div",{class:"md-typeset__scrollwrap"},I("div",{class:"md-typeset__table"},e))}function $a(e){let t=ce(),r=new URL(`../${e.version}/`,t.base);return I("li",{class:"md-version__item"},I("a",{href:r.toString(),class:"md-version__link"},e.title))}function ni(e){let t=ce(),[,r]=t.base.match(/([^/]+)\/?$/),o=e.find(({version:n,aliases:i})=>n===r||i.includes(r))||e[0];return I("div",{class:"md-version"},I("button",{class:"md-version__current","aria-label":Y("select.version.title")},o.title),I("ul",{class:"md-version__list"},e.map($a)))}var Wa=0;function Ua(e,{viewport$:t}){let r=F(e).pipe(M(o=>{let n=o.closest("[data-tabs]");return n instanceof HTMLElement?W(...z("input",n).map(i=>w(i,"change"))):K}));return W(t.pipe(V("size")),r).pipe(f(()=>{let o=Re(e);return{scroll:Bt(e).width>o.width}}),V("scroll"))}function ai(e,t){let r=new T;if(r.pipe(fe(vt("(hover)"))).subscribe(([{scroll:o},n])=>{o&&n?er(e):gt(e)}),ii.default.isSupported()){let o=e.closest("pre");o.id=`__code_${Wa++}`,o.insertBefore(ei(o.id),e)}return Ua(e,t).pipe(L(o=>r.next(o)),j(()=>r.complete()),f(o=>P({ref:e},o)))}function Va(e,{target$:t,print$:r}){return t.pipe(f(o=>o.closest("details:not([open])")),_(o=>e===o),Z({scroll:!0}),Ne(r.pipe(Z({}))))}function si(e,t){let r=new T;return r.subscribe(({scroll:o})=>{e.setAttribute("open",""),o&&e.scrollIntoView()}),Va(e,t).pipe(L(o=>r.next(o)),j(()=>r.complete()),Z({ref:e}))}var ci=I("table");function li(e){return ze(e,ci),ze(ci,oi(e)),F({ref:e})}function Na(e){return e.classList.contains(".tabbed-alternate")?W(...z(":scope > input",e).map(t=>w(t,"change").pipe(Z(t.id)))).pipe(f(t=>({active:se(`label[for=${t}]`)}))):K}function pi(e){let t=new T;return t.subscribe(({active:r})=>{r.scrollIntoView({behavior:"smooth",block:"nearest"})}),Na(e).pipe(L(r=>t.next(r)),j(()=>t.complete()),f(r=>P({ref:e},r)))}function ui(e,{target$:t,viewport$:r,print$:o}){return W(...z("pre > code",e).map(n=>ai(n,{viewport$:r})),...z("table:not([class])",e).map(n=>li(n)),...z("details",e).map(n=>si(n,{target$:t,print$:o})),...z("[data-tabs]",e).map(n=>pi(n)))}function Da(e,{alert$:t}){return t.pipe(M(r=>W(F(!0),F(!1).pipe(Ae(2e3))).pipe(f(o=>({message:r,open:o})))))}function fi(e,t){let r=new T;return r.pipe(B(X)).subscribe(({message:o,open:n})=>{Hn(e,o),n?jn(e,"open"):Rn(e)}),Da(e,t).pipe(L(o=>r.next(o)),j(()=>r.complete()),f(o=>P({ref:e},o)))}function za({viewport$:e}){if(!me("header.autohide"))return F(!1);let t=e.pipe(f(({offset:{y:n}})=>n),ge(2,1),f(([n,i])=>[nMath.abs(i-n.y)>100),f(([,[n]])=>n),D()),o=Yt("search");return J([e,o]).pipe(f(([{offset:n},i])=>n.y>400&&!i),D(),M(n=>n?r:F(!1)),N(!1))}function mi(e,t){return Me(()=>{let r=getComputedStyle(e);return F(r.position==="sticky"||r.position==="-webkit-sticky")}).pipe(ot(Fe(e),za(t)),f(([r,{height:o},n])=>({height:r?o:0,sticky:r,hidden:n})),D((r,o)=>r.sticky===o.sticky&&r.height===o.height&&r.hidden===o.hidden),ee(1))}function di(e,{header$:t,main$:r}){let o=new T;return o.pipe(V("active"),ot(t),B(X)).subscribe(([{active:n},{hidden:i}])=>{n?Fn(e,i?"hidden":"shadow"):In(e)}),r.subscribe(n=>o.next(n)),t.pipe(f(n=>P({ref:e},n)))}function qa(e,{viewport$:t,header$:r}){return Zt(e,{header$:r,viewport$:t}).pipe(f(({offset:{y:o}})=>{let{height:n}=Re(e);return{active:o>=n}}),V("active"))}function hi(e,t){let r=new T;r.pipe(B(X)).subscribe(({active:n})=>{n?Pn(e,"active"):$n(e)});let o=ae("article h1");return typeof o=="undefined"?K:qa(o,t).pipe(L(n=>r.next(n)),j(()=>r.complete()),f(n=>P({ref:e},n)))}function bi(e,{viewport$:t,header$:r}){let o=r.pipe(f(({height:i})=>i),D()),n=o.pipe(M(()=>Fe(e).pipe(f(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),V("bottom"))));return J([o,n,t]).pipe(f(([i,{top:a,bottom:s},{offset:{y:c},size:{height:l}}])=>(l=Math.max(0,l-Math.max(0,a-c,i)-Math.max(0,l+c-s)),{offset:a-i,height:l,active:a-i<=c})),D((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Qa(e){let t=localStorage.getItem(__prefix("__palette")),r=JSON.parse(t)||{index:e.findIndex(n=>matchMedia(n.getAttribute("data-md-color-media")).matches)},o=F(...e).pipe(oe(n=>w(n,"change").pipe(Z(n))),N(e[Math.max(0,r.index)]),f(n=>({index:e.indexOf(n),color:{scheme:n.getAttribute("data-md-color-scheme"),primary:n.getAttribute("data-md-color-primary"),accent:n.getAttribute("data-md-color-accent")}})),ee(1));return o.subscribe(n=>{localStorage.setItem(__prefix("__palette"),JSON.stringify(n))}),o}function vi(e){let t=new T;t.subscribe(o=>{for(let[n,i]of Object.entries(o.color))typeof i=="string"&&document.body.setAttribute(`data-md-color-${n}`,i);for(let n=0;nt.next(o)),j(()=>t.complete()),f(o=>P({ref:e},o)))}var Ur=Ke(Fr());function xi({alert$:e}){Ur.default.isSupported()&&new A(t=>{new Ur.default("[data-clipboard-target], [data-clipboard-text]").on("success",r=>t.next(r))}).subscribe(()=>e.next(Y("clipboard.copied")))}function Ka(e){if(e.length<2)return e;let[t,r]=e.sort((i,a)=>i.length-a.length).map(i=>i.replace(/[^/]+$/,"")),o=0;if(t===r)o=t.length;else for(;t.charCodeAt(o)===r.charCodeAt(o);)o++;let n=ce();return e.map(i=>i.replace(t.slice(0,o),n.base))}function gi({document$:e,location$:t,viewport$:r}){let o=ce();if(location.protocol==="file:")return;"scrollRestoration"in history&&(history.scrollRestoration="manual",w(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}));let n=ae("link[rel=icon]");typeof n!="undefined"&&(n.href=n.href);let i=gn(new URL("sitemap.xml",o.base)).pipe(f(l=>Ka(z("loc",l).map(p=>p.textContent))),M(l=>w(document.body,"click").pipe(_(p=>!p.metaKey&&!p.ctrlKey),M(p=>{if(p.target instanceof Element){let m=p.target.closest("a");if(m&&!m.target){let u=new URL(m.href);if(u.search="",u.hash="",u.pathname!==location.pathname&&l.includes(u.toString()))return p.preventDefault(),F({url:new URL(m.href)})}}return K}))),ue()),a=w(window,"popstate").pipe(_(l=>l.state!==null),f(l=>({url:new URL(location.href),offset:l.state})),ue());W(i,a).pipe(D((l,p)=>l.url.href===p.url.href),f(({url:l})=>l)).subscribe(t);let s=t.pipe(V("pathname"),M(l=>Xt(l.href).pipe(rt(()=>(un(l),K)))),ue());i.pipe(nt(s)).subscribe(({url:l})=>{history.pushState({},"",`${l}`)});let c=new DOMParser;s.pipe(M(l=>l.text()),f(l=>c.parseFromString(l,"text/html"))).subscribe(e),e.pipe(Kt(1)).subscribe(l=>{for(let p of["title","link[rel=canonical]","meta[name=author]","meta[name=description]","[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=logo], .md-logo","[data-md-component=skip]",...me("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let m=ae(p),u=ae(p,l);typeof m!="undefined"&&typeof u!="undefined"&&ze(m,u)}}),e.pipe(Kt(1),f(()=>Te("container")),M(l=>F(...z("script",l))),yr(l=>{let p=I("script");if(l.src){for(let m of l.getAttributeNames())p.setAttribute(m,l.getAttribute(m));return ze(l,p),new A(m=>{p.onload=()=>m.complete()})}else return p.textContent=l.textContent,ze(l,p),xe})).subscribe(),W(i,a).pipe(nt(e)).subscribe(({url:l,offset:p})=>{l.hash&&!p?bn(l.hash):jr(p||{y:0})}),r.pipe(_r(i),Sr(250),V("offset")).subscribe(({offset:l})=>{history.replaceState(l,"")}),W(i,a).pipe(ge(2,1),_(([l,p])=>l.url.pathname===p.url.pathname),f(([,l])=>l)).subscribe(({offset:l})=>{jr(l||{y:0})})}var Ya=Ke(Vr());var Si=Ke(Vr());function Nr(e,t){let r=new RegExp(e.separator,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator})(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(t?(0,Si.default)(a):a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function wi(e){return e.split(/"([^"]+)"/g).map((t,r)=>r&1?t.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):t).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").trim()}var Le;(function(n){n[n.SETUP=0]="SETUP",n[n.READY=1]="READY",n[n.QUERY=2]="QUERY",n[n.RESULT=3]="RESULT"})(Le||(Le={}));function at(e){return e.type===1}function Ei(e){return e.type===2}function st(e){return e.type===3}function Ga({config:e,docs:t,index:r}){e.lang.length===1&&e.lang[0]==="en"&&(e.lang=[Y("search.config.lang")]),e.separator==="[\\s\\-]+"&&(e.separator=Y("search.config.separator"));let n={pipeline:Y("search.config.pipeline").split(/\s*,\s*/).filter(Boolean),suggestions:me("search.suggest")};return{config:e,docs:t,index:r,options:n}}function Ti(e,t){let r=ce(),o=new Worker(e),n=new T,i=On(o,{tx$:n}).pipe(f(a=>{if(st(a))for(let s of a.data.items)for(let c of s)c.location=`${new URL(c.location,r.base)}`;return a}),ue());return Se(t).pipe(f(a=>({type:Le.SETUP,data:Ga(a)}))).subscribe(n.next.bind(n)),{tx$:n,rx$:i}}function Oi(){let e=ce();Ee(new URL("../versions.json",e.base)).subscribe(t=>{se(".md-header__topic").appendChild(ni(t))})}function Xa(e,{rx$:t}){let r=(__search==null?void 0:__search.transform)||wi,o=on(e),n=W(w(e,"keyup"),w(e,"focus").pipe(Ae(1))).pipe(f(()=>r(e.value)),D()),i=Pe();return i.searchParams.has("q")&&(Ie("search",!0),t.pipe(_(at),be(1)).subscribe(()=>{e.value=i.searchParams.get("q"),ye(e)})),J([n,o]).pipe(f(([a,s])=>({value:a,focus:s})))}function _i(e,{tx$:t,rx$:r}){let o=new T;return o.pipe(V("value"),f(({value:n})=>({type:Le.QUERY,data:n}))).subscribe(t.next.bind(t)),o.pipe(V("focus")).subscribe(({focus:n})=>{n?(Ie("search",n),Wn(e,"")):Un(e)}),w(e.form,"reset").pipe(Ar(o.pipe(Er(1)))).subscribe(()=>ye(e)),Xa(e,{tx$:t,rx$:r}).pipe(L(n=>o.next(n)),j(()=>o.complete()),f(n=>P({ref:e},n)))}function Mi(e,{rx$:t},{query$:r}){let o=new T,n=sn(e.parentElement).pipe(_(Boolean)),i=se(":scope > :first-child",e),a=se(":scope > :last-child",e);return t.pipe(_(at),be(1)).subscribe(()=>{Ir(i)}),o.pipe(B(X),fe(r)).subscribe(([{items:c},{value:l}])=>{l?Vn(i,c.length):Ir(i)}),o.pipe(B(X),L(()=>Dn(a)),M(({items:c})=>W(F(...c.slice(0,10)),F(...c.slice(10)).pipe(ge(4),Cr(n),M(([l])=>F(...l)))))).subscribe(c=>{Nn(a,ti(c))}),t.pipe(_(st),f(({data:c})=>c)).pipe(L(c=>o.next(c)),j(()=>o.complete()),f(c=>P({ref:e},c)))}function Za(e,{query$:t}){return t.pipe(f(({value:r})=>{let o=Pe();return o.hash="",o.searchParams.delete("h"),o.searchParams.set("q",r),{url:o}}))}function Ai(e,t){let r=new T;return r.subscribe(({url:o})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${o}`}),w(e,"click").subscribe(o=>o.preventDefault()),Za(e,t).pipe(L(o=>r.next(o)),j(()=>r.complete()),f(o=>P({ref:e},o)))}function Li(e,{rx$:t},{keyboard$:r}){let o=new T,n=Te("search-query"),i=w(n,"keydown").pipe(B(Ce),f(()=>n.value),D());return o.pipe(ot(i),f(([{suggestions:s},c])=>{let l=c.split(/([\s-]+)/);if((s==null?void 0:s.length)&&l[l.length-1]){let p=s[s.length-1];p.startsWith(l[l.length-1])&&(l[l.length-1]=p)}else l.length=0;return l})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(_(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(_(st),f(({data:s})=>s)).pipe(L(s=>o.next(s)),j(()=>o.complete()),f(()=>({ref:e})))}function ki(e,{index$:t,keyboard$:r}){let o=ce();try{let n=(__search==null?void 0:__search.worker)||o.search,i=Ti(n,t),a=Te("search-query",e),s=Te("search-result",e),{tx$:c,rx$:l}=i;c.pipe(_(Ei),nt(l.pipe(_(at),be(1)))).subscribe(c.next.bind(c)),r.pipe(_(({mode:u})=>u==="search")).subscribe(u=>{let b=De();switch(u.type){case"Enter":if(b===a){let v=new Map;for(let d of z(":first-child [href]",s)){let U=d.firstElementChild;v.set(d,parseFloat(U.getAttribute("data-md-score")))}if(v.size){let[[d]]=[...v].sort(([,U],[,q])=>q-U);d.click()}u.claim()}break;case"Escape":case"Tab":Ie("search",!1),ye(a,!1);break;case"ArrowUp":case"ArrowDown":if(typeof b=="undefined")ye(a);else{let v=[a,...z(":not(details) > [href], summary, details[open] [href]",s)],d=Math.max(0,(Math.max(0,v.indexOf(b))+v.length+(u.type==="ArrowUp"?-1:1))%v.length);ye(v[d])}u.claim();break;default:a!==De()&&ye(a)}}),r.pipe(_(({mode:u})=>u==="global")).subscribe(u=>{switch(u.type){case"f":case"s":case"/":ye(a),cn(a),u.claim();break}});let p=_i(a,i),m=Mi(s,i,{query$:p});return W(p,m).pipe(Ne(...ne("search-share",e).map(u=>Ai(u,{query$:p})),...ne("search-suggest",e).map(u=>Li(u,i,{keyboard$:r}))))}catch(n){return e.hidden=!0,K}}function Ci(e,{index$:t,location$:r}){return J([t,r.pipe(N(Pe()),_(o=>o.searchParams.has("h")))]).pipe(f(([o,n])=>Nr(o.config,!0)(n.searchParams.get("h"))),f(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)==null?void 0:a.offsetHeight){let c=s.textContent,l=o(c);l.length>c.length&&n.set(s,l)}for(let[s,c]of n){let{childNodes:l}=I("span",null,c);s.replaceWith(...Array.from(l))}return{ref:e,nodes:n}}))}function es(e,{viewport$:t,main$:r}){let o=e.parentElement.offsetTop-e.parentElement.parentElement.offsetTop;return J([r,t]).pipe(f(([{offset:n,height:i},{offset:{y:a}}])=>(i=i+Math.min(o,Math.max(0,a-n))-o,{height:i,locked:a>=n+o})),D((n,i)=>n.height===i.height&&n.locked===i.locked))}function Dr(e,o){var n=o,{header$:t}=n,r=Jr(n,["header$"]);let i=new T;return i.pipe(B(X),fe(t)).subscribe({next([{height:a},{height:s}]){Qn(e,a),zn(e,s)},complete(){qn(e),Kn(e)}}),es(e,r).pipe(L(a=>i.next(a)),j(()=>i.complete()),f(a=>P({ref:e},a)))}function Hi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return bt(Ee(`${r}/releases/latest`).pipe(f(o=>({version:o.tag_name})),Ve({})),Ee(r).pipe(f(o=>({stars:o.stargazers_count,forks:o.forks_count})),Ve({}))).pipe(f(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ee(r).pipe(f(o=>({repositories:o.public_repos})),Ve({}))}}function ji(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ee(r).pipe(f(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Ve({}))}function Ri(e){let[t]=e.match(/(git(?:hub|lab))/i)||[];switch(t.toLowerCase()){case"github":let[,r,o]=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);return Hi(r,o);case"gitlab":let[,n,i]=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i);return ji(n,i);default:return K}}var ts;function rs(e){return ts||(ts=Me(()=>{let t=sessionStorage.getItem(__prefix("__source"));if(t)return F(JSON.parse(t));{let r=Ri(e.href);return r.subscribe(o=>{try{sessionStorage.setItem(__prefix("__source"),JSON.stringify(o))}catch(n){}}),r}}).pipe(rt(()=>K),_(t=>Object.keys(t).length>0),f(t=>({facts:t})),ee(1)))}function Fi(e){let t=new T;return t.subscribe(({facts:r})=>{Bn(e,ri(r)),Jn(e,"done")}),rs(e).pipe(L(r=>t.next(r)),j(()=>t.complete()),f(r=>P({ref:e},r)))}function os(e,{viewport$:t,header$:r}){return Fe(document.body).pipe(M(()=>Zt(e,{header$:r,viewport$:t})),f(({offset:{y:o}})=>({hidden:o>=10})),V("hidden"))}function Ii(e,t){let r=new T;return r.pipe(B(X)).subscribe({next({hidden:o}){o?Yn(e,"hidden"):Pr(e)},complete(){Pr(e)}}),(me("navigation.tabs.sticky")?F({hidden:!1}):os(e,t)).pipe(L(o=>r.next(o)),j(()=>r.complete()),f(o=>P({ref:e},o)))}function ns(e,{viewport$:t,header$:r}){let o=new Map;for(let a of e){let s=decodeURIComponent(a.hash.substring(1)),c=ae(`[id="${s}"]`);typeof c!="undefined"&&o.set(a,c)}let n=r.pipe(f(a=>24+a.height));return Fe(document.body).pipe(V("height"),f(()=>{let a=[];return[...o].reduce((s,[c,l])=>{for(;a.length&&o.get(a[a.length-1]).tagName>=l.tagName;)a.pop();let p=l.offsetTop;for(;!p&&l.parentElement;)l=l.parentElement,p=l.offsetTop;return s.set([...a=[...a,c]].reverse(),p)},new Map)}),f(a=>new Map([...a].sort(([,s],[,c])=>s-c))),M(a=>J([n,t]).pipe(Tr(([s,c],[l,{offset:{y:p}}])=>{for(;c.length;){let[,m]=c[0];if(m-l=p)c=[s.pop(),...c];else break}return[s,c]},[[],[...a]]),D((s,c)=>s[0]===c[0]&&s[1]===c[1])))).pipe(f(([a,s])=>({prev:a.map(([c])=>c),next:s.map(([c])=>c)})),N({prev:[],next:[]}),ge(2,1),f(([a,s])=>a.prev.length{for(let[a]of i)Cn(a),Ln(a);for(let[a,[s]]of n.entries())kn(s,a===n.length-1),An(s,"blur")});let o=z("[href^=\\#]",e);return ns(o,t).pipe(L(n=>r.next(n)),j(()=>r.complete()),f(n=>P({ref:e},n)))}function is(e,{viewport$:t,main$:r}){let o=t.pipe(f(({offset:{y:i}})=>i),ge(2,1),f(([i,a])=>i>a&&a),D()),n=r.pipe(V("active"));return J([n,o]).pipe(f(([{active:i},a])=>({hidden:!(i&&a)})),D((i,a)=>i.hidden===a.hidden))}function $i(e,{viewport$:t,header$:r,main$:o}){let n=new T;return n.pipe(B(X),fe(r.pipe(V("height")))).subscribe({next([{hidden:i},{height:a}]){Xn(e,a+16),i?(Gn(e,"hidden"),ye(e,!1),er(e,-1)):($r(e),gt(e))},complete(){Zn(e),$r(e),gt(e)}}),is(e,{viewport$:t,header$:r,main$:o}).pipe(L(i=>n.next(i)),j(()=>n.complete()),f(i=>P({ref:e},i)))}function Wi({document$:e,tablet$:t}){e.pipe(M(()=>F(...z("[data-md-state=indeterminate]"))),L(r=>{r.indeterminate=!0,r.checked=!1}),oe(r=>w(r,"change").pipe(Lr(()=>r.hasAttribute("data-md-state")),Z(r))),fe(t)).subscribe(([r,o])=>{r.removeAttribute("data-md-state"),o&&(r.checked=!1)})}function as(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(M(()=>F(...z("[data-md-scrollfix]"))),L(t=>t.removeAttribute("data-md-scrollfix")),_(as),oe(t=>w(t,"touchstart").pipe(Z(t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Vi({viewport$:e,tablet$:t}){J([Yt("search"),t]).pipe(f(([r,o])=>r&&!o),M(r=>F(r).pipe(Ae(r?400:100),B(X))),fe(e)).subscribe(([r,{offset:{y:o}}])=>{r?_n(document.body,o):Mn(document.body)})}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=rn(),tr=fn(),zr=vn(),qr=pn(),de=Tn(),rr=vt("(min-width: 960px)"),Ni=vt("(min-width: 1220px)"),Di=xn(),zi=ce(),qi=document.forms.namedItem("search")?(__search==null?void 0:__search.index)||Ee(new URL("search/search_index.json",zi.base)):K,Qr=new T;xi({alert$:Qr});me("navigation.instant")&&gi({document$:ct,location$:tr,viewport$:de});var Ki;((Ki=zi.version)==null?void 0:Ki.provider)==="mike"&&Oi();W(tr,zr).pipe(Ae(125)).subscribe(()=>{Ie("drawer",!1),Ie("search",!1)});qr.pipe(_(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ae("[href][rel=prev]");typeof t!="undefined"&&t.click();break;case"n":case".":let r=ae("[href][rel=next]");typeof r!="undefined"&&r.click();break}});Wi({document$:ct,tablet$:rr});Ui({document$:ct});Vi({viewport$:de,tablet$:rr});var $e=mi(Te("header"),{viewport$:de}),or=ct.pipe(f(()=>Te("main")),M(e=>bi(e,{viewport$:de,header$:$e})),ee(1)),ss=W(...ne("dialog").map(e=>fi(e,{alert$:Qr})),...ne("header").map(e=>di(e,{viewport$:de,header$:$e,main$:or})),...ne("palette").map(e=>vi(e)),...ne("search").map(e=>ki(e,{index$:qi,keyboard$:qr})),...ne("source").map(e=>Fi(e))),cs=Me(()=>W(...ne("content").map(e=>ui(e,{target$:zr,viewport$:de,print$:Di})),...ne("content").map(e=>me("search.highlight")?Ci(e,{index$:qi,location$:tr}):K),...ne("header-title").map(e=>hi(e,{viewport$:de,header$:$e})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Hr(Ni,()=>Dr(e,{viewport$:de,header$:$e,main$:or})):Hr(rr,()=>Dr(e,{viewport$:de,header$:$e,main$:or}))),...ne("tabs").map(e=>Ii(e,{viewport$:de,header$:$e})),...ne("toc").map(e=>Pi(e,{viewport$:de,header$:$e})),...ne("top").map(e=>$i(e,{viewport$:de,header$:$e,main$:or})))),Qi=ct.pipe(M(()=>cs),Ne(ss),ee(1));Qi.subscribe();window.document$=ct;window.location$=tr;window.target$=zr;window.keyboard$=qr;window.viewport$=de;window.tablet$=rr;window.screen$=Ni;window.print$=Di;window.alert$=Qr;window.component$=Qi;})(); +//# sourceMappingURL=bundle.c7d1b464.min.js.map + diff --git a/assets/javascripts/bundle.4fc53ad4.min.js.map b/assets/javascripts/bundle.c7d1b464.min.js.map similarity index 66% rename from assets/javascripts/bundle.4fc53ad4.min.js.map rename to assets/javascripts/bundle.c7d1b464.min.js.map index 19fa4ec42..ce83e1233 100644 --- a/assets/javascripts/bundle.4fc53ad4.min.js.map +++ b/assets/javascripts/bundle.c7d1b464.min.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/rxjs/node_modules/tslib/tslib.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/modules/index.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/caughtSchedule.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/fromArray.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/concatMap.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/sample.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/switchMapTo.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/assets/javascripts/browser/document/index.ts", "src/assets/javascripts/browser/element/_/index.ts", "src/assets/javascripts/browser/element/focus/index.ts", "src/assets/javascripts/browser/element/size/index.ts", "src/assets/javascripts/browser/element/offset/index.ts", "src/assets/javascripts/browser/element/selection/index.ts", "src/assets/javascripts/browser/toggle/index.ts", "src/assets/javascripts/browser/keyboard/index.ts", "src/assets/javascripts/browser/location/_/index.ts", "src/assets/javascripts/utilities/h/index.ts", "src/assets/javascripts/utilities/string/index.ts", "src/assets/javascripts/browser/location/hash/index.ts", "src/assets/javascripts/browser/media/index.ts", "src/assets/javascripts/browser/request/index.ts", "src/assets/javascripts/browser/viewport/offset/index.ts", "src/assets/javascripts/browser/viewport/size/index.ts", "src/assets/javascripts/browser/viewport/_/index.ts", "src/assets/javascripts/browser/worker/index.ts", "src/assets/javascripts/_/index.ts", "src/assets/javascripts/components/_/index.ts", "src/assets/javascripts/components/content/code/index.ts", "src/assets/javascripts/actions/_/index.ts", "src/assets/javascripts/actions/anchor/index.ts", "src/assets/javascripts/actions/dialog/index.ts", "src/assets/javascripts/actions/header/_/index.ts", "src/assets/javascripts/actions/header/title/index.ts", "src/assets/javascripts/actions/search/query/index.ts", "src/assets/javascripts/actions/search/result/index.ts", "src/assets/javascripts/actions/sidebar/index.ts", "src/assets/javascripts/actions/source/index.ts", "src/assets/javascripts/actions/tabs/index.ts", "src/assets/javascripts/actions/top/index.ts", "src/assets/javascripts/templates/clipboard/index.tsx", "src/assets/javascripts/templates/search/index.tsx", "src/assets/javascripts/templates/source/index.tsx", "src/assets/javascripts/templates/table/index.tsx", "src/assets/javascripts/templates/version/index.tsx", "src/assets/javascripts/components/content/details/index.ts", "src/assets/javascripts/components/content/table/index.ts", "src/assets/javascripts/components/content/_/index.ts", "src/assets/javascripts/components/dialog/index.ts", "src/assets/javascripts/components/header/_/index.ts", "src/assets/javascripts/components/header/title/index.ts", "src/assets/javascripts/components/main/index.ts", "src/assets/javascripts/components/palette/index.ts", "src/assets/javascripts/integrations/clipboard/index.ts", "src/assets/javascripts/integrations/instant/index.ts", "src/assets/javascripts/integrations/search/document/index.ts", "src/assets/javascripts/integrations/search/highlighter/index.ts", "src/assets/javascripts/integrations/search/query/transform/index.ts", "src/assets/javascripts/integrations/search/worker/message/index.ts", "src/assets/javascripts/integrations/search/worker/_/index.ts", "src/assets/javascripts/integrations/version/index.ts", "src/assets/javascripts/components/search/query/index.ts", "src/assets/javascripts/components/search/result/index.ts", "src/assets/javascripts/components/search/share/index.ts", "src/assets/javascripts/components/search/suggest/index.ts", "src/assets/javascripts/components/search/_/index.ts", "src/assets/javascripts/components/search/highlight/index.ts", "src/assets/javascripts/components/sidebar/index.ts", "src/assets/javascripts/components/source/facts/github/index.ts", "src/assets/javascripts/components/source/facts/gitlab/index.ts", "src/assets/javascripts/components/source/facts/_/index.ts", "src/assets/javascripts/components/source/_/index.ts", "src/assets/javascripts/components/tabs/index.ts", "src/assets/javascripts/components/toc/index.ts", "src/assets/javascripts/components/top/index.ts", "src/assets/javascripts/patches/indeterminate/index.ts", "src/assets/javascripts/patches/scrollfix/index.ts", "src/assets/javascripts/patches/scrolllock/index.ts"], - "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global global, define, System, Reflect, Promise */\r\nvar __extends;\r\nvar __assign;\r\nvar __rest;\r\nvar __decorate;\r\nvar __param;\r\nvar __metadata;\r\nvar __awaiter;\r\nvar __generator;\r\nvar __exportStar;\r\nvar __values;\r\nvar __read;\r\nvar __spread;\r\nvar __spreadArrays;\r\nvar __spreadArray;\r\nvar __await;\r\nvar __asyncGenerator;\r\nvar __asyncDelegator;\r\nvar __asyncValues;\r\nvar __makeTemplateObject;\r\nvar __importStar;\r\nvar __importDefault;\r\nvar __classPrivateFieldGet;\r\nvar __classPrivateFieldSet;\r\nvar __createBinding;\r\n(function (factory) {\r\n var root = typeof global === \"object\" ? global : typeof self === \"object\" ? self : typeof this === \"object\" ? this : {};\r\n if (typeof define === \"function\" && define.amd) {\r\n define(\"tslib\", [\"exports\"], function (exports) { factory(createExporter(root, createExporter(exports))); });\r\n }\r\n else if (typeof module === \"object\" && typeof module.exports === \"object\") {\r\n factory(createExporter(root, createExporter(module.exports)));\r\n }\r\n else {\r\n factory(createExporter(root));\r\n }\r\n function createExporter(exports, previous) {\r\n if (exports !== root) {\r\n if (typeof Object.create === \"function\") {\r\n Object.defineProperty(exports, \"__esModule\", { value: true });\r\n }\r\n else {\r\n exports.__esModule = true;\r\n }\r\n }\r\n return function (id, v) { return exports[id] = previous ? previous(id, v) : v; };\r\n }\r\n})\r\n(function (exporter) {\r\n var extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n\r\n __extends = function (d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n };\r\n\r\n __assign = Object.assign || function (t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n };\r\n\r\n __rest = function (s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n };\r\n\r\n __decorate = function (decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n };\r\n\r\n __param = function (paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n };\r\n\r\n __metadata = function (metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n };\r\n\r\n __awaiter = function (thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n };\r\n\r\n __generator = function (thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n };\r\n\r\n __exportStar = function(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n };\r\n\r\n __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n }) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n });\r\n\r\n __values = function (o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n };\r\n\r\n __read = function (o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n };\r\n\r\n /** @deprecated */\r\n __spread = function () {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n };\r\n\r\n /** @deprecated */\r\n __spreadArrays = function () {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n };\r\n\r\n __spreadArray = function (to, from) {\r\n for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)\r\n to[j] = from[i];\r\n return to;\r\n };\r\n\r\n __await = function (v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n };\r\n\r\n __asyncGenerator = function (thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n };\r\n\r\n __asyncDelegator = function (o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n };\r\n\r\n __asyncValues = function (o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n };\r\n\r\n __makeTemplateObject = function (cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n };\r\n\r\n var __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n }) : function(o, v) {\r\n o[\"default\"] = v;\r\n };\r\n\r\n __importStar = function (mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n };\r\n\r\n __importDefault = function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n };\r\n\r\n __classPrivateFieldGet = function (receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n };\r\n\r\n __classPrivateFieldSet = function (receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n };\r\n\r\n exporter(\"__extends\", __extends);\r\n exporter(\"__assign\", __assign);\r\n exporter(\"__rest\", __rest);\r\n exporter(\"__decorate\", __decorate);\r\n exporter(\"__param\", __param);\r\n exporter(\"__metadata\", __metadata);\r\n exporter(\"__awaiter\", __awaiter);\r\n exporter(\"__generator\", __generator);\r\n exporter(\"__exportStar\", __exportStar);\r\n exporter(\"__createBinding\", __createBinding);\r\n exporter(\"__values\", __values);\r\n exporter(\"__read\", __read);\r\n exporter(\"__spread\", __spread);\r\n exporter(\"__spreadArrays\", __spreadArrays);\r\n exporter(\"__spreadArray\", __spreadArray);\r\n exporter(\"__await\", __await);\r\n exporter(\"__asyncGenerator\", __asyncGenerator);\r\n exporter(\"__asyncDelegator\", __asyncDelegator);\r\n exporter(\"__asyncValues\", __asyncValues);\r\n exporter(\"__makeTemplateObject\", __makeTemplateObject);\r\n exporter(\"__importStar\", __importStar);\r\n exporter(\"__importDefault\", __importDefault);\r\n exporter(\"__classPrivateFieldGet\", __classPrivateFieldGet);\r\n exporter(\"__classPrivateFieldSet\", __classPrivateFieldSet);\r\n});\r\n", "/*!\n * clipboard.js v2.0.8\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 134:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/clipboard-action.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _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); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n/**\n * Inner class which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n */\n\nvar ClipboardAction = /*#__PURE__*/function () {\n /**\n * @param {Object} options\n */\n function ClipboardAction(options) {\n _classCallCheck(this, ClipboardAction);\n\n this.resolveOptions(options);\n this.initSelection();\n }\n /**\n * Defines base properties passed from constructor.\n * @param {Object} options\n */\n\n\n _createClass(ClipboardAction, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = options.action;\n this.container = options.container;\n this.emitter = options.emitter;\n this.target = options.target;\n this.text = options.text;\n this.trigger = options.trigger;\n this.selectedText = '';\n }\n /**\n * Decides which selection strategy is going to be applied based\n * on the existence of `text` and `target` properties.\n */\n\n }, {\n key: \"initSelection\",\n value: function initSelection() {\n if (this.text) {\n this.selectFake();\n } else if (this.target) {\n this.selectTarget();\n }\n }\n /**\n * Creates a fake textarea element, sets its value from `text` property,\n */\n\n }, {\n key: \"createFakeElement\",\n value: function createFakeElement() {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n this.fakeElem = document.createElement('textarea'); // Prevent zooming on iOS\n\n this.fakeElem.style.fontSize = '12pt'; // Reset box model\n\n this.fakeElem.style.border = '0';\n this.fakeElem.style.padding = '0';\n this.fakeElem.style.margin = '0'; // Move element out of screen horizontally\n\n this.fakeElem.style.position = 'absolute';\n this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n this.fakeElem.style.top = \"\".concat(yPosition, \"px\");\n this.fakeElem.setAttribute('readonly', '');\n this.fakeElem.value = this.text;\n return this.fakeElem;\n }\n /**\n * Get's the value of fakeElem,\n * and makes a selection on it.\n */\n\n }, {\n key: \"selectFake\",\n value: function selectFake() {\n var _this = this;\n\n var fakeElem = this.createFakeElement();\n\n this.fakeHandlerCallback = function () {\n return _this.removeFake();\n };\n\n this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;\n this.container.appendChild(fakeElem);\n this.selectedText = select_default()(fakeElem);\n this.copyText();\n this.removeFake();\n }\n /**\n * Only removes the fake element after another click event, that way\n * a user can hit `Ctrl+C` to copy because selection still exists.\n */\n\n }, {\n key: \"removeFake\",\n value: function removeFake() {\n if (this.fakeHandler) {\n this.container.removeEventListener('click', this.fakeHandlerCallback);\n this.fakeHandler = null;\n this.fakeHandlerCallback = null;\n }\n\n if (this.fakeElem) {\n this.container.removeChild(this.fakeElem);\n this.fakeElem = null;\n }\n }\n /**\n * Selects the content from element passed on `target` property.\n */\n\n }, {\n key: \"selectTarget\",\n value: function selectTarget() {\n this.selectedText = select_default()(this.target);\n this.copyText();\n }\n /**\n * Executes the copy operation based on the current selection.\n */\n\n }, {\n key: \"copyText\",\n value: function copyText() {\n var succeeded;\n\n try {\n succeeded = document.execCommand(this.action);\n } catch (err) {\n succeeded = false;\n }\n\n this.handleResult(succeeded);\n }\n /**\n * Fires an event based on the copy operation result.\n * @param {Boolean} succeeded\n */\n\n }, {\n key: \"handleResult\",\n value: function handleResult(succeeded) {\n this.emitter.emit(succeeded ? 'success' : 'error', {\n action: this.action,\n text: this.selectedText,\n trigger: this.trigger,\n clearSelection: this.clearSelection.bind(this)\n });\n }\n /**\n * Moves focus away from `target` and back to the trigger, removes current selection.\n */\n\n }, {\n key: \"clearSelection\",\n value: function clearSelection() {\n if (this.trigger) {\n this.trigger.focus();\n }\n\n document.activeElement.blur();\n window.getSelection().removeAllRanges();\n }\n /**\n * Sets the `action` to be performed which can be either 'copy' or 'cut'.\n * @param {String} action\n */\n\n }, {\n key: \"destroy\",\n\n /**\n * Destroy lifecycle.\n */\n value: function destroy() {\n this.removeFake();\n }\n }, {\n key: \"action\",\n set: function set() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';\n this._action = action;\n\n if (this._action !== 'copy' && this._action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n }\n }\n /**\n * Gets the `action` property.\n * @return {String}\n */\n ,\n get: function get() {\n return this._action;\n }\n /**\n * Sets the `target` property using an element\n * that will be have its content copied.\n * @param {Element} target\n */\n\n }, {\n key: \"target\",\n set: function set(target) {\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (this.action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n\n this._target = target;\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n }\n }\n /**\n * Gets the `target` property.\n * @return {String|HTMLElement}\n */\n ,\n get: function get() {\n return this._target;\n }\n }]);\n\n return ClipboardAction;\n}();\n\n/* harmony default export */ var clipboard_action = (ClipboardAction);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction clipboard_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction clipboard_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); } }\n\nfunction clipboard_createClass(Constructor, protoProps, staticProps) { if (protoProps) clipboard_defineProperties(Constructor.prototype, protoProps); if (staticProps) clipboard_defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n clipboard_classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n clipboard_createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n\n if (this.clipboardAction) {\n this.clipboardAction = null;\n }\n\n this.clipboardAction = new clipboard_action({\n action: this.action(trigger),\n target: this.target(trigger),\n text: this.text(trigger),\n container: this.container,\n trigger: trigger,\n emitter: this\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n\n if (this.clipboardAction) {\n this.clipboardAction.destroy();\n this.clipboardAction = null;\n }\n }\n }], [{\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(134);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\nimport { NEVER, Subject, defer, merge } from \"rxjs\"\nimport {\n delay,\n filter,\n map,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs/operators\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getElement,\n requestJSON,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountBackToTop,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantLoading,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget()\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? __search?.index || requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up instant loading, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantLoading({ document$, location$, viewport$ })\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector()\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getElement(\"[href][rel=prev]\")\n if (typeof prev !== \"undefined\")\n prev.click()\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getElement(\"[href][rel=next]\")\n if (typeof next !== \"undefined\")\n next.click()\n break\n }\n })\n\n/* Set up patches */\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { target$, viewport$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : NEVER\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, { viewport$, header$ })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Tablet observable */\nwindow.screen$ = screen$ /* Screen observable */\nwindow.print$ = print$ /* Print mode observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.component$ = component$ /* Component observable */\n", "import tslib from '../tslib.js';\r\nconst {\r\n __extends,\r\n __assign,\r\n __rest,\r\n __decorate,\r\n __param,\r\n __metadata,\r\n __awaiter,\r\n __generator,\r\n __exportStar,\r\n __createBinding,\r\n __values,\r\n __read,\r\n __spread,\r\n __spreadArrays,\r\n __spreadArray,\r\n __await,\r\n __asyncGenerator,\r\n __asyncDelegator,\r\n __asyncValues,\r\n __makeTemplateObject,\r\n __importStar,\r\n __importDefault,\r\n __classPrivateFieldGet,\r\n __classPrivateFieldSet,\r\n} = tslib;\r\nexport {\r\n __extends,\r\n __assign,\r\n __rest,\r\n __decorate,\r\n __param,\r\n __metadata,\r\n __awaiter,\r\n __generator,\r\n __exportStar,\r\n __createBinding,\r\n __values,\r\n __read,\r\n __spread,\r\n __spreadArrays,\r\n __spreadArray,\r\n __await,\r\n __asyncGenerator,\r\n __asyncDelegator,\r\n __asyncValues,\r\n __makeTemplateObject,\r\n __importStar,\r\n __importDefault,\r\n __classPrivateFieldGet,\r\n __classPrivateFieldSet,\r\n};\r\n", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ReplaySubject, Subject, fromEvent } from \"rxjs\"\nimport { mapTo } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch document\n *\n * Documents are implemented as subjects, so all downstream observables are\n * automatically updated when a new document is emitted.\n *\n * @returns Document subject\n */\nexport function watchDocument(): Subject {\n const document$ = new ReplaySubject()\n fromEvent(document, \"DOMContentLoaded\")\n .pipe(\n mapTo(document)\n )\n .subscribe(document$)\n\n /* Return document */\n return document$\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve an element matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Element or nothing\n */\nexport function getElement(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T] | undefined\n\nexport function getElement(\n selector: string, node?: ParentNode\n): T | undefined\n\nexport function getElement(\n selector: string, node: ParentNode = document\n): T | undefined {\n return node.querySelector(selector) || undefined\n}\n\n/**\n * Retrieve an element matching a query selector or throw a reference error\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Element\n */\nexport function getElementOrThrow(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T]\n\nexport function getElementOrThrow(\n selector: string, node?: ParentNode\n): T\n\nexport function getElementOrThrow(\n selector: string, node: ParentNode = document\n): T {\n const el = getElement(selector, node)\n if (typeof el === \"undefined\")\n throw new ReferenceError(\n `Missing element: expected \"${selector}\" to be present`\n )\n\n /* Return element */\n return el\n}\n\n/**\n * Retrieve the currently active element\n *\n * @returns Element or nothing\n */\nexport function getActiveElement(): HTMLElement | undefined {\n return document.activeElement instanceof HTMLElement\n ? document.activeElement\n : undefined\n}\n\n/**\n * Retrieve all elements matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Elements\n */\nexport function getElements(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T][]\n\nexport function getElements(\n selector: string, node?: ParentNode\n): T[]\n\nexport function getElements(\n selector: string, node: ParentNode = document\n): T[] {\n return Array.from(node.querySelectorAll(selector))\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Replace an element with the given list of nodes\n *\n * @param el - Element\n * @param nodes - Replacement nodes\n */\nexport function replaceElement(\n el: HTMLElement, ...nodes: Node[]\n): void {\n el.replaceWith(...nodes)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getActiveElement } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element focus\n *\n * @param el - Element\n * @param value - Whether the element should be focused\n */\nexport function setElementFocus(\n el: HTMLElement, value = true\n): void {\n if (value)\n el.focus()\n else\n el.blur()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element focus\n *\n * @param el - Element\n *\n * @returns Element focus observable\n */\nexport function watchElementFocus(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"focus\"),\n fromEvent(el, \"blur\")\n )\n .pipe(\n map(({ type }) => type === \"focus\"),\n startWith(el === getActiveElement())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n Subject,\n defer,\n of\n} from \"rxjs\"\nimport {\n filter,\n finalize,\n map,\n shareReplay,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementSize {\n width: number /* Element width */\n height: number /* Element height */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Resize observer entry subject\n */\nconst entry$ = new Subject()\n\n/**\n * Resize observer observable\n *\n * This observable will create a `ResizeObserver` on the first subscription\n * and will automatically terminate it when there are no more subscribers.\n * It's quite important to centralize observation in a single `ResizeObserver`,\n * as the performance difference can be quite dramatic, as the link shows.\n *\n * @see https://bit.ly/3iIYfEm - Google Groups on performance\n */\nconst observer$ = defer(() => of(\n new ResizeObserver(entries => {\n for (const entry of entries)\n entry$.next(entry)\n })\n))\n .pipe(\n switchMap(resize => NEVER.pipe(startWith(resize))\n .pipe(\n finalize(() => resize.disconnect())\n )\n ),\n shareReplay(1)\n )\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element size\n *\n * @param el - Element\n *\n * @returns Element size\n */\nexport function getElementSize(el: HTMLElement): ElementSize {\n return {\n width: el.offsetWidth,\n height: el.offsetHeight\n }\n}\n\n/**\n * Retrieve element content size, i.e. including overflowing content\n *\n * @param el - Element\n *\n * @returns Element size\n */\nexport function getElementContentSize(el: HTMLElement): ElementSize {\n return {\n width: el.scrollWidth,\n height: el.scrollHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element size\n *\n * This function returns an observable that subscribes to a single internal\n * instance of `ResizeObserver` upon subscription, and emit resize events until\n * termination. Note that this function should not be called with the same\n * element twice, as the first unsubscription will terminate observation.\n *\n * Sadly, we can't use the `DOMRect` objects returned by the observer, because\n * we need the emitted values to be consistent with `getElementSize`, which will\n * return the used values (rounded) and not actual values (unrounded). Thus, we\n * use the `offset*` properties. See the linked GitHub issue.\n *\n * @see https://bit.ly/3m0k3he - GitHub issue\n *\n * @param el - Element\n *\n * @returns Element size observable\n */\nexport function watchElementSize(\n el: HTMLElement\n): Observable {\n return observer$\n .pipe(\n tap(observer => observer.observe(el)),\n switchMap(observer => entry$\n .pipe(\n filter(({ target }) => target === el),\n finalize(() => observer.unobserve(el)),\n map(() => getElementSize(el))\n )\n ),\n startWith(getElementSize(el))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport {\n distinctUntilChanged,\n map,\n startWith\n} from \"rxjs/operators\"\n\nimport {\n getElementContentSize,\n getElementSize\n} from \"../size\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element offset\n *\n * @param el - Element\n *\n * @returns Element offset\n */\nexport function getElementOffset(el: HTMLElement): ElementOffset {\n return {\n x: el.scrollLeft,\n y: el.scrollTop\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element offset\n *\n * @param el - Element\n *\n * @returns Element offset observable\n */\nexport function watchElementOffset(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"scroll\"),\n fromEvent(window, \"resize\")\n )\n .pipe(\n map(() => getElementOffset(el)),\n startWith(getElementOffset(el))\n )\n}\n\n/**\n * Watch element threshold\n *\n * This function returns an observable which emits whether the bottom scroll\n * offset of an elements is within a certain threshold.\n *\n * @param el - Element\n * @param threshold - Threshold\n *\n * @returns Element threshold observable\n */\nexport function watchElementThreshold(\n el: HTMLElement, threshold = 16\n): Observable {\n return watchElementOffset(el)\n .pipe(\n map(({ y }) => {\n const visible = getElementSize(el)\n const content = getElementContentSize(el)\n return y >= (\n content.height - visible.height - threshold\n )\n }),\n distinctUntilChanged()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element text selection\n *\n * @param el - Element\n */\nexport function setElementSelection(\n el: HTMLElement\n): void {\n if (el instanceof HTMLInputElement)\n el.select()\n else\n throw new Error(\"Not implemented\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getElementOrThrow } from \"../element\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle\n */\nexport type Toggle =\n | \"drawer\" /* Toggle for drawer */\n | \"search\" /* Toggle for search */\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle map\n */\nconst toggles: Record = {\n drawer: getElementOrThrow(\"[data-md-toggle=drawer]\"),\n search: getElementOrThrow(\"[data-md-toggle=search]\")\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve the value of a toggle\n *\n * @param name - Toggle\n *\n * @returns Toggle value\n */\nexport function getToggle(name: Toggle): boolean {\n return toggles[name].checked\n}\n\n/**\n * Set toggle\n *\n * Simulating a click event seems to be the most cross-browser compatible way\n * of changing the value while also emitting a `change` event. Before, Material\n * used `CustomEvent` to programmatically change the value of a toggle, but this\n * is a much simpler and cleaner solution which doesn't require a polyfill.\n *\n * @param name - Toggle\n * @param value - Toggle value\n */\nexport function setToggle(name: Toggle, value: boolean): void {\n if (toggles[name].checked !== value)\n toggles[name].click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch toggle\n *\n * @param name - Toggle\n *\n * @returns Toggle value observable\n */\nexport function watchToggle(name: Toggle): Observable {\n const el = toggles[name]\n return fromEvent(el, \"change\")\n .pipe(\n map(() => el.checked),\n startWith(el.checked)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { filter, map, share } from \"rxjs/operators\"\n\nimport { getActiveElement } from \"../element\"\nimport { getToggle } from \"../toggle\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Keyboard mode\n */\nexport type KeyboardMode =\n | \"global\" /* Global */\n | \"search\" /* Search is open */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Keyboard\n */\nexport interface Keyboard {\n mode: KeyboardMode /* Keyboard mode */\n type: string /* Key type */\n claim(): void /* Key claim */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether an element may receive keyboard input\n *\n * @param el - Element\n *\n * @returns Test result\n */\nfunction isSusceptibleToKeyboard(el: HTMLElement): boolean {\n switch (el.tagName) {\n\n /* Form elements */\n case \"INPUT\":\n case \"SELECT\":\n case \"TEXTAREA\":\n return true\n\n /* Everything else */\n default:\n return el.isContentEditable\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch keyboard\n *\n * @returns Keyboard observable\n */\nexport function watchKeyboard(): Observable {\n return fromEvent(window, \"keydown\")\n .pipe(\n filter(ev => !(ev.metaKey || ev.ctrlKey)),\n map(ev => ({\n mode: getToggle(\"search\") ? \"search\" : \"global\",\n type: ev.key,\n claim() {\n ev.preventDefault()\n ev.stopPropagation()\n }\n } as Keyboard)),\n filter(({ mode }) => {\n if (mode === \"global\") {\n const active = getActiveElement()\n if (typeof active !== \"undefined\")\n return !isSusceptibleToKeyboard(active)\n }\n return true\n }),\n share()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Subject } from \"rxjs\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location\n *\n * This function returns a `URL` object (and not `Location`) to normalize the\n * typings across the application. Furthermore, locations need to be tracked\n * without setting them and `Location` is a singleton which represents the\n * current location.\n *\n * @returns URL\n */\nexport function getLocation(): URL {\n return new URL(location.href)\n}\n\n/**\n * Set location\n *\n * @param url - URL to change to\n */\nexport function setLocation(url: URL): void {\n location.href = url.href\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location\n *\n * @returns Location subject\n */\nexport function watchLocation(): Subject {\n return new Subject()\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { JSX as JSXInternal } from \"preact\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * HTML attributes\n */\ntype Attributes =\n & JSXInternal.HTMLAttributes\n & JSXInternal.SVGAttributes\n & Record\n\n/**\n * Child element\n */\ntype Child =\n | HTMLElement\n | Text\n | string\n | number\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Append a child node to an element\n *\n * @param el - Element\n * @param child - Child node(s)\n */\nfunction appendChild(el: HTMLElement, child: Child | Child[]): void {\n\n /* Handle primitive types (including raw HTML) */\n if (typeof child === \"string\" || typeof child === \"number\") {\n el.innerHTML += child.toString()\n\n /* Handle nodes */\n } else if (child instanceof Node) {\n el.appendChild(child)\n\n /* Handle nested children */\n } else if (Array.isArray(child)) {\n for (const node of child)\n appendChild(el, node)\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * JSX factory\n *\n * @template T - Element type\n *\n * @param tag - HTML tag\n * @param attributes - HTML attributes\n * @param children - Child elements\n *\n * @returns Element\n */\nexport function h(\n tag: T, attributes?: Attributes | null, ...children: Child[]\n): HTMLElementTagNameMap[T]\n\nexport function h(\n tag: string, attributes?: Attributes | null, ...children: Child[]\n): T\n\nexport function h(\n tag: string, attributes?: Attributes | null, ...children: Child[]\n): T {\n const el = document.createElement(tag)\n\n /* Set attributes, if any */\n if (attributes)\n for (const attr of Object.keys(attributes))\n if (typeof attributes[attr] !== \"boolean\")\n el.setAttribute(attr, attributes[attr])\n else if (attributes[attr])\n el.setAttribute(attr, \"\")\n\n /* Append child nodes */\n for (const child of children)\n appendChild(el, child)\n\n /* Return element */\n return el as T\n}\n\n/* ----------------------------------------------------------------------------\n * Namespace\n * ------------------------------------------------------------------------- */\n\nexport declare namespace h {\n namespace JSX {\n type Element = HTMLElement\n type IntrinsicElements = JSXInternal.IntrinsicElements\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Truncate a string after the given number of characters\n *\n * This is not a very reasonable approach, since the summaries kind of suck.\n * It would be better to create something more intelligent, highlighting the\n * search occurrences and making a better summary out of it, but this note was\n * written three years ago, so who knows if we'll ever fix it.\n *\n * @param value - Value to be truncated\n * @param n - Number of characters\n *\n * @returns Truncated value\n */\nexport function truncate(value: string, n: number): string {\n let i = n\n if (value.length > i) {\n while (value[i] !== \" \" && --i > 0) { /* keep eating */ }\n return `${value.substring(0, i)}...`\n }\n return value\n}\n\n/**\n * Round a number for display with repository facts\n *\n * This is a reverse-engineered version of GitHub's weird rounding algorithm\n * for stars, forks and all other numbers. While all numbers below `1,000` are\n * returned as-is, bigger numbers are converted to fixed numbers:\n *\n * - `1,049` => `1k`\n * - `1,050` => `1.1k`\n * - `1,949` => `1.9k`\n * - `1,950` => `2k`\n *\n * @param value - Original value\n *\n * @returns Rounded value\n */\nexport function round(value: number): string {\n if (value > 999) {\n const digits = +((value - 950) % 1000 > 99)\n return `${((value + 0.000001) / 1000).toFixed(digits)}k`\n } else {\n return value.toString()\n }\n}\n\n/**\n * Simple hash function\n *\n * @see https://bit.ly/2wsVjJ4 - Original source\n *\n * @param value - Value to be hashed\n *\n * @returns Hash as 32bit integer\n */\nexport function hash(value: string): number {\n let h = 0\n for (let i = 0, len = value.length; i < len; i++) {\n h = ((h << 5) - h) + value.charCodeAt(i)\n h |= 0 // Convert to 32bit integer\n }\n return h\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport {\n filter,\n map,\n shareReplay,\n startWith\n} from \"rxjs/operators\"\n\nimport { getElement } from \"~/browser\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location hash\n *\n * @returns Location hash\n */\nexport function getLocationHash(): string {\n return location.hash.substring(1)\n}\n\n/**\n * Set location hash\n *\n * Setting a new fragment identifier via `location.hash` will have no effect\n * if the value doesn't change. When a new fragment identifier is set, we want\n * the browser to target the respective element at all times, which is why we\n * use this dirty little trick.\n *\n * @param hash - Location hash\n */\nexport function setLocationHash(hash: string): void {\n const el = h(\"a\", { href: hash })\n el.addEventListener(\"click\", ev => ev.stopPropagation())\n el.click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location hash\n *\n * @returns Location hash observable\n */\nexport function watchLocationHash(): Observable {\n return fromEvent(window, \"hashchange\")\n .pipe(\n map(getLocationHash),\n startWith(getLocationHash()),\n filter(hash => hash.length > 0),\n shareReplay(1)\n )\n}\n\n/**\n * Watch location target\n *\n * @returns Location target observable\n */\nexport function watchLocationTarget(): Observable {\n return watchLocationHash()\n .pipe(\n map(id => getElement(`[id=\"${id}\"]`)!),\n filter(el => typeof el !== \"undefined\")\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n fromEvent,\n fromEventPattern\n} from \"rxjs\"\nimport {\n mapTo,\n startWith,\n switchMap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch media query\n *\n * Note that although `MediaQueryList.addListener` is deprecated we have to\n * use it, because it's the only way to ensure proper downward compatibility.\n *\n * @see https://bit.ly/3dUBH2m - GitHub issue\n *\n * @param query - Media query\n *\n * @returns Media observable\n */\nexport function watchMedia(query: string): Observable {\n const media = matchMedia(query)\n return fromEventPattern(next => (\n media.addListener(() => next(media.matches))\n ))\n .pipe(\n startWith(media.matches)\n )\n}\n\n/**\n * Watch print mode, cross-browser\n *\n * @returns Print mode observable\n */\nexport function watchPrint(): Observable {\n return fromEvent(window, \"beforeprint\")\n .pipe(\n mapTo(undefined)\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Toggle an observable with a media observable\n *\n * @template T - Data type\n *\n * @param query$ - Media observable\n * @param factory - Observable factory\n *\n * @returns Toggled observable\n */\nexport function at(\n query$: Observable, factory: () => Observable\n): Observable {\n return query$\n .pipe(\n switchMap(active => active ? factory() : NEVER)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, from } from \"rxjs\"\nimport {\n filter,\n map,\n shareReplay,\n switchMap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch the given URL\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Response observable\n */\nexport function request(\n url: URL | string, options: RequestInit = { credentials: \"same-origin\" }\n): Observable {\n return from(fetch(`${url}`, options))\n .pipe(\n filter(res => res.status === 200),\n )\n}\n\n/**\n * Fetch JSON from the given URL\n *\n * @template T - Data type\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Data observable\n */\nexport function requestJSON(\n url: URL | string, options?: RequestInit\n): Observable {\n return request(url, options)\n .pipe(\n switchMap(res => res.json()),\n shareReplay(1)\n )\n}\n\n/**\n * Fetch XML from the given URL\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Data observable\n */\nexport function requestXML(\n url: URL | string, options?: RequestInit\n): Observable {\n const dom = new DOMParser()\n return request(url, options)\n .pipe(\n switchMap(res => res.text()),\n map(res => dom.parseFromString(res, \"text/xml\")),\n shareReplay(1)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport offset\n */\nexport interface ViewportOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport offset\n *\n * On iOS Safari, viewport offset can be negative due to overflow scrolling.\n * As this may induce strange behaviors downstream, we'll just limit it to 0.\n *\n * @returns Viewport offset\n */\nexport function getViewportOffset(): ViewportOffset {\n return {\n x: Math.max(0, pageXOffset),\n y: Math.max(0, pageYOffset)\n }\n}\n\n/**\n * Set viewport offset\n *\n * @param offset - Viewport offset\n */\nexport function setViewportOffset(\n { x, y }: Partial\n): void {\n window.scrollTo(x || 0, y || 0)\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport offset\n *\n * @returns Viewport offset observable\n */\nexport function watchViewportOffset(): Observable {\n return merge(\n fromEvent(window, \"scroll\", { passive: true }),\n fromEvent(window, \"resize\", { passive: true })\n )\n .pipe(\n map(getViewportOffset),\n startWith(getViewportOffset())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport size\n */\nexport interface ViewportSize {\n width: number /* Viewport width */\n height: number /* Viewport height */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport size\n *\n * @returns Viewport size\n */\nexport function getViewportSize(): ViewportSize {\n return {\n width: innerWidth,\n height: innerHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport size\n *\n * @returns Viewport size observable\n */\nexport function watchViewportSize(): Observable {\n return fromEvent(window, \"resize\", { passive: true })\n .pipe(\n map(getViewportSize),\n startWith(getViewportSize())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, combineLatest } from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n map,\n shareReplay\n} from \"rxjs/operators\"\n\nimport { Header } from \"~/components\"\n\nimport {\n ViewportOffset,\n watchViewportOffset\n} from \"../offset\"\nimport {\n ViewportSize,\n watchViewportSize\n} from \"../size\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport\n */\nexport interface Viewport {\n offset: ViewportOffset /* Viewport offset */\n size: ViewportSize /* Viewport size */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch at options\n */\ninterface WatchAtOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport\n *\n * @returns Viewport observable\n */\nexport function watchViewport(): Observable {\n return combineLatest([\n watchViewportOffset(),\n watchViewportSize()\n ])\n .pipe(\n map(([offset, size]) => ({ offset, size })),\n shareReplay(1)\n )\n}\n\n/**\n * Watch viewport relative to element\n *\n * @param el - Element\n * @param options - Options\n *\n * @returns Viewport observable\n */\nexport function watchViewportAt(\n el: HTMLElement, { viewport$, header$ }: WatchAtOptions\n): Observable {\n const size$ = viewport$\n .pipe(\n distinctUntilKeyChanged(\"size\")\n )\n\n /* Compute element offset */\n const offset$ = combineLatest([size$, header$])\n .pipe(\n map((): ViewportOffset => ({\n x: el.offsetLeft,\n y: el.offsetTop\n }))\n )\n\n /* Compute relative viewport, return hot observable */\n return combineLatest([header$, viewport$, offset$])\n .pipe(\n map(([{ height }, { offset, size }, { x, y }]) => ({\n offset: {\n x: offset.x - x,\n y: offset.y - y + height\n },\n size\n }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject, fromEvent } from \"rxjs\"\nimport {\n map,\n share,\n switchMapTo,\n tap,\n throttle\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Worker message\n */\nexport interface WorkerMessage {\n type: unknown /* Message type */\n data?: unknown /* Message data */\n}\n\n/**\n * Worker handler\n *\n * @template T - Message type\n */\nexport interface WorkerHandler<\n T extends WorkerMessage\n> {\n tx$: Subject /* Message transmission subject */\n rx$: Observable /* Message receive observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n *\n * @template T - Worker message type\n */\ninterface WatchOptions {\n tx$: Observable /* Message transmission observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch a web worker\n *\n * This function returns an observable that sends all values emitted by the\n * message observable to the web worker. Web worker communication is expected\n * to be bidirectional (request-response) and synchronous. Messages that are\n * emitted during a pending request are throttled, the last one is emitted.\n *\n * @param worker - Web worker\n * @param options - Options\n *\n * @returns Worker message observable\n */\nexport function watchWorker(\n worker: Worker, { tx$ }: WatchOptions\n): Observable {\n\n /* Intercept messages from worker-like objects */\n const rx$ = fromEvent(worker, \"message\")\n .pipe(\n map(({ data }) => data as T)\n )\n\n /* Send and receive messages, return hot observable */\n return tx$\n .pipe(\n throttle(() => rx$, { leading: true, trailing: true }),\n tap(message => worker.postMessage(message)),\n switchMapTo(rx$),\n share()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { getElementOrThrow, getLocation } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Feature flag\n */\nexport type Flag =\n | \"header.autohide\" /* Hide header */\n | \"navigation.expand\" /* Automatic expansion */\n | \"navigation.instant\" /* Instant loading */\n | \"navigation.indexes\" /* Section pages */\n | \"navigation.sections\" /* Section navigation */\n | \"navigation.tabs\" /* Tabs navigation */\n | \"navigation.tabs.sticky\" /* Tabs navigation (sticky) */\n | \"navigation.top\" /* Back-to-top button */\n | \"search.highlight\" /* Search highlighting */\n | \"search.share\" /* Search sharing */\n | \"search.suggest\" /* Search suggestions */\n | \"toc.integrate\" /* Integrated table of contents */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Translation\n */\nexport type Translation =\n | \"clipboard.copy\" /* Copy to clipboard */\n | \"clipboard.copied\" /* Copied to clipboard */\n | \"search.config.lang\" /* Search language */\n | \"search.config.pipeline\" /* Search pipeline */\n | \"search.config.separator\" /* Search separator */\n | \"search.placeholder\" /* Search */\n | \"search.result.placeholder\" /* Type to start searching */\n | \"search.result.none\" /* No matching documents */\n | \"search.result.one\" /* 1 matching document */\n | \"search.result.other\" /* # matching documents */\n | \"search.result.more.one\" /* 1 more on this page */\n | \"search.result.more.other\" /* # more on this page */\n | \"search.result.term.missing\" /* Missing */\n | \"select.version.title\" /* Version selector */\n\n/**\n * Translations\n */\nexport type Translations = Record\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Versioning\n */\nexport interface Versioning {\n provider: \"mike\" /* Version provider */\n}\n\n/**\n * Configuration\n */\nexport interface Config {\n base: string /* Base URL */\n features: Flag[] /* Feature flags */\n translations: Translations /* Translations */\n search: string /* Search worker URL */\n version?: Versioning /* Versioning */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve global configuration and make base URL absolute\n */\nconst script = getElementOrThrow(\"#__config\")\nconst config: Config = JSON.parse(script.textContent!)\nconfig.base = `${new URL(config.base, getLocation())}`\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve global configuration\n *\n * @returns Global configuration\n */\nexport function configuration(): Config {\n return config\n}\n\n/**\n * Check whether a feature flag is enabled\n *\n * @param flag - Feature flag\n *\n * @returns Test result\n */\nexport function feature(flag: Flag): boolean {\n return config.features.includes(flag)\n}\n\n/**\n * Retrieve the translation for the given key\n *\n * @param key - Key to be translated\n * @param value - Positional value, if any\n *\n * @returns Translation\n */\nexport function translation(\n key: Translation, value?: string | number\n): string {\n return typeof value !== \"undefined\"\n ? config.translations[key].replace(\"#\", value.toString())\n : config.translations[key]\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { getElementOrThrow, getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Component type\n */\nexport type ComponentType =\n | \"announce\" /* Announcement bar */\n | \"container\" /* Container */\n | \"content\" /* Content */\n | \"dialog\" /* Dialog */\n | \"header\" /* Header */\n | \"header-title\" /* Header title */\n | \"header-topic\" /* Header topic */\n | \"main\" /* Main area */\n | \"palette\" /* Color palette */\n | \"search\" /* Search */\n | \"search-query\" /* Search input */\n | \"search-result\" /* Search results */\n | \"search-share\" /* Search sharing */\n | \"search-suggest\" /* Search suggestions */\n | \"sidebar\" /* Sidebar */\n | \"skip\" /* Skip link */\n | \"source\" /* Repository information */\n | \"tabs\" /* Navigation tabs */\n | \"toc\" /* Table of contents */\n | \"top\" /* Back-to-top button */\n\n/**\n * Component\n *\n * @template T - Component type\n * @template U - Reference type\n */\nexport type Component<\n T extends {} = {},\n U extends HTMLElement = HTMLElement\n> =\n T & {\n ref: U /* Component reference */\n }\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Component type map\n */\ninterface ComponentTypeMap {\n \"announce\": HTMLElement /* Announcement bar */\n \"container\": HTMLElement /* Container */\n \"content\": HTMLElement /* Content */\n \"dialog\": HTMLElement /* Dialog */\n \"header\": HTMLElement /* Header */\n \"header-title\": HTMLElement /* Header title */\n \"header-topic\": HTMLElement /* Header topic */\n \"main\": HTMLElement /* Main area */\n \"palette\": HTMLElement /* Color palette */\n \"search\": HTMLElement /* Search */\n \"search-query\": HTMLInputElement /* Search input */\n \"search-result\": HTMLElement /* Search results */\n \"search-share\": HTMLAnchorElement /* Search sharing */\n \"search-suggest\": HTMLElement /* Search suggestions */\n \"sidebar\": HTMLElement /* Sidebar */\n \"skip\": HTMLAnchorElement /* Skip link */\n \"source\": HTMLAnchorElement /* Repository information */\n \"tabs\": HTMLElement /* Navigation tabs */\n \"toc\": HTMLElement /* Table of contents */\n \"top\": HTMLAnchorElement /* Back-to-top button */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve the element for a given component or throw a reference error\n *\n * @template T - Component type\n *\n * @param type - Component type\n * @param node - Node of reference\n *\n * @returns Element\n */\nexport function getComponentElement(\n type: T, node: ParentNode = document\n): ComponentTypeMap[T] {\n return getElementOrThrow(`[data-md-component=${type}]`, node)\n}\n\n/**\n * Retrieve all elements for a given component\n *\n * @template T - Component type\n *\n * @param type - Component type\n * @param node - Node of reference\n *\n * @returns Elements\n */\nexport function getComponentElements(\n type: T, node: ParentNode = document\n): ComponentTypeMap[T][] {\n return getElements(`[data-md-component=${type}]`, node)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport ClipboardJS from \"clipboard\"\nimport {\n NEVER,\n Observable,\n Subject,\n fromEvent,\n merge,\n of\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n switchMap,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { resetFocusable, setFocusable } from \"~/actions\"\nimport {\n Viewport,\n getElementContentSize,\n getElementSize,\n getElements,\n watchMedia\n} from \"~/browser\"\nimport { renderClipboardButton } from \"~/templates\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Code block\n */\nexport interface CodeBlock {\n scroll: boolean /* Code block overflows */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Global index for Clipboard.js integration\n */\nlet index = 0\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch code block\n *\n * This function monitors size changes of the viewport, as well as switches of\n * content tabs with embedded code blocks, as both may trigger overflow.\n *\n * @param el - Code block element\n * @param options - Options\n *\n * @returns Code block observable\n */\nexport function watchCodeBlock(\n el: HTMLElement, { viewport$ }: WatchOptions\n): Observable {\n const container$ = of(el)\n .pipe(\n switchMap(child => {\n const container = child.closest(\"[data-tabs]\")\n if (container instanceof HTMLElement) {\n return merge(\n ...getElements(\"input\", container)\n .map(input => fromEvent(input, \"change\"))\n )\n }\n return NEVER\n })\n )\n\n /* Check overflow on resize and tab change */\n return merge(\n viewport$.pipe(distinctUntilKeyChanged(\"size\")),\n container$\n )\n .pipe(\n map(() => {\n const visible = getElementSize(el)\n const content = getElementContentSize(el)\n return {\n scroll: content.width > visible.width\n }\n }),\n distinctUntilKeyChanged(\"scroll\")\n )\n}\n\n/**\n * Mount code block\n *\n * This function ensures that an overflowing code block is focusable through\n * keyboard, so it can be scrolled without a mouse to improve on accessibility.\n *\n * @param el - Code block element\n * @param options - Options\n *\n * @returns Code block component observable\n */\nexport function mountCodeBlock(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n withLatestFrom(watchMedia(\"(hover)\"))\n )\n .subscribe(([{ scroll }, hover]) => {\n if (scroll && hover)\n setFocusable(el)\n else\n resetFocusable(el)\n })\n\n /* Render button for Clipboard.js integration */\n if (ClipboardJS.isSupported()) {\n const parent = el.closest(\"pre\")!\n parent.id = `__code_${index++}`\n parent.insertBefore(\n renderClipboardButton(parent.id),\n el\n )\n }\n\n /* Create and return component */\n return watchCodeBlock(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set focusable property\n *\n * @param el - Element\n * @param value - Tabindex value\n */\nexport function setFocusable(\n el: HTMLElement, value = 0\n): void {\n el.setAttribute(\"tabindex\", value.toString())\n}\n\n/**\n * Reset focusable property\n *\n * @param el - Element\n */\nexport function resetFocusable(\n el: HTMLElement\n): void {\n el.removeAttribute(\"tabindex\")\n}\n\n/**\n * Set scroll lock\n *\n * @param el - Scrollable element\n * @param value - Vertical offset\n */\nexport function setScrollLock(\n el: HTMLElement, value: number\n): void {\n el.setAttribute(\"data-md-state\", \"lock\")\n el.style.top = `-${value}px`\n}\n\n/**\n * Reset scroll lock\n *\n * @param el - Scrollable element\n */\nexport function resetScrollLock(\n el: HTMLElement\n): void {\n const value = -1 * parseInt(el.style.top, 10)\n el.removeAttribute(\"data-md-state\")\n el.style.top = \"\"\n if (value)\n window.scrollTo(0, value)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set anchor state\n *\n * @param el - Anchor element\n * @param state - Anchor state\n */\nexport function setAnchorState(\n el: HTMLElement, state: \"blur\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset anchor state\n *\n * @param el - Anchor element\n */\nexport function resetAnchorState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set anchor active\n *\n * @param el - Anchor element\n * @param value - Whether the anchor is active\n */\nexport function setAnchorActive(\n el: HTMLElement, value: boolean\n): void {\n el.classList.toggle(\"md-nav__link--active\", value)\n}\n\n/**\n * Reset anchor active\n *\n * @param el - Anchor element\n */\nexport function resetAnchorActive(\n el: HTMLElement\n): void {\n el.classList.remove(\"md-nav__link--active\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set dialog message\n *\n * @param el - Dialog element\n * @param value - Dialog message\n */\nexport function setDialogMessage(\n el: HTMLElement, value: string\n): void {\n el.firstElementChild!.innerHTML = value\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set dialog state\n *\n * @param el - Dialog element\n * @param state - Dialog state\n */\nexport function setDialogState(\n el: HTMLElement, state: \"open\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset dialog state\n *\n * @param el - Dialog element\n */\nexport function resetDialogState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header state\n *\n * @param el - Header element\n * @param state - Header state\n */\nexport function setHeaderState(\n el: HTMLElement, state: \"shadow\" | \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset header state\n *\n * @param el - Header element\n */\nexport function resetHeaderState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header title state\n *\n * @param el - Header title element\n * @param state - Header title state\n */\nexport function setHeaderTitleState(\n el: HTMLElement, state: \"active\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset header title state\n *\n * @param el - Header title element\n */\nexport function resetHeaderTitleState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set search query placeholder\n *\n * @param el - Search query element\n * @param value - Placeholder\n */\nexport function setSearchQueryPlaceholder(\n el: HTMLInputElement, value: string\n): void {\n el.placeholder = value\n}\n\n/**\n * Reset search query placeholder\n *\n * @param el - Search query element\n */\nexport function resetSearchQueryPlaceholder(\n el: HTMLInputElement\n): void {\n el.placeholder = translation(\"search.placeholder\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\nimport { round } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set number of search results\n *\n * @param el - Search result metadata element\n * @param value - Number of results\n */\nexport function setSearchResultMeta(\n el: HTMLElement, value: number\n): void {\n switch (value) {\n\n /* No results */\n case 0:\n el.textContent = translation(\"search.result.none\")\n break\n\n /* One result */\n case 1:\n el.textContent = translation(\"search.result.one\")\n break\n\n /* Multiple result */\n default:\n el.textContent = translation(\"search.result.other\", round(value))\n }\n}\n\n/**\n * Reset number of search results\n *\n * @param el - Search result metadata element\n */\nexport function resetSearchResultMeta(\n el: HTMLElement\n): void {\n el.textContent = translation(\"search.result.placeholder\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Add an element to the search result list\n *\n * @param el - Search result list element\n * @param child - Search result element\n */\nexport function addToSearchResultList(\n el: HTMLElement, child: Element\n): void {\n el.appendChild(child)\n}\n\n/**\n * Reset search result list\n *\n * @param el - Search result list element\n */\nexport function resetSearchResultList(\n el: HTMLElement\n): void {\n el.innerHTML = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar offset\n *\n * @param el - Sidebar element\n * @param value - Sidebar offset\n */\nexport function setSidebarOffset(\n el: HTMLElement, value: number\n): void {\n el.style.top = `${value}px`\n}\n\n/**\n * Reset sidebar offset\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarOffset(\n el: HTMLElement\n): void {\n el.style.top = \"\"\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar height\n *\n * This function doesn't set the height of the actual sidebar, but of its first\n * child \u2013 the `.md-sidebar__scrollwrap` element in order to mitigiate jittery\n * sidebars when the footer is scrolled into view. At some point we switched\n * from `absolute` / `fixed` positioning to `sticky` positioning, significantly\n * reducing jitter in some browsers (respectively Firefox and Safari) when\n * scrolling from the top. However, top-aligned sticky positioning means that\n * the sidebar snaps to the bottom when the end of the container is reached.\n * This is what leads to the mentioned jitter, as the sidebar's height may be\n * updated too slowly.\n *\n * This behaviour can be mitigiated by setting the height of the sidebar to `0`\n * while preserving the padding, and the height on its first element.\n *\n * @param el - Sidebar element\n * @param value - Sidebar height\n */\nexport function setSidebarHeight(\n el: HTMLElement, value: number\n): void {\n const scrollwrap = el.firstElementChild as HTMLElement\n scrollwrap.style.height = `${value - 2 * scrollwrap.offsetTop}px`\n}\n\n/**\n * Reset sidebar height\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarHeight(\n el: HTMLElement\n): void {\n const scrollwrap = el.firstElementChild as HTMLElement\n scrollwrap.style.height = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set repository facts\n *\n * @param el - Repository element\n * @param child - Repository facts element\n */\nexport function setSourceFacts(\n el: HTMLElement, child: Element\n): void {\n el.lastElementChild!.appendChild(child)\n}\n\n/**\n * Set repository state\n *\n * @param el - Repository element\n * @param state - Repository state\n */\nexport function setSourceState(\n el: HTMLElement, state: \"done\"\n): void {\n el.lastElementChild!.setAttribute(\"data-md-state\", state)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set tabs state\n *\n * @param el - Tabs element\n * @param state - Tabs state\n */\nexport function setTabsState(\n el: HTMLElement, state: \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset tabs state\n *\n * @param el - Tabs element\n */\nexport function resetTabsState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set back-to-top state\n *\n * @param el - Back-to-top element\n * @param state - Back-to-top state\n */\nexport function setBackToTopState(\n el: HTMLElement, state: \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset back-to-top state\n *\n * @param el - Back-to-top element\n */\nexport function resetBackToTopState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set back-to-top offset\n *\n * @param el - Back-to-top element\n * @param value - Back-to-top offset\n */\nexport function setBackToTopOffset(\n el: HTMLElement, value: number\n): void {\n el.style.top = `${value}px`\n}\n\n/**\n * Reset back-to-top offset\n *\n * @param el - Back-to-top element\n */\nexport function resetBackToTopOffset(\n el: HTMLElement\n): void {\n el.style.top = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a 'copy-to-clipboard' button\n *\n * @param id - Unique identifier\n *\n * @returns Element\n */\nexport function renderClipboardButton(id: string): HTMLElement {\n return (\n code`}\n >\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { feature, translation } from \"~/_\"\nimport {\n SearchDocument,\n SearchMetadata,\n SearchResultItem\n} from \"~/integrations/search\"\nimport { h, truncate } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Render flag\n */\nconst enum Flag {\n TEASER = 1, /* Render teaser */\n PARENT = 2 /* Render as parent */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper function\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search document\n *\n * @param document - Search document\n * @param flag - Render flags\n *\n * @returns Element\n */\nfunction renderSearchDocument(\n document: SearchDocument & SearchMetadata, flag: Flag\n): HTMLElement {\n const parent = flag & Flag.PARENT\n const teaser = flag & Flag.TEASER\n\n /* Render missing query terms */\n const missing = Object.keys(document.terms)\n .filter(key => !document.terms[key])\n .map(key => [{key}, \" \"])\n .flat()\n .slice(0, -1)\n\n /* Assemble query string for highlighting */\n const url = new URL(document.location)\n if (feature(\"search.highlight\"))\n url.searchParams.set(\"h\", Object.entries(document.terms)\n .filter(([, match]) => match)\n .reduce((highlight, [value]) => `${highlight} ${value}`.trim(), \"\")\n )\n\n /* Render article or section, depending on flags */\n return (\n \n \n {parent > 0 &&
    }\n

    {document.title}

    \n {teaser > 0 && document.text.length > 0 &&\n

    \n {truncate(document.text, 320)}\n

    \n }\n {teaser > 0 && missing.length > 0 &&\n

    \n {translation(\"search.result.term.missing\")}: {...missing}\n

    \n }\n \n
    \n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search result\n *\n * @param result - Search result\n *\n * @returns Element\n */\nexport function renderSearchResultItem(\n result: SearchResultItem\n): HTMLElement {\n const threshold = result[0].score\n const docs = [...result]\n\n /* Find and extract parent article */\n const parent = docs.findIndex(doc => !doc.location.includes(\"#\"))\n const [article] = docs.splice(parent, 1)\n\n /* Determine last index above threshold */\n let index = docs.findIndex(doc => doc.score < threshold)\n if (index === -1)\n index = docs.length\n\n /* Partition sections */\n const best = docs.slice(0, index)\n const more = docs.slice(index)\n\n /* Render children */\n const children = [\n renderSearchDocument(article, Flag.PARENT | +(!parent && index === 0)),\n ...best.map(section => renderSearchDocument(section, Flag.TEASER)),\n ...more.length ? [\n
    \n \n {more.length > 0 && more.length === 1\n ? translation(\"search.result.more.one\")\n : translation(\"search.result.more.other\", more.length)\n }\n \n {...more.map(section => renderSearchDocument(section, Flag.TEASER))}\n
    \n ] : []\n ]\n\n /* Render search result */\n return (\n
  • \n {children}\n
  • \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SourceFacts } from \"~/components\"\nimport { h, round } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render repository facts\n *\n * @param facts - Repository facts\n *\n * @returns Element\n */\nexport function renderSourceFacts(facts: SourceFacts): HTMLElement {\n return (\n
      \n {Object.entries(facts).map(([key, value]) => (\n
    • \n {typeof value === \"number\" ? round(value) : value}\n
    • \n ))}\n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a table inside a wrapper to improve scrolling on mobile\n *\n * @param table - Table element\n *\n * @returns Element\n */\nexport function renderTable(table: HTMLElement): HTMLElement {\n return (\n
    \n
    \n {table}\n
    \n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { configuration, translation } from \"~/_\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Version\n */\nexport interface Version {\n version: string /* Version identifier */\n title: string /* Version title */\n aliases: string[] /* Version aliases */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a version\n *\n * @param version - Version\n *\n * @returns Element\n */\nfunction renderVersion(version: Version): HTMLElement {\n const config = configuration()\n\n /* Ensure trailing slash, see https://bit.ly/3rL5u3f */\n const url = new URL(`../${version.version}/`, config.base)\n return (\n
  • \n \n {version.title}\n \n
  • \n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a version selector\n *\n * @param versions - Versions\n *\n * @returns Element\n */\nexport function renderVersionSelector(versions: Version[]): HTMLElement {\n const config = configuration()\n\n /* Determine active version */\n const [, current] = config.base.match(/([^/]+)\\/?$/)!\n const active =\n versions.find(({ version, aliases }) => (\n version === current || aliases.includes(current)\n )) || versions[0]\n\n /* Render version selector */\n return (\n
    \n \n {active.title}\n \n
      \n {versions.map(renderVersion)}\n
    \n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject } from \"rxjs\"\nimport {\n filter,\n finalize,\n map,\n mapTo,\n mergeWith,\n tap\n} from \"rxjs/operators\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Details\n */\nexport interface Details {\n scroll?: boolean /* Scroll into view */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n target$: Observable /* Location target observable */\n print$: Observable /* Print mode observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n target$: Observable /* Location target observable */\n print$: Observable /* Print mode observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch details\n *\n * @param el - Details element\n * @param options - Options\n *\n * @returns Details observable\n */\nexport function watchDetails(\n el: HTMLDetailsElement, { target$, print$ }: WatchOptions\n): Observable
    {\n return target$\n .pipe(\n map(target => target.closest(\"details:not([open])\")!),\n filter(details => el === details),\n mapTo({ scroll: true }),\n mergeWith(print$.pipe(mapTo({})))\n )\n}\n\n/**\n * Mount details\n *\n * This function ensures that `details` tags are opened on anchor jumps and\n * prior to printing, so the whole content of the page is visible.\n *\n * @param el - Details element\n * @param options - Options\n *\n * @returns Details component observable\n */\nexport function mountDetails(\n el: HTMLDetailsElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject
    ()\n internal$.subscribe(({ scroll }) => {\n el.setAttribute(\"open\", \"\")\n if (scroll)\n el.scrollIntoView()\n })\n\n /* Create and return component */\n return watchDetails(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n mapTo({ ref: el })\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, of } from \"rxjs\"\n\nimport { replaceElement } from \"~/browser\"\nimport { renderTable } from \"~/templates\"\nimport { h } from \"~/utilities\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Data table\n */\nexport interface DataTable {}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Sentinel for replacement\n */\nconst sentinel = h(\"table\")\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount data table\n *\n * This function wraps a data table in another scrollable container, so it can\n * be smoothly scrolled on smaller screen sizes and won't break the layout.\n *\n * @param el - Data table element\n *\n * @returns Data table component observable\n */\nexport function mountDataTable(\n el: HTMLElement\n): Observable> {\n replaceElement(el, sentinel)\n replaceElement(sentinel, renderTable(el))\n\n /* Create and return component */\n return of({ ref: el })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, merge } from \"rxjs\"\n\nimport { Viewport, getElements } from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { CodeBlock, mountCodeBlock } from \"../code\"\nimport { Details, mountDetails } from \"../details\"\nimport { DataTable, mountDataTable } from \"../table\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Content\n */\nexport type Content =\n | CodeBlock\n | DataTable\n | Details\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n target$: Observable /* Location target observable */\n viewport$: Observable /* Viewport observable */\n print$: Observable /* Print mode observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount content\n *\n * This function mounts all components that are found in the content of the\n * actual article, including code blocks, data tables and details.\n *\n * @param el - Content element\n * @param options - Options\n *\n * @returns Content component observable\n */\nexport function mountContent(\n el: HTMLElement, { target$, viewport$, print$ }: MountOptions\n): Observable> {\n return merge(\n\n /* Code blocks */\n ...getElements(\"pre > code\", el)\n .map(child => mountCodeBlock(child, { viewport$ })),\n\n /* Data tables */\n ...getElements(\"table:not([class])\", el)\n .map(child => mountDataTable(child)),\n\n /* Details */\n ...getElements(\"details\", el)\n .map(child => mountDetails(child, { target$, print$ }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n merge,\n of\n} from \"rxjs\"\nimport {\n delay,\n finalize,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetDialogState,\n setDialogMessage,\n setDialogState\n} from \"~/actions\"\n\nimport { Component } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Dialog\n */\nexport interface Dialog {\n message: string /* Dialog message */\n open: boolean /* Dialog is visible */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n alert$: Subject /* Alert subject */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n alert$: Subject /* Alert subject */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch dialog\n *\n * @param _el - Dialog element\n * @param options - Options\n *\n * @returns Dialog observable\n */\nexport function watchDialog(\n _el: HTMLElement, { alert$ }: WatchOptions\n): Observable {\n return alert$\n .pipe(\n switchMap(message => merge(\n of(true),\n of(false).pipe(delay(2000))\n )\n .pipe(\n map(open => ({ message, open }))\n )\n )\n )\n}\n\n/**\n * Mount dialog\n *\n * This function reveals the dialog in the right cornerwhen a new alert is\n * emitted through the subject that is passed as part of the options.\n *\n * @param el - Dialog element\n * @param options - Options\n *\n * @returns Dialog component observable\n */\nexport function mountDialog(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe(({ message, open }) => {\n setDialogMessage(el, message)\n if (open)\n setDialogState(el, \"open\")\n else\n resetDialogState(el)\n })\n\n /* Create and return component */\n return watchDialog(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest,\n defer,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n combineLatestWith,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n map,\n observeOn,\n shareReplay,\n startWith,\n switchMap\n} from \"rxjs/operators\"\n\nimport { feature } from \"~/_\"\nimport { resetHeaderState, setHeaderState } from \"~/actions\"\nimport {\n Viewport,\n watchElementSize,\n watchToggle\n} from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { Main } from \"../../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Header\n */\nexport interface Header {\n height: number /* Header visible height */\n sticky: boolean /* Header stickyness */\n hidden: boolean /* User scrolled past threshold */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Compute whether the header is hidden\n *\n * If the user scrolls past a certain threshold, the header can be hidden when\n * scrolling down, and shown when scrolling up.\n *\n * @param options - Options\n *\n * @returns Toggle observable\n */\nfunction isHidden({ viewport$ }: WatchOptions): Observable {\n if (!feature(\"header.autohide\"))\n return of(false)\n\n /* Compute direction and turning point */\n const direction$ = viewport$\n .pipe(\n map(({ offset: { y } }) => y),\n bufferCount(2, 1),\n map(([a, b]) => [a < b, b] as const),\n distinctUntilKeyChanged(0)\n )\n\n /* Compute whether header should be hidden */\n const hidden$ = combineLatest([viewport$, direction$])\n .pipe(\n filter(([{ offset }, [, y]]) => Math.abs(y - offset.y) > 100),\n map(([, [direction]]) => direction),\n distinctUntilChanged()\n )\n\n /* Compute threshold for hiding */\n const search$ = watchToggle(\"search\")\n return combineLatest([viewport$, search$])\n .pipe(\n map(([{ offset }, search]) => offset.y > 400 && !search),\n distinctUntilChanged(),\n switchMap(active => active ? hidden$ : of(false)),\n startWith(false)\n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch header\n *\n * @param el - Header element\n * @param options - Options\n *\n * @returns Header observable\n */\nexport function watchHeader(\n el: HTMLElement, options: WatchOptions\n): Observable
    {\n return defer(() => {\n const styles = getComputedStyle(el)\n return of(\n styles.position === \"sticky\" ||\n styles.position === \"-webkit-sticky\"\n )\n })\n .pipe(\n combineLatestWith(watchElementSize(el), isHidden(options)),\n map(([sticky, { height }, hidden]) => ({\n height: sticky ? height : 0,\n sticky,\n hidden\n })),\n distinctUntilChanged((a, b) => (\n a.sticky === b.sticky &&\n a.height === b.height &&\n a.hidden === b.hidden\n )),\n shareReplay(1)\n )\n}\n\n/**\n * Mount header\n *\n * This function manages the different states of the header, i.e. whether it's\n * hidden or rendered with a shadow. This depends heavily on the main area.\n *\n * @param el - Header element\n * @param options - Options\n *\n * @returns Header component observable\n */\nexport function mountHeader(\n el: HTMLElement, { header$, main$ }: MountOptions\n): Observable> {\n const internal$ = new Subject
    ()\n internal$\n .pipe(\n distinctUntilKeyChanged(\"active\"),\n combineLatestWith(header$),\n observeOn(animationFrameScheduler)\n )\n .subscribe(([{ active }, { hidden }]) => {\n if (active)\n setHeaderState(el, hidden ? \"hidden\" : \"shadow\")\n else\n resetHeaderState(el)\n })\n\n /* Connect to long-living subject and return component */\n main$.subscribe(main => internal$.next(main))\n return header$\n .pipe(\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n Subject,\n animationFrameScheduler\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetHeaderTitleState,\n setHeaderTitleState\n} from \"~/actions\"\nimport {\n Viewport,\n getElement,\n getElementSize,\n watchViewportAt\n} from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { Header } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Header\n */\nexport interface HeaderTitle {\n active: boolean /* User scrolled past first headline */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch header title\n *\n * @param el - Heading element\n * @param options - Options\n *\n * @returns Header title observable\n */\nexport function watchHeaderTitle(\n el: HTMLHeadingElement, { viewport$, header$ }: WatchOptions\n): Observable {\n return watchViewportAt(el, { header$, viewport$ })\n .pipe(\n map(({ offset: { y } }) => {\n const { height } = getElementSize(el)\n return {\n active: y >= height\n }\n }),\n distinctUntilKeyChanged(\"active\")\n )\n}\n\n/**\n * Mount header title\n *\n * This function swaps the header title from the site title to the title of the\n * current page when the user scrolls past the first headline.\n *\n * @param el - Header title element\n * @param options - Options\n *\n * @returns Header title component observable\n */\nexport function mountHeaderTitle(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe(({ active }) => {\n if (active)\n setHeaderTitleState(el, \"active\")\n else\n resetHeaderTitleState(el)\n })\n\n /* Obtain headline, if any */\n const headline = getElement(\"article h1\")\n if (typeof headline === \"undefined\")\n return NEVER\n\n /* Create and return component */\n return watchHeaderTitle(headline, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n combineLatest\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n distinctUntilKeyChanged,\n map,\n switchMap\n} from \"rxjs/operators\"\n\nimport { Viewport, watchElementSize } from \"~/browser\"\n\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Main area\n */\nexport interface Main {\n offset: number /* Main area top offset */\n height: number /* Main area visible height */\n active: boolean /* User scrolled past header */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch main area\n *\n * This function returns an observable that computes the visual parameters of\n * the main area which depends on the viewport vertical offset and height, as\n * well as the height of the header element, if the header is fixed.\n *\n * @param el - Main area element\n * @param options - Options\n *\n * @returns Main area observable\n */\nexport function watchMain(\n el: HTMLElement, { viewport$, header$ }: WatchOptions\n): Observable
    {\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(({ height }) => height),\n distinctUntilChanged()\n )\n\n /* Compute the main area's top and bottom borders */\n const border$ = adjust$\n .pipe(\n switchMap(() => watchElementSize(el)\n .pipe(\n map(({ height }) => ({\n top: el.offsetTop,\n bottom: el.offsetTop + height\n })),\n distinctUntilKeyChanged(\"bottom\")\n )\n )\n )\n\n /* Compute the main area's offset, visible height and if we scrolled past */\n return combineLatest([adjust$, border$, viewport$])\n .pipe(\n map(([header, { top, bottom }, { offset: { y }, size: { height } }]) => {\n height = Math.max(0, height\n - Math.max(0, top - y, header)\n - Math.max(0, height + y - bottom)\n )\n return {\n offset: top - header,\n height,\n active: top - header <= y\n }\n }),\n distinctUntilChanged((a, b) => (\n a.offset === b.offset &&\n a.height === b.height &&\n a.active === b.active\n ))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n fromEvent,\n of\n} from \"rxjs\"\nimport {\n finalize,\n map,\n mapTo,\n mergeMap,\n shareReplay,\n startWith,\n tap\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\nimport { Component } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Palette colors\n */\nexport interface PaletteColor {\n scheme?: string /* Color scheme */\n primary?: string /* Primary color */\n accent?: string /* Accent color */\n}\n\n/**\n * Palette\n */\nexport interface Palette {\n index: number /* Palette index */\n color: PaletteColor /* Palette colors */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch color palette\n *\n * @param inputs - Color palette element\n *\n * @returns Color palette observable\n */\nexport function watchPalette(\n inputs: HTMLInputElement[]\n): Observable {\n const data = localStorage.getItem(__prefix(\"__palette\"))!\n const current = JSON.parse(data) || {\n index: inputs.findIndex(input => (\n matchMedia(input.getAttribute(\"data-md-color-media\")!).matches\n ))\n }\n\n /* Emit changes in color palette */\n const palette$ = of(...inputs)\n .pipe(\n mergeMap(input => fromEvent(input, \"change\")\n .pipe(\n mapTo(input)\n )\n ),\n startWith(inputs[Math.max(0, current.index)]),\n map(input => ({\n index: inputs.indexOf(input),\n color: {\n scheme: input.getAttribute(\"data-md-color-scheme\"),\n primary: input.getAttribute(\"data-md-color-primary\"),\n accent: input.getAttribute(\"data-md-color-accent\")\n }\n } as Palette)),\n shareReplay(1)\n )\n\n /* Persist preference in local storage */\n palette$.subscribe(palette => {\n localStorage.setItem(__prefix(\"__palette\"), JSON.stringify(palette))\n })\n\n /* Return palette */\n return palette$\n}\n\n/**\n * Mount color palette\n *\n * @param el - Color palette element\n *\n * @returns Color palette component observable\n */\nexport function mountPalette(\n el: HTMLElement\n): Observable> {\n const internal$ = new Subject()\n\n /* Set color palette */\n internal$.subscribe(palette => {\n for (const [key, value] of Object.entries(palette.color))\n if (typeof value === \"string\")\n document.body.setAttribute(`data-md-color-${key}`, value)\n\n /* Toggle visibility */\n for (let index = 0; index < inputs.length; index++) {\n const label = inputs[index].nextElementSibling\n if (label instanceof HTMLElement)\n label.hidden = palette.index !== index\n }\n })\n\n /* Create and return component */\n const inputs = getElements(\"input\", el)\n return watchPalette(inputs)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport ClipboardJS from \"clipboard\"\nimport { Observable, Subject } from \"rxjs\"\n\nimport { translation } from \"~/_\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n alert$: Subject /* Alert subject */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up Clipboard.js integration\n *\n * @param options - Options\n */\nexport function setupClipboardJS(\n { alert$ }: SetupOptions\n): void {\n if (ClipboardJS.isSupported()) {\n new Observable(subscriber => {\n new ClipboardJS(\"[data-clipboard-target], [data-clipboard-text]\")\n .on(\"success\", ev => subscriber.next(ev))\n })\n .subscribe(() => alert$.next(translation(\"clipboard.copied\")))\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n fromEvent,\n merge,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n catchError,\n concatMap,\n debounceTime,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n map,\n sample,\n share,\n skip,\n skipUntil,\n switchMap\n} from \"rxjs/operators\"\n\nimport { configuration, feature } from \"~/_\"\nimport {\n Viewport,\n ViewportOffset,\n getElement,\n getElements,\n replaceElement,\n request,\n requestXML,\n setLocation,\n setLocationHash,\n setViewportOffset\n} from \"~/browser\"\nimport { getComponentElement } from \"~/components\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * History state\n */\nexport interface HistoryState {\n url: URL /* State URL */\n offset?: ViewportOffset /* State viewport offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n document$: Subject /* Document subject */\n location$: Subject /* Location subject */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Preprocess a list of URLs\n *\n * This function replaces the `site_url` in the sitemap with the actual base\n * URL, to allow instant loading to work in occasions like Netlify previews.\n *\n * @param urls - URLs\n *\n * @returns Processed URLs\n */\nfunction preprocess(urls: string[]): string[] {\n if (urls.length < 2)\n return urls\n\n /* Take the first two URLs and remove everything after the last slash */\n const [root, next] = urls\n .sort((a, b) => a.length - b.length)\n .map(url => url.replace(/[^/]+$/, \"\"))\n\n /* Compute common prefix */\n let index = 0\n if (root === next)\n index = root.length\n else\n while (root.charCodeAt(index) === next.charCodeAt(index))\n index++\n\n /* Replace common prefix (i.e. base) with effective base */\n const config = configuration()\n return urls.map(url => (\n url.replace(root.slice(0, index), config.base)\n ))\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up instant loading\n *\n * When fetching, theoretically, we could use `responseType: \"document\"`, but\n * since all MkDocs links are relative, we need to make sure that the current\n * location matches the document we just loaded. Otherwise any relative links\n * in the document could use the old location.\n *\n * This is the reason why we need to synchronize history events and the process\n * of fetching the document for navigation changes (except `popstate` events):\n *\n * 1. Fetch document via `XMLHTTPRequest`\n * 2. Set new location via `history.pushState`\n * 3. Parse and emit fetched document\n *\n * For `popstate` events, we must not use `history.pushState`, or the forward\n * history will be irreversibly overwritten. In case the request fails, the\n * location change is dispatched regularly.\n *\n * @param options - Options\n */\nexport function setupInstantLoading(\n { document$, location$, viewport$ }: SetupOptions\n): void {\n const config = configuration()\n if (location.protocol === \"file:\")\n return\n\n /* Disable automatic scroll restoration */\n if (\"scrollRestoration\" in history) {\n history.scrollRestoration = \"manual\"\n\n /* Hack: ensure that reloads restore viewport offset */\n fromEvent(window, \"beforeunload\")\n .subscribe(() => {\n history.scrollRestoration = \"auto\"\n })\n }\n\n /* Hack: ensure absolute favicon link to omit 404s when switching */\n const favicon = getElement(\"link[rel=icon]\")\n if (typeof favicon !== \"undefined\")\n favicon.href = favicon.href\n\n /* Intercept internal navigation */\n const push$ = requestXML(new URL(\"sitemap.xml\", config.base))\n .pipe(\n map(sitemap => preprocess(getElements(\"loc\", sitemap)\n .map(node => node.textContent!)\n )),\n switchMap(urls => fromEvent(document.body, \"click\")\n .pipe(\n filter(ev => !ev.metaKey && !ev.ctrlKey),\n switchMap(ev => {\n\n /* Handle HTML and SVG elements */\n if (ev.target instanceof Element) {\n const el = ev.target.closest(\"a\")\n if (el && !el.target) {\n const url = new URL(el.href)\n\n /* Canonicalize URL */\n url.search = \"\"\n url.hash = \"\"\n\n /* Check if URL should be intercepted */\n if (\n url.pathname !== location.pathname &&\n urls.includes(url.toString())\n ) {\n ev.preventDefault()\n return of({\n url: new URL(el.href)\n })\n }\n }\n }\n return NEVER\n })\n )\n ),\n share()\n )\n\n /* Intercept history back and forward */\n const pop$ = fromEvent(window, \"popstate\")\n .pipe(\n filter(ev => ev.state !== null),\n map(ev => ({\n url: new URL(location.href),\n offset: ev.state\n })),\n share()\n )\n\n /* Emit location change */\n merge(push$, pop$)\n .pipe(\n distinctUntilChanged((a, b) => a.url.href === b.url.href),\n map(({ url }) => url)\n )\n .subscribe(location$)\n\n /* Fetch document via `XMLHTTPRequest` */\n const response$ = location$\n .pipe(\n distinctUntilKeyChanged(\"pathname\"),\n switchMap(url => request(url.href)\n .pipe(\n catchError(() => {\n setLocation(url)\n return NEVER\n })\n )\n ),\n share()\n )\n\n /* Set new location via `history.pushState` */\n push$\n .pipe(\n sample(response$)\n )\n .subscribe(({ url }) => {\n history.pushState({}, \"\", `${url}`)\n })\n\n /* Parse and emit fetched document */\n const dom = new DOMParser()\n response$\n .pipe(\n switchMap(res => res.text()),\n map(res => dom.parseFromString(res, \"text/html\"))\n )\n .subscribe(document$)\n\n /* Replace meta tags and components */\n document$\n .pipe(\n skip(1)\n )\n .subscribe(replacement => {\n for (const selector of [\n\n /* Meta tags */\n \"title\",\n \"link[rel=canonical]\",\n \"meta[name=author]\",\n \"meta[name=description]\",\n\n /* Components */\n \"[data-md-component=announce]\",\n \"[data-md-component=container]\",\n \"[data-md-component=header-topic]\",\n \"[data-md-component=logo], .md-logo\", // compat\n \"[data-md-component=skip]\",\n ...feature(\"navigation.tabs.sticky\")\n ? [\"[data-md-component=tabs]\"]\n : []\n ]) {\n const source = getElement(selector)\n const target = getElement(selector, replacement)\n if (\n typeof source !== \"undefined\" &&\n typeof target !== \"undefined\"\n ) {\n replaceElement(source, target)\n }\n }\n })\n\n /* Re-evaluate scripts */\n document$\n .pipe(\n skip(1),\n map(() => getComponentElement(\"container\")),\n switchMap(el => of(...getElements(\"script\", el))),\n concatMap(el => {\n const script = h(\"script\")\n if (el.src) {\n for (const name of el.getAttributeNames())\n script.setAttribute(name, el.getAttribute(name)!)\n replaceElement(el, script)\n\n /* Complete when script is loaded */\n return new Observable(observer => {\n script.onload = () => observer.complete()\n })\n\n /* Complete immediately */\n } else {\n script.textContent = el.textContent\n replaceElement(el, script)\n return EMPTY\n }\n })\n )\n .subscribe()\n\n /* Emit history state change */\n merge(push$, pop$)\n .pipe(\n sample(document$),\n )\n .subscribe(({ url, offset }) => {\n if (url.hash && !offset) {\n setLocationHash(url.hash)\n } else {\n setViewportOffset(offset || { y: 0 })\n }\n })\n\n /* Debounce update of viewport offset */\n viewport$\n .pipe(\n skipUntil(push$),\n debounceTime(250),\n distinctUntilKeyChanged(\"offset\")\n )\n .subscribe(({ offset }) => {\n history.replaceState(offset, \"\")\n })\n\n /* Set viewport offset from history */\n merge(push$, pop$)\n .pipe(\n bufferCount(2, 1),\n filter(([a, b]) => a.url.pathname === b.url.pathname),\n map(([, state]) => state)\n )\n .subscribe(({ offset }) => {\n setViewportOffset(offset || { y: 0 })\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexDocument } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search document\n */\nexport interface SearchDocument extends SearchIndexDocument {\n parent?: SearchIndexDocument /* Parent article */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search document mapping\n */\nexport type SearchDocumentMap = Map\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search document mapping\n *\n * @param docs - Search index documents\n *\n * @returns Search document map\n */\nexport function setupSearchDocumentMap(\n docs: SearchIndexDocument[]\n): SearchDocumentMap {\n const documents = new Map()\n const parents = new Set()\n for (const doc of docs) {\n const [path, hash] = doc.location.split(\"#\")\n\n /* Extract location and title */\n const location = doc.location\n const title = doc.title\n\n /* Escape and cleanup text */\n const text = escapeHTML(doc.text)\n .replace(/\\s+(?=[,.:;!?])/g, \"\")\n .replace(/\\s+/g, \" \")\n\n /* Handle section */\n if (hash) {\n const parent = documents.get(path)!\n\n /* Ignore first section, override article */\n if (!parents.has(parent)) {\n parent.title = doc.title\n parent.text = text\n\n /* Remember that we processed the article */\n parents.add(parent)\n\n /* Add subsequent section */\n } else {\n documents.set(location, {\n location,\n title,\n text,\n parent\n })\n }\n\n /* Add article */\n } else {\n documents.set(location, {\n location,\n title,\n text\n })\n }\n }\n return documents\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexConfig } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlight function\n *\n * @param value - Value\n *\n * @returns Highlighted value\n */\nexport type SearchHighlightFn = (value: string) => string\n\n/**\n * Search highlight factory function\n *\n * @param query - Query value\n *\n * @returns Search highlight function\n */\nexport type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search highlighter\n *\n * @param config - Search index configuration\n * @param escape - Whether to escape HTML\n *\n * @returns Search highlight factory function\n */\nexport function setupSearchHighlighter(\n config: SearchIndexConfig, escape: boolean\n): SearchHighlightFactoryFn {\n const separator = new RegExp(config.separator, \"img\")\n const highlight = (_: unknown, data: string, term: string) => {\n return `${data}${term}`\n }\n\n /* Return factory function */\n return (query: string) => {\n query = query\n .replace(/[\\s*+\\-:~^]+/g, \" \")\n .trim()\n\n /* Create search term match expression */\n const match = new RegExp(`(^|${config.separator})(${\n query\n .replace(/[|\\\\{}()[\\]^$+*?.-]/g, \"\\\\$&\")\n .replace(separator, \"|\")\n })`, \"img\")\n\n /* Highlight string value */\n return value => (\n escape\n ? escapeHTML(value)\n : value\n )\n .replace(match, highlight)\n .replace(/<\\/mark>(\\s+)]*>/img, \"$1\")\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search transformation function\n *\n * @param value - Query value\n *\n * @returns Transformed query value\n */\nexport type SearchTransformFn = (value: string) => string\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Default transformation function\n *\n * 1. Search for terms in quotation marks and prepend a `+` modifier to denote\n * that the resulting document must contain all terms, converting the query\n * to an `AND` query (as opposed to the default `OR` behavior). While users\n * may expect terms enclosed in quotation marks to map to span queries, i.e.\n * for which order is important, Lunr.js doesn't support them, so the best\n * we can do is to convert the terms to an `AND` query.\n *\n * 2. Replace control characters which are not located at the beginning of the\n * query or preceded by white space, or are not followed by a non-whitespace\n * character or are at the end of the query string. Furthermore, filter\n * unmatched quotation marks.\n *\n * 3. Trim excess whitespace from left and right.\n *\n * @param query - Query value\n *\n * @returns Transformed query value\n */\nexport function defaultTransform(query: string): string {\n return query\n .split(/\"([^\"]+)\"/g) /* => 1 */\n .map((terms, index) => index & 1\n ? terms.replace(/^\\b|^(?![^\\x00-\\x7F]|$)|\\s+/g, \" +\")\n : terms\n )\n .join(\"\")\n .replace(/\"|(?:^|\\s+)[*+\\-:^~]+(?=\\s+|$)/g, \"\") /* => 2 */\n .trim() /* => 3 */\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchResult } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search message type\n */\nexport const enum SearchMessageType {\n SETUP, /* Search index setup */\n READY, /* Search index ready */\n QUERY, /* Search query */\n RESULT /* Search results */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message containing the data necessary to setup the search index\n */\nexport interface SearchSetupMessage {\n type: SearchMessageType.SETUP /* Message type */\n data: SearchIndex /* Message data */\n}\n\n/**\n * Message indicating the search index is ready\n */\nexport interface SearchReadyMessage {\n type: SearchMessageType.READY /* Message type */\n}\n\n/**\n * Message containing a search query\n */\nexport interface SearchQueryMessage {\n type: SearchMessageType.QUERY /* Message type */\n data: string /* Message data */\n}\n\n/**\n * Message containing results for a search query\n */\nexport interface SearchResultMessage {\n type: SearchMessageType.RESULT /* Message type */\n data: SearchResult /* Message data */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message exchanged with the search worker\n */\nexport type SearchMessage =\n | SearchSetupMessage\n | SearchReadyMessage\n | SearchQueryMessage\n | SearchResultMessage\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Type guard for search setup messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchSetupMessage(\n message: SearchMessage\n): message is SearchSetupMessage {\n return message.type === SearchMessageType.SETUP\n}\n\n/**\n * Type guard for search ready messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchReadyMessage(\n message: SearchMessage\n): message is SearchReadyMessage {\n return message.type === SearchMessageType.READY\n}\n\n/**\n * Type guard for search query messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchQueryMessage(\n message: SearchMessage\n): message is SearchQueryMessage {\n return message.type === SearchMessageType.QUERY\n}\n\n/**\n * Type guard for search result messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchResultMessage(\n message: SearchMessage\n): message is SearchResultMessage {\n return message.type === SearchMessageType.RESULT\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ObservableInput, Subject, from } from \"rxjs\"\nimport { map, share } from \"rxjs/operators\"\n\nimport { configuration, feature, translation } from \"~/_\"\nimport { WorkerHandler, watchWorker } from \"~/browser\"\n\nimport { SearchIndex } from \"../../_\"\nimport {\n SearchOptions,\n SearchPipeline\n} from \"../../options\"\nimport {\n SearchMessage,\n SearchMessageType,\n SearchSetupMessage,\n isSearchResultMessage\n} from \"../message\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search worker\n */\nexport type SearchWorker = WorkerHandler\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search index\n *\n * @param data - Search index\n *\n * @returns Search index\n */\nfunction setupSearchIndex(\n { config, docs, index }: SearchIndex\n): SearchIndex {\n\n /* Override default language with value from translation */\n if (config.lang.length === 1 && config.lang[0] === \"en\")\n config.lang = [\n translation(\"search.config.lang\")\n ]\n\n /* Override default separator with value from translation */\n if (config.separator === \"[\\\\s\\\\-]+\")\n config.separator = translation(\"search.config.separator\")\n\n /* Set pipeline from translation */\n const pipeline = translation(\"search.config.pipeline\")\n .split(/\\s*,\\s*/)\n .filter(Boolean) as SearchPipeline\n\n /* Determine search options */\n const options: SearchOptions = {\n pipeline,\n suggestions: feature(\"search.suggest\")\n }\n\n /* Return search index after defaulting */\n return { config, docs, index, options }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search worker\n *\n * This function creates a web worker to set up and query the search index,\n * which is done using Lunr.js. The index must be passed as an observable to\n * enable hacks like _localsearch_ via search index embedding as JSON.\n *\n * @param url - Worker URL\n * @param index - Search index observable input\n *\n * @returns Search worker\n */\nexport function setupSearchWorker(\n url: string, index: ObservableInput\n): SearchWorker {\n const config = configuration()\n const worker = new Worker(url)\n\n /* Create communication channels and resolve relative links */\n const tx$ = new Subject()\n const rx$ = watchWorker(worker, { tx$ })\n .pipe(\n map(message => {\n if (isSearchResultMessage(message)) {\n for (const result of message.data.items)\n for (const document of result)\n document.location = `${new URL(document.location, config.base)}`\n }\n return message\n }),\n share()\n )\n\n /* Set up search index */\n from(index)\n .pipe(\n map(data => ({\n type: SearchMessageType.SETUP,\n data: setupSearchIndex(data)\n }))\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Return search worker */\n return { tx$, rx$ }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { configuration } from \"~/_\"\nimport { getElementOrThrow, requestJSON } from \"~/browser\"\nimport { Version, renderVersionSelector } from \"~/templates\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up version selector\n */\nexport function setupVersionSelector(): void {\n const config = configuration()\n requestJSON(new URL(\"../versions.json\", config.base))\n .subscribe(versions => {\n const topic = getElementOrThrow(\".md-header__topic\")\n topic.appendChild(renderVersionSelector(versions))\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n combineLatest,\n fromEvent,\n merge\n} from \"rxjs\"\nimport {\n delay,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n finalize,\n map,\n take,\n takeLast,\n takeUntil,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetSearchQueryPlaceholder,\n setSearchQueryPlaceholder\n} from \"~/actions\"\nimport {\n getLocation,\n setElementFocus,\n setToggle,\n watchElementFocus\n} from \"~/browser\"\nimport {\n SearchMessageType,\n SearchQueryMessage,\n SearchWorker,\n defaultTransform,\n isSearchReadyMessage\n} from \"~/integrations\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search query\n */\nexport interface SearchQuery {\n value: string /* Query value */\n focus: boolean /* Query focus */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch search query\n *\n * Note that the focus event which triggers re-reading the current query value\n * is delayed by `1ms` so the input's empty state is allowed to propagate.\n *\n * @param el - Search query element\n * @param worker - Search worker\n *\n * @returns Search query observable\n */\nexport function watchSearchQuery(\n el: HTMLInputElement, { rx$ }: SearchWorker\n): Observable {\n const fn = __search?.transform || defaultTransform\n\n /* Intercept focus and input events */\n const focus$ = watchElementFocus(el)\n const value$ = merge(\n fromEvent(el, \"keyup\"),\n fromEvent(el, \"focus\").pipe(delay(1))\n )\n .pipe(\n map(() => fn(el.value)),\n distinctUntilChanged()\n )\n\n /* Intercept deep links */\n const location = getLocation()\n if (location.searchParams.has(\"q\")) {\n setToggle(\"search\", true)\n rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n .subscribe(() => {\n el.value = location.searchParams.get(\"q\")!\n setElementFocus(el)\n })\n }\n\n /* Combine into single observable */\n return combineLatest([value$, focus$])\n .pipe(\n map(([value, focus]) => ({ value, focus }))\n )\n}\n\n/**\n * Mount search query\n *\n * @param el - Search query element\n * @param worker - Search worker\n *\n * @returns Search query component observable\n */\nexport function mountSearchQuery(\n el: HTMLInputElement, { tx$, rx$ }: SearchWorker\n): Observable> {\n const internal$ = new Subject()\n\n /* Handle value changes */\n internal$\n .pipe(\n distinctUntilKeyChanged(\"value\"),\n map(({ value }): SearchQueryMessage => ({\n type: SearchMessageType.QUERY,\n data: value\n }))\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Handle focus changes */\n internal$\n .pipe(\n distinctUntilKeyChanged(\"focus\")\n )\n .subscribe(({ focus }) => {\n if (focus) {\n setToggle(\"search\", focus)\n setSearchQueryPlaceholder(el, \"\")\n } else {\n resetSearchQueryPlaceholder(el)\n }\n })\n\n /* Handle reset */\n fromEvent(el.form!, \"reset\")\n .pipe(\n takeUntil(internal$.pipe(takeLast(1)))\n )\n .subscribe(() => setElementFocus(el))\n\n /* Create and return component */\n return watchSearchQuery(el, { tx$, rx$ })\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n merge,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n filter,\n finalize,\n map,\n observeOn,\n switchMap,\n take,\n tap,\n withLatestFrom,\n zipWith\n} from \"rxjs/operators\"\n\nimport {\n addToSearchResultList,\n resetSearchResultList,\n resetSearchResultMeta,\n setSearchResultMeta\n} from \"~/actions\"\nimport {\n getElementOrThrow,\n watchElementThreshold\n} from \"~/browser\"\nimport {\n SearchResult,\n SearchWorker,\n isSearchReadyMessage,\n isSearchResultMessage\n} from \"~/integrations\"\nimport { renderSearchResultItem } from \"~/templates\"\n\nimport { Component } from \"../../_\"\nimport { SearchQuery } from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search result list\n *\n * This function performs a lazy rendering of the search results, depending on\n * the vertical offset of the search result container.\n *\n * @param el - Search result list element\n * @param worker - Search worker\n * @param options - Options\n *\n * @returns Search result list component observable\n */\nexport function mountSearchResult(\n el: HTMLElement, { rx$ }: SearchWorker, { query$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n const boundary$ = watchElementThreshold(el.parentElement!)\n .pipe(\n filter(Boolean)\n )\n\n /* Retrieve nested components */\n const meta = getElementOrThrow(\":scope > :first-child\", el)\n const list = getElementOrThrow(\":scope > :last-child\", el)\n\n /* Update search result metadata when ready */\n rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n .subscribe(() => {\n resetSearchResultMeta(meta)\n })\n\n /* Update search result metadata */\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(query$)\n )\n .subscribe(([{ items }, { value }]) => {\n if (value)\n setSearchResultMeta(meta, items.length)\n else\n resetSearchResultMeta(meta)\n })\n\n /* Update search result list */\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n tap(() => resetSearchResultList(list)),\n switchMap(({ items }) => merge(\n of(...items.slice(0, 10)),\n of(...items.slice(10))\n .pipe(\n bufferCount(4),\n zipWith(boundary$),\n switchMap(([chunk]) => of(...chunk))\n )\n ))\n )\n .subscribe(result => {\n addToSearchResultList(list, renderSearchResultItem(result))\n })\n\n /* Filter search result message */\n const result$ = rx$\n .pipe(\n filter(isSearchResultMessage),\n map(({ data }) => data)\n )\n\n /* Create and return component */\n return result$\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n fromEvent\n} from \"rxjs\"\nimport {\n finalize,\n map,\n tap\n} from \"rxjs/operators\"\n\nimport { getLocation } from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { SearchQuery } from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search sharing\n */\nexport interface SearchShare {\n url: URL /* Deep link for sharing */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n query$: Observable /* Search query observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search sharing\n *\n * @param _el - Search sharing element\n * @param options - Options\n *\n * @returns Search sharing observable\n */\nexport function watchSearchShare(\n _el: HTMLElement, { query$ }: WatchOptions\n): Observable {\n return query$\n .pipe(\n map(({ value }) => {\n const url = getLocation()\n url.hash = \"\"\n url.searchParams.delete(\"h\")\n url.searchParams.set(\"q\", value)\n return { url }\n })\n )\n}\n\n/**\n * Mount search sharing\n *\n * @param el - Search sharing element\n * @param options - Options\n *\n * @returns Search sharing component observable\n */\nexport function mountSearchShare(\n el: HTMLAnchorElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$.subscribe(({ url }) => {\n el.setAttribute(\"data-clipboard-text\", el.href)\n el.href = `${url}`\n })\n\n /* Prevent following of link */\n fromEvent(el, \"click\")\n .subscribe(ev => ev.preventDefault())\n\n /* Create and return component */\n return watchSearchShare(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n asyncScheduler,\n fromEvent\n} from \"rxjs\"\nimport {\n combineLatestWith,\n distinctUntilChanged,\n filter,\n finalize,\n map,\n observeOn,\n tap\n} from \"rxjs/operators\"\n\nimport { Keyboard } from \"~/browser\"\nimport {\n SearchResult,\n SearchWorker,\n isSearchResultMessage\n} from \"~/integrations\"\n\nimport { Component, getComponentElement } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search suggestions\n */\nexport interface SearchSuggest {}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n keyboard$: Observable /* Keyboard observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search suggestions\n *\n * This function will perform a lazy rendering of the search results, depending\n * on the vertical offset of the search result container.\n *\n * @param el - Search result list element\n * @param worker - Search worker\n * @param options - Options\n *\n * @returns Search result list component observable\n */\nexport function mountSearchSuggest(\n el: HTMLElement, { rx$ }: SearchWorker, { keyboard$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n\n /* Retrieve query component and track all changes */\n const query = getComponentElement(\"search-query\")\n const query$ = fromEvent(query, \"keydown\")\n .pipe(\n observeOn(asyncScheduler),\n map(() => query.value),\n distinctUntilChanged(),\n )\n\n /* Update search suggestions */\n internal$\n .pipe(\n combineLatestWith(query$),\n map(([{ suggestions }, value]) => {\n const words = value.split(/([\\s-]+)/)\n if (suggestions?.length && words[words.length - 1]) {\n const last = suggestions[suggestions.length - 1]\n if (last.startsWith(words[words.length - 1]))\n words[words.length - 1] = last\n } else {\n words.length = 0\n }\n return words\n })\n )\n .subscribe(words => el.innerHTML = words\n .join(\"\")\n .replace(/\\s/g, \" \")\n )\n\n /* Set up search keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"search\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Right arrow: accept current suggestion */\n case \"ArrowRight\":\n if (\n el.innerText.length &&\n query.selectionStart === query.value.length\n )\n query.value = el.innerText\n break\n }\n })\n\n /* Filter search result message */\n const result$ = rx$\n .pipe(\n filter(isSearchResultMessage),\n map(({ data }) => data)\n )\n\n /* Create and return component */\n return result$\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(() => ({ ref: el }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, ObservableInput, merge } from \"rxjs\"\nimport { filter, mergeWith, sample, take } from \"rxjs/operators\"\n\nimport { configuration } from \"~/_\"\nimport {\n Keyboard,\n getActiveElement,\n getElements,\n setElementFocus,\n setElementSelection,\n setToggle\n} from \"~/browser\"\nimport {\n SearchIndex,\n SearchResult,\n isSearchQueryMessage,\n isSearchReadyMessage,\n setupSearchWorker\n} from \"~/integrations\"\n\nimport {\n Component,\n getComponentElement,\n getComponentElements\n} from \"../../_\"\nimport { SearchQuery, mountSearchQuery } from \"../query\"\nimport { mountSearchResult } from \"../result\"\nimport { SearchShare, mountSearchShare } from \"../share\"\nimport { SearchSuggest, mountSearchSuggest } from \"../suggest\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search\n */\nexport type Search =\n | SearchQuery\n | SearchResult\n | SearchShare\n | SearchSuggest\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n index$: ObservableInput /* Search index observable */\n keyboard$: Observable /* Keyboard observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search\n *\n * This function sets up the search functionality, including the underlying\n * web worker and all keyboard bindings.\n *\n * @param el - Search element\n * @param options - Options\n *\n * @returns Search component observable\n */\nexport function mountSearch(\n el: HTMLElement, { index$, keyboard$ }: MountOptions\n): Observable> {\n const config = configuration()\n try {\n const url = __search?.worker || config.search\n const worker = setupSearchWorker(url, index$)\n\n /* Retrieve query and result components */\n const query = getComponentElement(\"search-query\", el)\n const result = getComponentElement(\"search-result\", el)\n\n /* Re-emit query when search is ready */\n const { tx$, rx$ } = worker\n tx$\n .pipe(\n filter(isSearchQueryMessage),\n sample(rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n )\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Set up search keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"search\")\n )\n .subscribe(key => {\n const active = getActiveElement()\n switch (key.type) {\n\n /* Enter: go to first (best) result */\n case \"Enter\":\n if (active === query) {\n const anchors = new Map()\n for (const anchor of getElements(\n \":first-child [href]\", result\n )) {\n const article = anchor.firstElementChild!\n anchors.set(anchor, parseFloat(\n article.getAttribute(\"data-md-score\")!\n ))\n }\n\n /* Go to result with highest score, if any */\n if (anchors.size) {\n const [[best]] = [...anchors].sort(([, a], [, b]) => b - a)\n best.click()\n }\n\n /* Otherwise omit form submission */\n key.claim()\n }\n break\n\n /* Escape or Tab: close search */\n case \"Escape\":\n case \"Tab\":\n setToggle(\"search\", false)\n setElementFocus(query, false)\n break\n\n /* Vertical arrows: select previous or next search result */\n case \"ArrowUp\":\n case \"ArrowDown\":\n if (typeof active === \"undefined\") {\n setElementFocus(query)\n } else {\n const els = [query, ...getElements(\n \":not(details) > [href], summary, details[open] [href]\",\n result\n )]\n const i = Math.max(0, (\n Math.max(0, els.indexOf(active)) + els.length + (\n key.type === \"ArrowUp\" ? -1 : +1\n )\n ) % els.length)\n setElementFocus(els[i])\n }\n\n /* Prevent scrolling of page */\n key.claim()\n break\n\n /* All other keys: hand to search query */\n default:\n if (query !== getActiveElement())\n setElementFocus(query)\n }\n })\n\n /* Set up global keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\"),\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Open search and select query */\n case \"f\":\n case \"s\":\n case \"/\":\n setElementFocus(query)\n setElementSelection(query)\n key.claim()\n break\n }\n })\n\n /* Create and return component */\n const query$ = mountSearchQuery(query, worker)\n const result$ = mountSearchResult(result, worker, { query$ })\n return merge(query$, result$)\n .pipe(\n mergeWith(\n\n /* Search sharing */\n ...getComponentElements(\"search-share\", el)\n .map(child => mountSearchShare(child, { query$ })),\n\n /* Search suggestions */\n ...getComponentElements(\"search-suggest\", el)\n .map(child => mountSearchSuggest(child, worker, { keyboard$ }))\n )\n )\n\n /* Gracefully handle broken search */\n } catch (err) {\n el.hidden = true\n return NEVER\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n ObservableInput,\n combineLatest\n} from \"rxjs\"\nimport { filter, map, startWith } from \"rxjs/operators\"\n\nimport { getLocation } from \"~/browser\"\nimport {\n SearchIndex,\n setupSearchHighlighter\n} from \"~/integrations\"\nimport { h } from \"~/utilities\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlighting\n */\nexport interface SearchHighlight {\n nodes: Map /* Map of replacements */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n index$: ObservableInput /* Search index observable */\n location$: Observable /* Location observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search highlighting\n *\n * @param el - Content element\n * @param options - Options\n *\n * @returns Search highlighting component observable\n */\nexport function mountSearchHiglight(\n el: HTMLElement, { index$, location$ }: MountOptions\n): Observable> {\n return combineLatest([\n index$,\n location$\n .pipe(\n startWith(getLocation()),\n filter(url => url.searchParams.has(\"h\"))\n )\n ])\n .pipe(\n map(([index, url]) => setupSearchHighlighter(index.config, true)(\n url.searchParams.get(\"h\")!\n )),\n map(fn => {\n const nodes = new Map()\n\n /* Traverse text nodes and collect matches */\n const it = document.createNodeIterator(el, NodeFilter.SHOW_TEXT)\n for (let node = it.nextNode(); node; node = it.nextNode()) {\n if (node.parentElement?.offsetHeight) {\n const original = node.textContent!\n const replaced = fn(original)\n if (replaced.length > original.length)\n nodes.set(node as ChildNode, replaced)\n }\n }\n\n /* Replace original nodes with matches */\n for (const [node, text] of nodes) {\n const { childNodes } = h(\"span\", null, text)\n node.replaceWith(...Array.from(childNodes))\n }\n\n /* Return component */\n return { ref: el, nodes }\n })\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n finalize,\n map,\n observeOn,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n resetSidebarHeight,\n resetSidebarOffset,\n setSidebarHeight,\n setSidebarOffset\n} from \"~/actions\"\nimport { Viewport } from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\nimport { Main } from \"../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Sidebar\n */\nexport interface Sidebar {\n height: number /* Sidebar height */\n locked: boolean /* User scrolled past header */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n main$: Observable
    /* Main area observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch sidebar\n *\n * This function returns an observable that computes the visual parameters of\n * the sidebar which depends on the vertical viewport offset, as well as the\n * height of the main area. When the page is scrolled beyond the header, the\n * sidebar is locked and fills the remaining space.\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @returns Sidebar observable\n */\nexport function watchSidebar(\n el: HTMLElement, { viewport$, main$ }: WatchOptions\n): Observable {\n const adjust =\n el.parentElement!.offsetTop -\n el.parentElement!.parentElement!.offsetTop\n\n /* Compute the sidebar's available height and if it should be locked */\n return combineLatest([main$, viewport$])\n .pipe(\n map(([{ offset, height }, { offset: { y } }]) => {\n height = height\n + Math.min(adjust, Math.max(0, y - offset))\n - adjust\n return {\n height,\n locked: y >= offset + adjust\n }\n }),\n distinctUntilChanged((a, b) => (\n a.height === b.height &&\n a.locked === b.locked\n ))\n )\n}\n\n/**\n * Mount sidebar\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @returns Sidebar component observable\n */\nexport function mountSidebar(\n el: HTMLElement, { header$, ...options }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(header$)\n )\n .subscribe({\n\n /* Update height and offset */\n next([{ height }, { height: offset }]) {\n setSidebarHeight(el, height)\n setSidebarOffset(el, offset)\n },\n\n /* Reset on complete */\n complete() {\n resetSidebarOffset(el)\n resetSidebarHeight(el)\n }\n })\n\n /* Create and return component */\n return watchSidebar(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Repo, User } from \"github-types\"\nimport { Observable, zip } from \"rxjs\"\nimport { defaultIfEmpty, map } from \"rxjs/operators\"\n\nimport { requestJSON } from \"~/browser\"\n\nimport { SourceFacts } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * GitHub release (partial)\n */\ninterface Release {\n tag_name: string /* Tag name */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitHub repository facts\n *\n * @param user - GitHub user\n * @param repo - GitHub repository\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFactsFromGitHub(\n user: string, repo?: string\n): Observable {\n if (typeof repo !== \"undefined\") {\n const url = `https://api.github.com/repos/${user}/${repo}`\n return zip(\n\n /* Fetch version */\n requestJSON(`${url}/releases/latest`)\n .pipe(\n map(release => ({\n version: release.tag_name\n })),\n defaultIfEmpty({})\n ),\n\n /* Fetch stars and forks */\n requestJSON(url)\n .pipe(\n map(info => ({\n stars: info.stargazers_count,\n forks: info.forks_count\n })),\n defaultIfEmpty({})\n )\n )\n .pipe(\n map(([release, info]) => ({ ...release, ...info }))\n )\n\n /* User or organization */\n } else {\n const url = `https://api.github.com/repos/${user}`\n return requestJSON(url)\n .pipe(\n map(info => ({\n repositories: info.public_repos\n })),\n defaultIfEmpty({})\n )\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ProjectSchema } from \"gitlab\"\nimport { Observable } from \"rxjs\"\nimport { defaultIfEmpty, map } from \"rxjs/operators\"\n\nimport { requestJSON } from \"~/browser\"\n\nimport { SourceFacts } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitLab repository facts\n *\n * @param base - GitLab base\n * @param project - GitLab project\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFactsFromGitLab(\n base: string, project: string\n): Observable {\n const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}`\n return requestJSON(url)\n .pipe(\n map(({ star_count, forks_count }) => ({\n stars: star_count,\n forks: forks_count\n })),\n defaultIfEmpty({})\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable } from \"rxjs\"\n\nimport { fetchSourceFactsFromGitHub } from \"../github\"\nimport { fetchSourceFactsFromGitLab } from \"../gitlab\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository facts for repositories\n */\nexport interface RepositoryFacts {\n stars?: number /* Number of stars */\n forks?: number /* Number of forks */\n version?: string /* Latest version */\n}\n\n/**\n * Repository facts for organizations\n */\nexport interface OrganizationFacts {\n repositories?: number /* Number of repositories */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Repository facts\n */\nexport type SourceFacts =\n | RepositoryFacts\n | OrganizationFacts\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch repository facts\n *\n * @param url - Repository URL\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFacts(\n url: string\n): Observable {\n const [type] = url.match(/(git(?:hub|lab))/i) || []\n switch (type.toLowerCase()) {\n\n /* GitHub repository */\n case \"github\":\n const [, user, repo] = url.match(/^.+github\\.com\\/([^/]+)\\/?([^/]+)?/i)!\n return fetchSourceFactsFromGitHub(user, repo)\n\n /* GitLab repository */\n case \"gitlab\":\n const [, base, slug] = url.match(/^.+?([^/]*gitlab[^/]+)\\/(.+?)\\/?$/i)!\n return fetchSourceFactsFromGitLab(base, slug)\n\n /* Everything else */\n default:\n return NEVER\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, Subject, defer, of } from \"rxjs\"\nimport {\n catchError,\n filter,\n finalize,\n map,\n shareReplay,\n tap\n} from \"rxjs/operators\"\n\nimport { setSourceFacts, setSourceState } from \"~/actions\"\nimport { renderSourceFacts } from \"~/templates\"\n\nimport { Component } from \"../../_\"\nimport { SourceFacts, fetchSourceFacts } from \"../facts\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository information\n */\nexport interface Source {\n facts: SourceFacts /* Repository facts */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository information observable\n */\nlet fetch$: Observable\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch repository information\n *\n * This function tries to read the repository facts from session storage, and\n * if unsuccessful, fetches them from the underlying provider.\n *\n * @param el - Repository information element\n *\n * @returns Repository information observable\n */\nexport function watchSource(\n el: HTMLAnchorElement\n): Observable {\n return fetch$ ||= defer(() => {\n const data = sessionStorage.getItem(__prefix(\"__source\"))\n if (data) {\n return of(JSON.parse(data))\n } else {\n const value$ = fetchSourceFacts(el.href)\n value$.subscribe(value => {\n try {\n sessionStorage.setItem(__prefix(\"__source\"), JSON.stringify(value))\n } catch (err) {\n /* Uncritical, just swallow */\n }\n })\n\n /* Return value */\n return value$\n }\n })\n .pipe(\n catchError(() => NEVER),\n filter(facts => Object.keys(facts).length > 0),\n map(facts => ({ facts })),\n shareReplay(1)\n )\n}\n\n/**\n * Mount repository information\n *\n * @param el - Repository information element\n *\n * @returns Repository information component observable\n */\nexport function mountSource(\n el: HTMLAnchorElement\n): Observable> {\n const internal$ = new Subject()\n internal$.subscribe(({ facts }) => {\n setSourceFacts(el, renderSourceFacts(facts))\n setSourceState(el, \"done\")\n })\n\n /* Create and return component */\n return watchSource(el)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n of\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { feature } from \"~/_\"\nimport { resetTabsState, setTabsState } from \"~/actions\"\nimport {\n Viewport,\n watchElementSize,\n watchViewportAt\n} from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Navigation tabs\n */\nexport interface Tabs {\n hidden: boolean /* User scrolled past tabs */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch navigation tabs\n *\n * @param el - Navigation tabs element\n * @param options - Options\n *\n * @returns Navigation tabs observable\n */\nexport function watchTabs(\n el: HTMLElement, { viewport$, header$ }: WatchOptions\n): Observable {\n return watchElementSize(document.body)\n .pipe(\n switchMap(() => watchViewportAt(el, { header$, viewport$ })),\n map(({ offset: { y } }) => {\n return {\n hidden: y >= 10\n }\n }),\n distinctUntilKeyChanged(\"hidden\")\n )\n}\n\n/**\n * Mount navigation tabs\n *\n * This function hides the navigation tabs when scrolling past the threshold\n * and makes them reappear in a nice CSS animation when scrolling back up.\n *\n * @param el - Navigation tabs element\n * @param options - Options\n *\n * @returns Navigation tabs component observable\n */\nexport function mountTabs(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe({\n\n /* Update state */\n next({ hidden }) {\n if (hidden)\n setTabsState(el, \"hidden\")\n else\n resetTabsState(el)\n },\n\n /* Reset on complete */\n complete() {\n resetTabsState(el)\n }\n })\n\n /* Create and return component */\n return (\n feature(\"navigation.tabs.sticky\")\n ? of({ hidden: false })\n : watchTabs(el, options)\n )\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n bufferCount,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n scan,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetAnchorActive,\n resetAnchorState,\n setAnchorActive,\n setAnchorState\n} from \"~/actions\"\nimport {\n Viewport,\n getElement,\n getElements,\n watchElementSize\n} from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Table of contents\n */\nexport interface TableOfContents {\n prev: HTMLAnchorElement[][] /* Anchors (previous) */\n next: HTMLAnchorElement[][] /* Anchors (next) */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch table of contents\n *\n * This is effectively a scroll spy implementation which will account for the\n * fixed header and automatically re-calculate anchor offsets when the viewport\n * is resized. The returned observable will only emit if the table of contents\n * needs to be repainted.\n *\n * This implementation tracks an anchor element's entire path starting from its\n * level up to the top-most anchor element, e.g. `[h3, h2, h1]`. Although the\n * Material theme currently doesn't make use of this information, it enables\n * the styling of the entire hierarchy through customization.\n *\n * Note that the current anchor is the last item of the `prev` anchor list.\n *\n * @param anchors - Anchor elements\n * @param options - Options\n *\n * @returns Table of contents observable\n */\nexport function watchTableOfContents(\n anchors: HTMLAnchorElement[], { viewport$, header$ }: WatchOptions\n): Observable {\n const table = new Map()\n for (const anchor of anchors) {\n const id = decodeURIComponent(anchor.hash.substring(1))\n const target = getElement(`[id=\"${id}\"]`)\n if (typeof target !== \"undefined\")\n table.set(anchor, target)\n }\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(header => 24 + header.height)\n )\n\n /* Compute partition of previous and next anchors */\n const partition$ = watchElementSize(document.body)\n .pipe(\n distinctUntilKeyChanged(\"height\"),\n\n /* Build index to map anchor paths to vertical offsets */\n map(() => {\n let path: HTMLAnchorElement[] = []\n return [...table].reduce((index, [anchor, target]) => {\n while (path.length) {\n const last = table.get(path[path.length - 1])!\n if (last.tagName >= target.tagName) {\n path.pop()\n } else {\n break\n }\n }\n\n /* If the current anchor is hidden, continue with its parent */\n let offset = target.offsetTop\n while (!offset && target.parentElement) {\n target = target.parentElement\n offset = target.offsetTop\n }\n\n /* Map reversed anchor path to vertical offset */\n return index.set(\n [...path = [...path, anchor]].reverse(),\n offset\n )\n }, new Map())\n }),\n\n /* Sort index by vertical offset (see https://bit.ly/30z6QSO) */\n map(index => new Map([...index].sort(([, a], [, b]) => a - b))),\n\n /* Re-compute partition when viewport offset changes */\n switchMap(index => combineLatest([adjust$, viewport$])\n .pipe(\n scan(([prev, next], [adjust, { offset: { y } }]) => {\n\n /* Look forward */\n while (next.length) {\n const [, offset] = next[0]\n if (offset - adjust < y) {\n prev = [...prev, next.shift()!]\n } else {\n break\n }\n }\n\n /* Look backward */\n while (prev.length) {\n const [, offset] = prev[prev.length - 1]\n if (offset - adjust >= y) {\n next = [prev.pop()!, ...next]\n } else {\n break\n }\n }\n\n /* Return partition */\n return [prev, next]\n }, [[], [...index]]),\n distinctUntilChanged((a, b) => (\n a[0] === b[0] &&\n a[1] === b[1]\n ))\n )\n )\n )\n\n /* Compute and return anchor list migrations */\n return partition$\n .pipe(\n map(([prev, next]) => ({\n prev: prev.map(([path]) => path),\n next: next.map(([path]) => path)\n })),\n\n /* Extract anchor list migrations */\n startWith({ prev: [], next: [] }),\n bufferCount(2, 1),\n map(([a, b]) => {\n\n /* Moving down */\n if (a.prev.length < b.prev.length) {\n return {\n prev: b.prev.slice(Math.max(0, a.prev.length - 1), b.prev.length),\n next: []\n }\n\n /* Moving up */\n } else {\n return {\n prev: b.prev.slice(-1),\n next: b.next.slice(0, b.next.length - a.next.length)\n }\n }\n })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Mount table of contents\n *\n * @param el - Anchor list element\n * @param options - Options\n *\n * @returns Table of contents component observable\n */\nexport function mountTableOfContents(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n )\n .subscribe(({ prev, next }) => {\n\n /* Look forward */\n for (const [anchor] of next) {\n resetAnchorActive(anchor)\n resetAnchorState(anchor)\n }\n\n /* Look backward */\n for (const [index, [anchor]] of prev.entries()) {\n setAnchorActive(anchor, index === prev.length - 1)\n setAnchorState(anchor, \"blur\")\n }\n })\n\n /* Create and return component */\n const anchors = getElements(\"[href^=\\\\#]\", el)\n return watchTableOfContents(anchors, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n bufferCount,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n resetBackToTopOffset,\n resetBackToTopState,\n resetFocusable,\n setBackToTopOffset,\n setBackToTopState,\n setFocusable\n} from \"~/actions\"\nimport { Viewport, setElementFocus } from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\nimport { Main } from \"../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Back-to-top button\n */\nexport interface BackToTop {\n hidden: boolean /* User scrolled up */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch back-to-top\n *\n * @param _el - Back-to-top element\n * @param options - Options\n *\n * @returns Back-to-top observable\n */\nexport function watchBackToTop(\n _el: HTMLElement, { viewport$, main$ }: WatchOptions\n): Observable {\n\n /* Compute direction */\n const direction$ = viewport$\n .pipe(\n map(({ offset: { y } }) => y),\n bufferCount(2, 1),\n map(([a, b]) => a > b && b),\n distinctUntilChanged()\n )\n\n /* Compute whether button should be hidden */\n const hidden$ = main$\n .pipe(\n distinctUntilKeyChanged(\"active\")\n )\n\n /* Compute threshold for hiding */\n return combineLatest([hidden$, direction$])\n .pipe(\n map(([{ active }, direction]) => ({\n hidden: !(active && direction)\n })),\n distinctUntilChanged((a, b) => (\n a.hidden === b.hidden\n ))\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Mount back-to-top\n *\n * @param el - Back-to-top element\n * @param options - Options\n *\n * @returns Back-to-top component observable\n */\nexport function mountBackToTop(\n el: HTMLElement, { viewport$, header$, main$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(header$\n .pipe(\n distinctUntilKeyChanged(\"height\")\n )\n )\n )\n .subscribe({\n\n /* Update state */\n next([{ hidden }, { height }]) {\n setBackToTopOffset(el, height + 16)\n if (hidden) {\n setBackToTopState(el, \"hidden\")\n setElementFocus(el, false)\n setFocusable(el, -1)\n } else {\n resetBackToTopState(el)\n resetFocusable(el)\n }\n },\n\n /* Reset on complete */\n complete() {\n resetBackToTopOffset(el)\n resetBackToTopState(el)\n resetFocusable(el)\n }\n })\n\n /* Create and return component */\n return watchBackToTop(el, { viewport$, header$, main$ })\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, of } from \"rxjs\"\nimport {\n mapTo,\n mergeMap,\n switchMap,\n takeWhile,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n tablet$: Observable /* Tablet breakpoint observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch indeterminate checkboxes\n *\n * This function replaces the indeterminate \"pseudo state\" with the actual\n * indeterminate state, which is used to keep navigation always expanded.\n *\n * @param options - Options\n */\nexport function patchIndeterminate(\n { document$, tablet$ }: PatchOptions\n): void {\n document$\n .pipe(\n switchMap(() => of(...getElements(\n \"[data-md-state=indeterminate]\"\n ))),\n tap(el => {\n el.indeterminate = true\n el.checked = false\n }),\n mergeMap(el => fromEvent(el, \"change\")\n .pipe(\n takeWhile(() => el.hasAttribute(\"data-md-state\")),\n mapTo(el)\n )\n ),\n withLatestFrom(tablet$)\n )\n .subscribe(([el, tablet]) => {\n el.removeAttribute(\"data-md-state\")\n if (tablet)\n el.checked = false\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, of } from \"rxjs\"\nimport {\n filter,\n mapTo,\n mergeMap,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether the given device is an Apple device\n *\n * @returns Test result\n */\nfunction isAppleDevice(): boolean {\n return /(iPad|iPhone|iPod)/.test(navigator.userAgent)\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all elements with `data-md-scrollfix` attributes\n *\n * This is a year-old patch which ensures that overflow scrolling works at the\n * top and bottom of containers on iOS by ensuring a `1px` scroll offset upon\n * the start of a touch event.\n *\n * @see https://bit.ly/2SCtAOO - Original source\n *\n * @param options - Options\n */\nexport function patchScrollfix(\n { document$ }: PatchOptions\n): void {\n document$\n .pipe(\n switchMap(() => of(...getElements(\"[data-md-scrollfix]\"))),\n tap(el => el.removeAttribute(\"data-md-scrollfix\")),\n filter(isAppleDevice),\n mergeMap(el => fromEvent(el, \"touchstart\")\n .pipe(\n mapTo(el)\n )\n )\n )\n .subscribe(el => {\n const top = el.scrollTop\n\n /* We're at the top of the container */\n if (top === 0) {\n el.scrollTop = 1\n\n /* We're at the bottom of the container */\n } else if (top + el.offsetHeight === el.scrollHeight) {\n el.scrollTop = top - 1\n }\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n animationFrameScheduler,\n combineLatest,\n of\n} from \"rxjs\"\nimport {\n delay,\n map,\n observeOn,\n switchMap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { resetScrollLock, setScrollLock } from \"~/actions\"\nimport { Viewport, watchToggle } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n viewport$: Observable /* Viewport observable */\n tablet$: Observable /* Tablet breakpoint observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch the document body to lock when search is open\n *\n * For mobile and tablet viewports, the search is rendered full screen, which\n * leads to scroll leaking when at the top or bottom of the search result. This\n * function locks the body when the search is in full screen mode, and restores\n * the scroll position when leaving.\n *\n * @param options - Options\n */\nexport function patchScrolllock(\n { viewport$, tablet$ }: PatchOptions\n): void {\n combineLatest([watchToggle(\"search\"), tablet$])\n .pipe(\n map(([active, tablet]) => active && !tablet),\n switchMap(active => of(active)\n .pipe(\n delay(active ? 400 : 100),\n observeOn(animationFrameScheduler)\n )\n ),\n withLatestFrom(viewport$)\n )\n .subscribe(([active, { offset: { y }}]) => {\n if (active)\n setScrollLock(document.body, y)\n else\n resetScrollLock(document.body)\n })\n}\n"], - "mappings": "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", + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/rxjs/node_modules/tslib/tslib.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/modules/index.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/caughtSchedule.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/fromArray.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/concatMap.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/sample.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/switchMapTo.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/assets/javascripts/browser/document/index.ts", "src/assets/javascripts/browser/element/_/index.ts", "src/assets/javascripts/browser/element/focus/index.ts", "src/assets/javascripts/browser/element/size/index.ts", "src/assets/javascripts/browser/element/offset/index.ts", "src/assets/javascripts/browser/element/selection/index.ts", "src/assets/javascripts/browser/toggle/index.ts", "src/assets/javascripts/browser/keyboard/index.ts", "src/assets/javascripts/browser/location/_/index.ts", "src/assets/javascripts/utilities/h/index.ts", "src/assets/javascripts/utilities/string/index.ts", "src/assets/javascripts/browser/location/hash/index.ts", "src/assets/javascripts/browser/media/index.ts", "src/assets/javascripts/browser/request/index.ts", "src/assets/javascripts/browser/viewport/offset/index.ts", "src/assets/javascripts/browser/viewport/size/index.ts", "src/assets/javascripts/browser/viewport/_/index.ts", "src/assets/javascripts/browser/worker/index.ts", "src/assets/javascripts/_/index.ts", "src/assets/javascripts/components/_/index.ts", "src/assets/javascripts/components/content/code/index.ts", "src/assets/javascripts/actions/_/index.ts", "src/assets/javascripts/actions/anchor/index.ts", "src/assets/javascripts/actions/dialog/index.ts", "src/assets/javascripts/actions/header/_/index.ts", "src/assets/javascripts/actions/header/title/index.ts", "src/assets/javascripts/actions/search/query/index.ts", "src/assets/javascripts/actions/search/result/index.ts", "src/assets/javascripts/actions/sidebar/index.ts", "src/assets/javascripts/actions/source/index.ts", "src/assets/javascripts/actions/tabs/index.ts", "src/assets/javascripts/actions/top/index.ts", "src/assets/javascripts/templates/clipboard/index.tsx", "src/assets/javascripts/templates/search/index.tsx", "src/assets/javascripts/templates/source/index.tsx", "src/assets/javascripts/templates/table/index.tsx", "src/assets/javascripts/templates/version/index.tsx", "src/assets/javascripts/components/content/details/index.ts", "src/assets/javascripts/components/content/table/index.ts", "src/assets/javascripts/components/content/tabs/index.ts", "src/assets/javascripts/components/content/_/index.ts", "src/assets/javascripts/components/dialog/index.ts", "src/assets/javascripts/components/header/_/index.ts", "src/assets/javascripts/components/header/title/index.ts", "src/assets/javascripts/components/main/index.ts", "src/assets/javascripts/components/palette/index.ts", "src/assets/javascripts/integrations/clipboard/index.ts", "src/assets/javascripts/integrations/instant/index.ts", "src/assets/javascripts/integrations/search/document/index.ts", "src/assets/javascripts/integrations/search/highlighter/index.ts", "src/assets/javascripts/integrations/search/query/transform/index.ts", "src/assets/javascripts/integrations/search/worker/message/index.ts", "src/assets/javascripts/integrations/search/worker/_/index.ts", "src/assets/javascripts/integrations/version/index.ts", "src/assets/javascripts/components/search/query/index.ts", "src/assets/javascripts/components/search/result/index.ts", "src/assets/javascripts/components/search/share/index.ts", "src/assets/javascripts/components/search/suggest/index.ts", "src/assets/javascripts/components/search/_/index.ts", "src/assets/javascripts/components/search/highlight/index.ts", "src/assets/javascripts/components/sidebar/index.ts", "src/assets/javascripts/components/source/facts/github/index.ts", "src/assets/javascripts/components/source/facts/gitlab/index.ts", "src/assets/javascripts/components/source/facts/_/index.ts", "src/assets/javascripts/components/source/_/index.ts", "src/assets/javascripts/components/tabs/index.ts", "src/assets/javascripts/components/toc/index.ts", "src/assets/javascripts/components/top/index.ts", "src/assets/javascripts/patches/indeterminate/index.ts", "src/assets/javascripts/patches/scrollfix/index.ts", "src/assets/javascripts/patches/scrolllock/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global global, define, System, Reflect, Promise */\r\nvar __extends;\r\nvar __assign;\r\nvar __rest;\r\nvar __decorate;\r\nvar __param;\r\nvar __metadata;\r\nvar __awaiter;\r\nvar __generator;\r\nvar __exportStar;\r\nvar __values;\r\nvar __read;\r\nvar __spread;\r\nvar __spreadArrays;\r\nvar __spreadArray;\r\nvar __await;\r\nvar __asyncGenerator;\r\nvar __asyncDelegator;\r\nvar __asyncValues;\r\nvar __makeTemplateObject;\r\nvar __importStar;\r\nvar __importDefault;\r\nvar __classPrivateFieldGet;\r\nvar __classPrivateFieldSet;\r\nvar __createBinding;\r\n(function (factory) {\r\n var root = typeof global === \"object\" ? global : typeof self === \"object\" ? self : typeof this === \"object\" ? this : {};\r\n if (typeof define === \"function\" && define.amd) {\r\n define(\"tslib\", [\"exports\"], function (exports) { factory(createExporter(root, createExporter(exports))); });\r\n }\r\n else if (typeof module === \"object\" && typeof module.exports === \"object\") {\r\n factory(createExporter(root, createExporter(module.exports)));\r\n }\r\n else {\r\n factory(createExporter(root));\r\n }\r\n function createExporter(exports, previous) {\r\n if (exports !== root) {\r\n if (typeof Object.create === \"function\") {\r\n Object.defineProperty(exports, \"__esModule\", { value: true });\r\n }\r\n else {\r\n exports.__esModule = true;\r\n }\r\n }\r\n return function (id, v) { return exports[id] = previous ? previous(id, v) : v; };\r\n }\r\n})\r\n(function (exporter) {\r\n var extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n\r\n __extends = function (d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n };\r\n\r\n __assign = Object.assign || function (t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n };\r\n\r\n __rest = function (s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n };\r\n\r\n __decorate = function (decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n };\r\n\r\n __param = function (paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n };\r\n\r\n __metadata = function (metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n };\r\n\r\n __awaiter = function (thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n };\r\n\r\n __generator = function (thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n };\r\n\r\n __exportStar = function(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n };\r\n\r\n __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n }) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n });\r\n\r\n __values = function (o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n };\r\n\r\n __read = function (o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n };\r\n\r\n /** @deprecated */\r\n __spread = function () {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n };\r\n\r\n /** @deprecated */\r\n __spreadArrays = function () {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n };\r\n\r\n __spreadArray = function (to, from) {\r\n for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)\r\n to[j] = from[i];\r\n return to;\r\n };\r\n\r\n __await = function (v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n };\r\n\r\n __asyncGenerator = function (thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n };\r\n\r\n __asyncDelegator = function (o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n };\r\n\r\n __asyncValues = function (o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n };\r\n\r\n __makeTemplateObject = function (cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n };\r\n\r\n var __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n }) : function(o, v) {\r\n o[\"default\"] = v;\r\n };\r\n\r\n __importStar = function (mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n };\r\n\r\n __importDefault = function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n };\r\n\r\n __classPrivateFieldGet = function (receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n };\r\n\r\n __classPrivateFieldSet = function (receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n };\r\n\r\n exporter(\"__extends\", __extends);\r\n exporter(\"__assign\", __assign);\r\n exporter(\"__rest\", __rest);\r\n exporter(\"__decorate\", __decorate);\r\n exporter(\"__param\", __param);\r\n exporter(\"__metadata\", __metadata);\r\n exporter(\"__awaiter\", __awaiter);\r\n exporter(\"__generator\", __generator);\r\n exporter(\"__exportStar\", __exportStar);\r\n exporter(\"__createBinding\", __createBinding);\r\n exporter(\"__values\", __values);\r\n exporter(\"__read\", __read);\r\n exporter(\"__spread\", __spread);\r\n exporter(\"__spreadArrays\", __spreadArrays);\r\n exporter(\"__spreadArray\", __spreadArray);\r\n exporter(\"__await\", __await);\r\n exporter(\"__asyncGenerator\", __asyncGenerator);\r\n exporter(\"__asyncDelegator\", __asyncDelegator);\r\n exporter(\"__asyncValues\", __asyncValues);\r\n exporter(\"__makeTemplateObject\", __makeTemplateObject);\r\n exporter(\"__importStar\", __importStar);\r\n exporter(\"__importDefault\", __importDefault);\r\n exporter(\"__classPrivateFieldGet\", __classPrivateFieldGet);\r\n exporter(\"__classPrivateFieldSet\", __classPrivateFieldSet);\r\n});\r\n", "/*!\n * clipboard.js v2.0.8\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 134:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/clipboard-action.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _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); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n/**\n * Inner class which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n */\n\nvar ClipboardAction = /*#__PURE__*/function () {\n /**\n * @param {Object} options\n */\n function ClipboardAction(options) {\n _classCallCheck(this, ClipboardAction);\n\n this.resolveOptions(options);\n this.initSelection();\n }\n /**\n * Defines base properties passed from constructor.\n * @param {Object} options\n */\n\n\n _createClass(ClipboardAction, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = options.action;\n this.container = options.container;\n this.emitter = options.emitter;\n this.target = options.target;\n this.text = options.text;\n this.trigger = options.trigger;\n this.selectedText = '';\n }\n /**\n * Decides which selection strategy is going to be applied based\n * on the existence of `text` and `target` properties.\n */\n\n }, {\n key: \"initSelection\",\n value: function initSelection() {\n if (this.text) {\n this.selectFake();\n } else if (this.target) {\n this.selectTarget();\n }\n }\n /**\n * Creates a fake textarea element, sets its value from `text` property,\n */\n\n }, {\n key: \"createFakeElement\",\n value: function createFakeElement() {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n this.fakeElem = document.createElement('textarea'); // Prevent zooming on iOS\n\n this.fakeElem.style.fontSize = '12pt'; // Reset box model\n\n this.fakeElem.style.border = '0';\n this.fakeElem.style.padding = '0';\n this.fakeElem.style.margin = '0'; // Move element out of screen horizontally\n\n this.fakeElem.style.position = 'absolute';\n this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n this.fakeElem.style.top = \"\".concat(yPosition, \"px\");\n this.fakeElem.setAttribute('readonly', '');\n this.fakeElem.value = this.text;\n return this.fakeElem;\n }\n /**\n * Get's the value of fakeElem,\n * and makes a selection on it.\n */\n\n }, {\n key: \"selectFake\",\n value: function selectFake() {\n var _this = this;\n\n var fakeElem = this.createFakeElement();\n\n this.fakeHandlerCallback = function () {\n return _this.removeFake();\n };\n\n this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;\n this.container.appendChild(fakeElem);\n this.selectedText = select_default()(fakeElem);\n this.copyText();\n this.removeFake();\n }\n /**\n * Only removes the fake element after another click event, that way\n * a user can hit `Ctrl+C` to copy because selection still exists.\n */\n\n }, {\n key: \"removeFake\",\n value: function removeFake() {\n if (this.fakeHandler) {\n this.container.removeEventListener('click', this.fakeHandlerCallback);\n this.fakeHandler = null;\n this.fakeHandlerCallback = null;\n }\n\n if (this.fakeElem) {\n this.container.removeChild(this.fakeElem);\n this.fakeElem = null;\n }\n }\n /**\n * Selects the content from element passed on `target` property.\n */\n\n }, {\n key: \"selectTarget\",\n value: function selectTarget() {\n this.selectedText = select_default()(this.target);\n this.copyText();\n }\n /**\n * Executes the copy operation based on the current selection.\n */\n\n }, {\n key: \"copyText\",\n value: function copyText() {\n var succeeded;\n\n try {\n succeeded = document.execCommand(this.action);\n } catch (err) {\n succeeded = false;\n }\n\n this.handleResult(succeeded);\n }\n /**\n * Fires an event based on the copy operation result.\n * @param {Boolean} succeeded\n */\n\n }, {\n key: \"handleResult\",\n value: function handleResult(succeeded) {\n this.emitter.emit(succeeded ? 'success' : 'error', {\n action: this.action,\n text: this.selectedText,\n trigger: this.trigger,\n clearSelection: this.clearSelection.bind(this)\n });\n }\n /**\n * Moves focus away from `target` and back to the trigger, removes current selection.\n */\n\n }, {\n key: \"clearSelection\",\n value: function clearSelection() {\n if (this.trigger) {\n this.trigger.focus();\n }\n\n document.activeElement.blur();\n window.getSelection().removeAllRanges();\n }\n /**\n * Sets the `action` to be performed which can be either 'copy' or 'cut'.\n * @param {String} action\n */\n\n }, {\n key: \"destroy\",\n\n /**\n * Destroy lifecycle.\n */\n value: function destroy() {\n this.removeFake();\n }\n }, {\n key: \"action\",\n set: function set() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';\n this._action = action;\n\n if (this._action !== 'copy' && this._action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n }\n }\n /**\n * Gets the `action` property.\n * @return {String}\n */\n ,\n get: function get() {\n return this._action;\n }\n /**\n * Sets the `target` property using an element\n * that will be have its content copied.\n * @param {Element} target\n */\n\n }, {\n key: \"target\",\n set: function set(target) {\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (this.action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n\n this._target = target;\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n }\n }\n /**\n * Gets the `target` property.\n * @return {String|HTMLElement}\n */\n ,\n get: function get() {\n return this._target;\n }\n }]);\n\n return ClipboardAction;\n}();\n\n/* harmony default export */ var clipboard_action = (ClipboardAction);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction clipboard_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction clipboard_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); } }\n\nfunction clipboard_createClass(Constructor, protoProps, staticProps) { if (protoProps) clipboard_defineProperties(Constructor.prototype, protoProps); if (staticProps) clipboard_defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n clipboard_classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n clipboard_createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n\n if (this.clipboardAction) {\n this.clipboardAction = null;\n }\n\n this.clipboardAction = new clipboard_action({\n action: this.action(trigger),\n target: this.target(trigger),\n text: this.text(trigger),\n container: this.container,\n trigger: trigger,\n emitter: this\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n\n if (this.clipboardAction) {\n this.clipboardAction.destroy();\n this.clipboardAction = null;\n }\n }\n }], [{\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(134);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\nimport { NEVER, Subject, defer, merge } from \"rxjs\"\nimport {\n delay,\n filter,\n map,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs/operators\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getElement,\n requestJSON,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountBackToTop,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantLoading,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget()\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? __search?.index || requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up instant loading, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantLoading({ document$, location$, viewport$ })\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector()\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getElement(\"[href][rel=prev]\")\n if (typeof prev !== \"undefined\")\n prev.click()\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getElement(\"[href][rel=next]\")\n if (typeof next !== \"undefined\")\n next.click()\n break\n }\n })\n\n/* Set up patches */\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { target$, viewport$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : NEVER\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, { viewport$, header$ })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Tablet observable */\nwindow.screen$ = screen$ /* Screen observable */\nwindow.print$ = print$ /* Print mode observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.component$ = component$ /* Component observable */\n", "import tslib from '../tslib.js';\r\nconst {\r\n __extends,\r\n __assign,\r\n __rest,\r\n __decorate,\r\n __param,\r\n __metadata,\r\n __awaiter,\r\n __generator,\r\n __exportStar,\r\n __createBinding,\r\n __values,\r\n __read,\r\n __spread,\r\n __spreadArrays,\r\n __spreadArray,\r\n __await,\r\n __asyncGenerator,\r\n __asyncDelegator,\r\n __asyncValues,\r\n __makeTemplateObject,\r\n __importStar,\r\n __importDefault,\r\n __classPrivateFieldGet,\r\n __classPrivateFieldSet,\r\n} = tslib;\r\nexport {\r\n __extends,\r\n __assign,\r\n __rest,\r\n __decorate,\r\n __param,\r\n __metadata,\r\n __awaiter,\r\n __generator,\r\n __exportStar,\r\n __createBinding,\r\n __values,\r\n __read,\r\n __spread,\r\n __spreadArrays,\r\n __spreadArray,\r\n __await,\r\n __asyncGenerator,\r\n __asyncDelegator,\r\n __asyncValues,\r\n __makeTemplateObject,\r\n __importStar,\r\n __importDefault,\r\n __classPrivateFieldGet,\r\n __classPrivateFieldSet,\r\n};\r\n", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ReplaySubject, Subject, fromEvent } from \"rxjs\"\nimport { mapTo } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch document\n *\n * Documents are implemented as subjects, so all downstream observables are\n * automatically updated when a new document is emitted.\n *\n * @returns Document subject\n */\nexport function watchDocument(): Subject {\n const document$ = new ReplaySubject()\n fromEvent(document, \"DOMContentLoaded\")\n .pipe(\n mapTo(document)\n )\n .subscribe(document$)\n\n /* Return document */\n return document$\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve an element matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Element or nothing\n */\nexport function getElement(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T] | undefined\n\nexport function getElement(\n selector: string, node?: ParentNode\n): T | undefined\n\nexport function getElement(\n selector: string, node: ParentNode = document\n): T | undefined {\n return node.querySelector(selector) || undefined\n}\n\n/**\n * Retrieve an element matching a query selector or throw a reference error\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Element\n */\nexport function getElementOrThrow(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T]\n\nexport function getElementOrThrow(\n selector: string, node?: ParentNode\n): T\n\nexport function getElementOrThrow(\n selector: string, node: ParentNode = document\n): T {\n const el = getElement(selector, node)\n if (typeof el === \"undefined\")\n throw new ReferenceError(\n `Missing element: expected \"${selector}\" to be present`\n )\n\n /* Return element */\n return el\n}\n\n/**\n * Retrieve the currently active element\n *\n * @returns Element or nothing\n */\nexport function getActiveElement(): HTMLElement | undefined {\n return document.activeElement instanceof HTMLElement\n ? document.activeElement\n : undefined\n}\n\n/**\n * Retrieve all elements matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @returns Elements\n */\nexport function getElements(\n selector: T, node?: ParentNode\n): HTMLElementTagNameMap[T][]\n\nexport function getElements(\n selector: string, node?: ParentNode\n): T[]\n\nexport function getElements(\n selector: string, node: ParentNode = document\n): T[] {\n return Array.from(node.querySelectorAll(selector))\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Replace an element with the given list of nodes\n *\n * @param el - Element\n * @param nodes - Replacement nodes\n */\nexport function replaceElement(\n el: HTMLElement, ...nodes: Node[]\n): void {\n el.replaceWith(...nodes)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getActiveElement } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element focus\n *\n * @param el - Element\n * @param value - Whether the element should be focused\n */\nexport function setElementFocus(\n el: HTMLElement, value = true\n): void {\n if (value)\n el.focus()\n else\n el.blur()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element focus\n *\n * @param el - Element\n *\n * @returns Element focus observable\n */\nexport function watchElementFocus(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"focus\"),\n fromEvent(el, \"blur\")\n )\n .pipe(\n map(({ type }) => type === \"focus\"),\n startWith(el === getActiveElement())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n Subject,\n defer,\n of\n} from \"rxjs\"\nimport {\n filter,\n finalize,\n map,\n shareReplay,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementSize {\n width: number /* Element width */\n height: number /* Element height */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Resize observer entry subject\n */\nconst entry$ = new Subject()\n\n/**\n * Resize observer observable\n *\n * This observable will create a `ResizeObserver` on the first subscription\n * and will automatically terminate it when there are no more subscribers.\n * It's quite important to centralize observation in a single `ResizeObserver`,\n * as the performance difference can be quite dramatic, as the link shows.\n *\n * @see https://bit.ly/3iIYfEm - Google Groups on performance\n */\nconst observer$ = defer(() => of(\n new ResizeObserver(entries => {\n for (const entry of entries)\n entry$.next(entry)\n })\n))\n .pipe(\n switchMap(resize => NEVER.pipe(startWith(resize))\n .pipe(\n finalize(() => resize.disconnect())\n )\n ),\n shareReplay(1)\n )\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element size\n *\n * @param el - Element\n *\n * @returns Element size\n */\nexport function getElementSize(el: HTMLElement): ElementSize {\n return {\n width: el.offsetWidth,\n height: el.offsetHeight\n }\n}\n\n/**\n * Retrieve element content size, i.e. including overflowing content\n *\n * @param el - Element\n *\n * @returns Element size\n */\nexport function getElementContentSize(el: HTMLElement): ElementSize {\n return {\n width: el.scrollWidth,\n height: el.scrollHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element size\n *\n * This function returns an observable that subscribes to a single internal\n * instance of `ResizeObserver` upon subscription, and emit resize events until\n * termination. Note that this function should not be called with the same\n * element twice, as the first unsubscription will terminate observation.\n *\n * Sadly, we can't use the `DOMRect` objects returned by the observer, because\n * we need the emitted values to be consistent with `getElementSize`, which will\n * return the used values (rounded) and not actual values (unrounded). Thus, we\n * use the `offset*` properties. See the linked GitHub issue.\n *\n * @see https://bit.ly/3m0k3he - GitHub issue\n *\n * @param el - Element\n *\n * @returns Element size observable\n */\nexport function watchElementSize(\n el: HTMLElement\n): Observable {\n return observer$\n .pipe(\n tap(observer => observer.observe(el)),\n switchMap(observer => entry$\n .pipe(\n filter(({ target }) => target === el),\n finalize(() => observer.unobserve(el)),\n map(() => getElementSize(el))\n )\n ),\n startWith(getElementSize(el))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport {\n distinctUntilChanged,\n map,\n startWith\n} from \"rxjs/operators\"\n\nimport {\n getElementContentSize,\n getElementSize\n} from \"../size\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element offset\n *\n * @param el - Element\n *\n * @returns Element offset\n */\nexport function getElementOffset(el: HTMLElement): ElementOffset {\n return {\n x: el.scrollLeft,\n y: el.scrollTop\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element offset\n *\n * @param el - Element\n *\n * @returns Element offset observable\n */\nexport function watchElementOffset(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"scroll\"),\n fromEvent(window, \"resize\")\n )\n .pipe(\n map(() => getElementOffset(el)),\n startWith(getElementOffset(el))\n )\n}\n\n/**\n * Watch element threshold\n *\n * This function returns an observable which emits whether the bottom scroll\n * offset of an elements is within a certain threshold.\n *\n * @param el - Element\n * @param threshold - Threshold\n *\n * @returns Element threshold observable\n */\nexport function watchElementThreshold(\n el: HTMLElement, threshold = 16\n): Observable {\n return watchElementOffset(el)\n .pipe(\n map(({ y }) => {\n const visible = getElementSize(el)\n const content = getElementContentSize(el)\n return y >= (\n content.height - visible.height - threshold\n )\n }),\n distinctUntilChanged()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element text selection\n *\n * @param el - Element\n */\nexport function setElementSelection(\n el: HTMLElement\n): void {\n if (el instanceof HTMLInputElement)\n el.select()\n else\n throw new Error(\"Not implemented\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getElementOrThrow } from \"../element\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle\n */\nexport type Toggle =\n | \"drawer\" /* Toggle for drawer */\n | \"search\" /* Toggle for search */\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle map\n */\nconst toggles: Record = {\n drawer: getElementOrThrow(\"[data-md-toggle=drawer]\"),\n search: getElementOrThrow(\"[data-md-toggle=search]\")\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve the value of a toggle\n *\n * @param name - Toggle\n *\n * @returns Toggle value\n */\nexport function getToggle(name: Toggle): boolean {\n return toggles[name].checked\n}\n\n/**\n * Set toggle\n *\n * Simulating a click event seems to be the most cross-browser compatible way\n * of changing the value while also emitting a `change` event. Before, Material\n * used `CustomEvent` to programmatically change the value of a toggle, but this\n * is a much simpler and cleaner solution which doesn't require a polyfill.\n *\n * @param name - Toggle\n * @param value - Toggle value\n */\nexport function setToggle(name: Toggle, value: boolean): void {\n if (toggles[name].checked !== value)\n toggles[name].click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch toggle\n *\n * @param name - Toggle\n *\n * @returns Toggle value observable\n */\nexport function watchToggle(name: Toggle): Observable {\n const el = toggles[name]\n return fromEvent(el, \"change\")\n .pipe(\n map(() => el.checked),\n startWith(el.checked)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { filter, map, share } from \"rxjs/operators\"\n\nimport { getActiveElement } from \"../element\"\nimport { getToggle } from \"../toggle\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Keyboard mode\n */\nexport type KeyboardMode =\n | \"global\" /* Global */\n | \"search\" /* Search is open */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Keyboard\n */\nexport interface Keyboard {\n mode: KeyboardMode /* Keyboard mode */\n type: string /* Key type */\n claim(): void /* Key claim */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether an element may receive keyboard input\n *\n * @param el - Element\n *\n * @returns Test result\n */\nfunction isSusceptibleToKeyboard(el: HTMLElement): boolean {\n switch (el.tagName) {\n\n /* Form elements */\n case \"INPUT\":\n case \"SELECT\":\n case \"TEXTAREA\":\n return true\n\n /* Everything else */\n default:\n return el.isContentEditable\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch keyboard\n *\n * @returns Keyboard observable\n */\nexport function watchKeyboard(): Observable {\n return fromEvent(window, \"keydown\")\n .pipe(\n filter(ev => !(ev.metaKey || ev.ctrlKey)),\n map(ev => ({\n mode: getToggle(\"search\") ? \"search\" : \"global\",\n type: ev.key,\n claim() {\n ev.preventDefault()\n ev.stopPropagation()\n }\n } as Keyboard)),\n filter(({ mode }) => {\n if (mode === \"global\") {\n const active = getActiveElement()\n if (typeof active !== \"undefined\")\n return !isSusceptibleToKeyboard(active)\n }\n return true\n }),\n share()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Subject } from \"rxjs\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location\n *\n * This function returns a `URL` object (and not `Location`) to normalize the\n * typings across the application. Furthermore, locations need to be tracked\n * without setting them and `Location` is a singleton which represents the\n * current location.\n *\n * @returns URL\n */\nexport function getLocation(): URL {\n return new URL(location.href)\n}\n\n/**\n * Set location\n *\n * @param url - URL to change to\n */\nexport function setLocation(url: URL): void {\n location.href = url.href\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location\n *\n * @returns Location subject\n */\nexport function watchLocation(): Subject {\n return new Subject()\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { JSX as JSXInternal } from \"preact\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * HTML attributes\n */\ntype Attributes =\n & JSXInternal.HTMLAttributes\n & JSXInternal.SVGAttributes\n & Record\n\n/**\n * Child element\n */\ntype Child =\n | HTMLElement\n | Text\n | string\n | number\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Append a child node to an element\n *\n * @param el - Element\n * @param child - Child node(s)\n */\nfunction appendChild(el: HTMLElement, child: Child | Child[]): void {\n\n /* Handle primitive types (including raw HTML) */\n if (typeof child === \"string\" || typeof child === \"number\") {\n el.innerHTML += child.toString()\n\n /* Handle nodes */\n } else if (child instanceof Node) {\n el.appendChild(child)\n\n /* Handle nested children */\n } else if (Array.isArray(child)) {\n for (const node of child)\n appendChild(el, node)\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * JSX factory\n *\n * @template T - Element type\n *\n * @param tag - HTML tag\n * @param attributes - HTML attributes\n * @param children - Child elements\n *\n * @returns Element\n */\nexport function h(\n tag: T, attributes?: Attributes | null, ...children: Child[]\n): HTMLElementTagNameMap[T]\n\nexport function h(\n tag: string, attributes?: Attributes | null, ...children: Child[]\n): T\n\nexport function h(\n tag: string, attributes?: Attributes | null, ...children: Child[]\n): T {\n const el = document.createElement(tag)\n\n /* Set attributes, if any */\n if (attributes)\n for (const attr of Object.keys(attributes))\n if (typeof attributes[attr] !== \"boolean\")\n el.setAttribute(attr, attributes[attr])\n else if (attributes[attr])\n el.setAttribute(attr, \"\")\n\n /* Append child nodes */\n for (const child of children)\n appendChild(el, child)\n\n /* Return element */\n return el as T\n}\n\n/* ----------------------------------------------------------------------------\n * Namespace\n * ------------------------------------------------------------------------- */\n\nexport declare namespace h {\n namespace JSX {\n type Element = HTMLElement\n type IntrinsicElements = JSXInternal.IntrinsicElements\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Truncate a string after the given number of characters\n *\n * This is not a very reasonable approach, since the summaries kind of suck.\n * It would be better to create something more intelligent, highlighting the\n * search occurrences and making a better summary out of it, but this note was\n * written three years ago, so who knows if we'll ever fix it.\n *\n * @param value - Value to be truncated\n * @param n - Number of characters\n *\n * @returns Truncated value\n */\nexport function truncate(value: string, n: number): string {\n let i = n\n if (value.length > i) {\n while (value[i] !== \" \" && --i > 0) { /* keep eating */ }\n return `${value.substring(0, i)}...`\n }\n return value\n}\n\n/**\n * Round a number for display with repository facts\n *\n * This is a reverse-engineered version of GitHub's weird rounding algorithm\n * for stars, forks and all other numbers. While all numbers below `1,000` are\n * returned as-is, bigger numbers are converted to fixed numbers:\n *\n * - `1,049` => `1k`\n * - `1,050` => `1.1k`\n * - `1,949` => `1.9k`\n * - `1,950` => `2k`\n *\n * @param value - Original value\n *\n * @returns Rounded value\n */\nexport function round(value: number): string {\n if (value > 999) {\n const digits = +((value - 950) % 1000 > 99)\n return `${((value + 0.000001) / 1000).toFixed(digits)}k`\n } else {\n return value.toString()\n }\n}\n\n/**\n * Simple hash function\n *\n * @see https://bit.ly/2wsVjJ4 - Original source\n *\n * @param value - Value to be hashed\n *\n * @returns Hash as 32bit integer\n */\nexport function hash(value: string): number {\n let h = 0\n for (let i = 0, len = value.length; i < len; i++) {\n h = ((h << 5) - h) + value.charCodeAt(i)\n h |= 0 // Convert to 32bit integer\n }\n return h\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport {\n filter,\n map,\n shareReplay,\n startWith\n} from \"rxjs/operators\"\n\nimport { getElement } from \"~/browser\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location hash\n *\n * @returns Location hash\n */\nexport function getLocationHash(): string {\n return location.hash.substring(1)\n}\n\n/**\n * Set location hash\n *\n * Setting a new fragment identifier via `location.hash` will have no effect\n * if the value doesn't change. When a new fragment identifier is set, we want\n * the browser to target the respective element at all times, which is why we\n * use this dirty little trick.\n *\n * @param hash - Location hash\n */\nexport function setLocationHash(hash: string): void {\n const el = h(\"a\", { href: hash })\n el.addEventListener(\"click\", ev => ev.stopPropagation())\n el.click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location hash\n *\n * @returns Location hash observable\n */\nexport function watchLocationHash(): Observable {\n return fromEvent(window, \"hashchange\")\n .pipe(\n map(getLocationHash),\n startWith(getLocationHash()),\n filter(hash => hash.length > 0),\n shareReplay(1)\n )\n}\n\n/**\n * Watch location target\n *\n * @returns Location target observable\n */\nexport function watchLocationTarget(): Observable {\n return watchLocationHash()\n .pipe(\n map(id => getElement(`[id=\"${id}\"]`)!),\n filter(el => typeof el !== \"undefined\")\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n fromEvent,\n fromEventPattern\n} from \"rxjs\"\nimport {\n mapTo,\n startWith,\n switchMap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch media query\n *\n * Note that although `MediaQueryList.addListener` is deprecated we have to\n * use it, because it's the only way to ensure proper downward compatibility.\n *\n * @see https://bit.ly/3dUBH2m - GitHub issue\n *\n * @param query - Media query\n *\n * @returns Media observable\n */\nexport function watchMedia(query: string): Observable {\n const media = matchMedia(query)\n return fromEventPattern(next => (\n media.addListener(() => next(media.matches))\n ))\n .pipe(\n startWith(media.matches)\n )\n}\n\n/**\n * Watch print mode, cross-browser\n *\n * @returns Print mode observable\n */\nexport function watchPrint(): Observable {\n return fromEvent(window, \"beforeprint\")\n .pipe(\n mapTo(undefined)\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Toggle an observable with a media observable\n *\n * @template T - Data type\n *\n * @param query$ - Media observable\n * @param factory - Observable factory\n *\n * @returns Toggled observable\n */\nexport function at(\n query$: Observable, factory: () => Observable\n): Observable {\n return query$\n .pipe(\n switchMap(active => active ? factory() : NEVER)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, from } from \"rxjs\"\nimport {\n filter,\n map,\n shareReplay,\n switchMap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch the given URL\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Response observable\n */\nexport function request(\n url: URL | string, options: RequestInit = { credentials: \"same-origin\" }\n): Observable {\n return from(fetch(`${url}`, options))\n .pipe(\n filter(res => res.status === 200),\n )\n}\n\n/**\n * Fetch JSON from the given URL\n *\n * @template T - Data type\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Data observable\n */\nexport function requestJSON(\n url: URL | string, options?: RequestInit\n): Observable {\n return request(url, options)\n .pipe(\n switchMap(res => res.json()),\n shareReplay(1)\n )\n}\n\n/**\n * Fetch XML from the given URL\n *\n * @param url - Request URL\n * @param options - Options\n *\n * @returns Data observable\n */\nexport function requestXML(\n url: URL | string, options?: RequestInit\n): Observable {\n const dom = new DOMParser()\n return request(url, options)\n .pipe(\n switchMap(res => res.text()),\n map(res => dom.parseFromString(res, \"text/xml\")),\n shareReplay(1)\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport offset\n */\nexport interface ViewportOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport offset\n *\n * On iOS Safari, viewport offset can be negative due to overflow scrolling.\n * As this may induce strange behaviors downstream, we'll just limit it to 0.\n *\n * @returns Viewport offset\n */\nexport function getViewportOffset(): ViewportOffset {\n return {\n x: Math.max(0, pageXOffset),\n y: Math.max(0, pageYOffset)\n }\n}\n\n/**\n * Set viewport offset\n *\n * @param offset - Viewport offset\n */\nexport function setViewportOffset(\n { x, y }: Partial\n): void {\n window.scrollTo(x || 0, y || 0)\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport offset\n *\n * @returns Viewport offset observable\n */\nexport function watchViewportOffset(): Observable {\n return merge(\n fromEvent(window, \"scroll\", { passive: true }),\n fromEvent(window, \"resize\", { passive: true })\n )\n .pipe(\n map(getViewportOffset),\n startWith(getViewportOffset())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport size\n */\nexport interface ViewportSize {\n width: number /* Viewport width */\n height: number /* Viewport height */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport size\n *\n * @returns Viewport size\n */\nexport function getViewportSize(): ViewportSize {\n return {\n width: innerWidth,\n height: innerHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport size\n *\n * @returns Viewport size observable\n */\nexport function watchViewportSize(): Observable {\n return fromEvent(window, \"resize\", { passive: true })\n .pipe(\n map(getViewportSize),\n startWith(getViewportSize())\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, combineLatest } from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n map,\n shareReplay\n} from \"rxjs/operators\"\n\nimport { Header } from \"~/components\"\n\nimport {\n ViewportOffset,\n watchViewportOffset\n} from \"../offset\"\nimport {\n ViewportSize,\n watchViewportSize\n} from \"../size\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport\n */\nexport interface Viewport {\n offset: ViewportOffset /* Viewport offset */\n size: ViewportSize /* Viewport size */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch at options\n */\ninterface WatchAtOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport\n *\n * @returns Viewport observable\n */\nexport function watchViewport(): Observable {\n return combineLatest([\n watchViewportOffset(),\n watchViewportSize()\n ])\n .pipe(\n map(([offset, size]) => ({ offset, size })),\n shareReplay(1)\n )\n}\n\n/**\n * Watch viewport relative to element\n *\n * @param el - Element\n * @param options - Options\n *\n * @returns Viewport observable\n */\nexport function watchViewportAt(\n el: HTMLElement, { viewport$, header$ }: WatchAtOptions\n): Observable {\n const size$ = viewport$\n .pipe(\n distinctUntilKeyChanged(\"size\")\n )\n\n /* Compute element offset */\n const offset$ = combineLatest([size$, header$])\n .pipe(\n map((): ViewportOffset => ({\n x: el.offsetLeft,\n y: el.offsetTop\n }))\n )\n\n /* Compute relative viewport, return hot observable */\n return combineLatest([header$, viewport$, offset$])\n .pipe(\n map(([{ height }, { offset, size }, { x, y }]) => ({\n offset: {\n x: offset.x - x,\n y: offset.y - y + height\n },\n size\n }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject, fromEvent } from \"rxjs\"\nimport {\n map,\n share,\n switchMapTo,\n tap,\n throttle\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Worker message\n */\nexport interface WorkerMessage {\n type: unknown /* Message type */\n data?: unknown /* Message data */\n}\n\n/**\n * Worker handler\n *\n * @template T - Message type\n */\nexport interface WorkerHandler<\n T extends WorkerMessage\n> {\n tx$: Subject /* Message transmission subject */\n rx$: Observable /* Message receive observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n *\n * @template T - Worker message type\n */\ninterface WatchOptions {\n tx$: Observable /* Message transmission observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch a web worker\n *\n * This function returns an observable that sends all values emitted by the\n * message observable to the web worker. Web worker communication is expected\n * to be bidirectional (request-response) and synchronous. Messages that are\n * emitted during a pending request are throttled, the last one is emitted.\n *\n * @param worker - Web worker\n * @param options - Options\n *\n * @returns Worker message observable\n */\nexport function watchWorker(\n worker: Worker, { tx$ }: WatchOptions\n): Observable {\n\n /* Intercept messages from worker-like objects */\n const rx$ = fromEvent(worker, \"message\")\n .pipe(\n map(({ data }) => data as T)\n )\n\n /* Send and receive messages, return hot observable */\n return tx$\n .pipe(\n throttle(() => rx$, { leading: true, trailing: true }),\n tap(message => worker.postMessage(message)),\n switchMapTo(rx$),\n share()\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { getElementOrThrow, getLocation } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Feature flag\n */\nexport type Flag =\n | \"header.autohide\" /* Hide header */\n | \"navigation.expand\" /* Automatic expansion */\n | \"navigation.instant\" /* Instant loading */\n | \"navigation.indexes\" /* Section pages */\n | \"navigation.sections\" /* Section navigation */\n | \"navigation.tabs\" /* Tabs navigation */\n | \"navigation.tabs.sticky\" /* Tabs navigation (sticky) */\n | \"navigation.top\" /* Back-to-top button */\n | \"search.highlight\" /* Search highlighting */\n | \"search.share\" /* Search sharing */\n | \"search.suggest\" /* Search suggestions */\n | \"toc.integrate\" /* Integrated table of contents */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Translation\n */\nexport type Translation =\n | \"clipboard.copy\" /* Copy to clipboard */\n | \"clipboard.copied\" /* Copied to clipboard */\n | \"search.config.lang\" /* Search language */\n | \"search.config.pipeline\" /* Search pipeline */\n | \"search.config.separator\" /* Search separator */\n | \"search.placeholder\" /* Search */\n | \"search.result.placeholder\" /* Type to start searching */\n | \"search.result.none\" /* No matching documents */\n | \"search.result.one\" /* 1 matching document */\n | \"search.result.other\" /* # matching documents */\n | \"search.result.more.one\" /* 1 more on this page */\n | \"search.result.more.other\" /* # more on this page */\n | \"search.result.term.missing\" /* Missing */\n | \"select.version.title\" /* Version selector */\n\n/**\n * Translations\n */\nexport type Translations = Record\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Versioning\n */\nexport interface Versioning {\n provider: \"mike\" /* Version provider */\n}\n\n/**\n * Configuration\n */\nexport interface Config {\n base: string /* Base URL */\n features: Flag[] /* Feature flags */\n translations: Translations /* Translations */\n search: string /* Search worker URL */\n version?: Versioning /* Versioning */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve global configuration and make base URL absolute\n */\nconst script = getElementOrThrow(\"#__config\")\nconst config: Config = JSON.parse(script.textContent!)\nconfig.base = `${new URL(config.base, getLocation())}`\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve global configuration\n *\n * @returns Global configuration\n */\nexport function configuration(): Config {\n return config\n}\n\n/**\n * Check whether a feature flag is enabled\n *\n * @param flag - Feature flag\n *\n * @returns Test result\n */\nexport function feature(flag: Flag): boolean {\n return config.features.includes(flag)\n}\n\n/**\n * Retrieve the translation for the given key\n *\n * @param key - Key to be translated\n * @param value - Positional value, if any\n *\n * @returns Translation\n */\nexport function translation(\n key: Translation, value?: string | number\n): string {\n return typeof value !== \"undefined\"\n ? config.translations[key].replace(\"#\", value.toString())\n : config.translations[key]\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { getElementOrThrow, getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Component type\n */\nexport type ComponentType =\n | \"announce\" /* Announcement bar */\n | \"container\" /* Container */\n | \"content\" /* Content */\n | \"dialog\" /* Dialog */\n | \"header\" /* Header */\n | \"header-title\" /* Header title */\n | \"header-topic\" /* Header topic */\n | \"main\" /* Main area */\n | \"palette\" /* Color palette */\n | \"search\" /* Search */\n | \"search-query\" /* Search input */\n | \"search-result\" /* Search results */\n | \"search-share\" /* Search sharing */\n | \"search-suggest\" /* Search suggestions */\n | \"sidebar\" /* Sidebar */\n | \"skip\" /* Skip link */\n | \"source\" /* Repository information */\n | \"tabs\" /* Navigation tabs */\n | \"toc\" /* Table of contents */\n | \"top\" /* Back-to-top button */\n\n/**\n * Component\n *\n * @template T - Component type\n * @template U - Reference type\n */\nexport type Component<\n T extends {} = {},\n U extends HTMLElement = HTMLElement\n> =\n T & {\n ref: U /* Component reference */\n }\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Component type map\n */\ninterface ComponentTypeMap {\n \"announce\": HTMLElement /* Announcement bar */\n \"container\": HTMLElement /* Container */\n \"content\": HTMLElement /* Content */\n \"dialog\": HTMLElement /* Dialog */\n \"header\": HTMLElement /* Header */\n \"header-title\": HTMLElement /* Header title */\n \"header-topic\": HTMLElement /* Header topic */\n \"main\": HTMLElement /* Main area */\n \"palette\": HTMLElement /* Color palette */\n \"search\": HTMLElement /* Search */\n \"search-query\": HTMLInputElement /* Search input */\n \"search-result\": HTMLElement /* Search results */\n \"search-share\": HTMLAnchorElement /* Search sharing */\n \"search-suggest\": HTMLElement /* Search suggestions */\n \"sidebar\": HTMLElement /* Sidebar */\n \"skip\": HTMLAnchorElement /* Skip link */\n \"source\": HTMLAnchorElement /* Repository information */\n \"tabs\": HTMLElement /* Navigation tabs */\n \"toc\": HTMLElement /* Table of contents */\n \"top\": HTMLAnchorElement /* Back-to-top button */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve the element for a given component or throw a reference error\n *\n * @template T - Component type\n *\n * @param type - Component type\n * @param node - Node of reference\n *\n * @returns Element\n */\nexport function getComponentElement(\n type: T, node: ParentNode = document\n): ComponentTypeMap[T] {\n return getElementOrThrow(`[data-md-component=${type}]`, node)\n}\n\n/**\n * Retrieve all elements for a given component\n *\n * @template T - Component type\n *\n * @param type - Component type\n * @param node - Node of reference\n *\n * @returns Elements\n */\nexport function getComponentElements(\n type: T, node: ParentNode = document\n): ComponentTypeMap[T][] {\n return getElements(`[data-md-component=${type}]`, node)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport ClipboardJS from \"clipboard\"\nimport {\n NEVER,\n Observable,\n Subject,\n fromEvent,\n merge,\n of\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n switchMap,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { resetFocusable, setFocusable } from \"~/actions\"\nimport {\n Viewport,\n getElementContentSize,\n getElementSize,\n getElements,\n watchMedia\n} from \"~/browser\"\nimport { renderClipboardButton } from \"~/templates\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Code block\n */\nexport interface CodeBlock {\n scroll: boolean /* Code block overflows */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Global index for Clipboard.js integration\n */\nlet index = 0\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch code block\n *\n * This function monitors size changes of the viewport, as well as switches of\n * content tabs with embedded code blocks, as both may trigger overflow.\n *\n * @param el - Code block element\n * @param options - Options\n *\n * @returns Code block observable\n */\nexport function watchCodeBlock(\n el: HTMLElement, { viewport$ }: WatchOptions\n): Observable {\n const container$ = of(el)\n .pipe(\n switchMap(child => {\n const container = child.closest(\"[data-tabs]\")\n if (container instanceof HTMLElement) {\n return merge(\n ...getElements(\"input\", container)\n .map(input => fromEvent(input, \"change\"))\n )\n }\n return NEVER\n })\n )\n\n /* Check overflow on resize and tab change */\n return merge(\n viewport$.pipe(distinctUntilKeyChanged(\"size\")),\n container$\n )\n .pipe(\n map(() => {\n const visible = getElementSize(el)\n const content = getElementContentSize(el)\n return {\n scroll: content.width > visible.width\n }\n }),\n distinctUntilKeyChanged(\"scroll\")\n )\n}\n\n/**\n * Mount code block\n *\n * This function ensures that an overflowing code block is focusable through\n * keyboard, so it can be scrolled without a mouse to improve on accessibility.\n *\n * @param el - Code block element\n * @param options - Options\n *\n * @returns Code block component observable\n */\nexport function mountCodeBlock(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n withLatestFrom(watchMedia(\"(hover)\"))\n )\n .subscribe(([{ scroll }, hover]) => {\n if (scroll && hover)\n setFocusable(el)\n else\n resetFocusable(el)\n })\n\n /* Render button for Clipboard.js integration */\n if (ClipboardJS.isSupported()) {\n const parent = el.closest(\"pre\")!\n parent.id = `__code_${index++}`\n parent.insertBefore(\n renderClipboardButton(parent.id),\n el\n )\n }\n\n /* Create and return component */\n return watchCodeBlock(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set focusable property\n *\n * @param el - Element\n * @param value - Tabindex value\n */\nexport function setFocusable(\n el: HTMLElement, value = 0\n): void {\n el.setAttribute(\"tabindex\", value.toString())\n}\n\n/**\n * Reset focusable property\n *\n * @param el - Element\n */\nexport function resetFocusable(\n el: HTMLElement\n): void {\n el.removeAttribute(\"tabindex\")\n}\n\n/**\n * Set scroll lock\n *\n * @param el - Scrollable element\n * @param value - Vertical offset\n */\nexport function setScrollLock(\n el: HTMLElement, value: number\n): void {\n el.setAttribute(\"data-md-state\", \"lock\")\n el.style.top = `-${value}px`\n}\n\n/**\n * Reset scroll lock\n *\n * @param el - Scrollable element\n */\nexport function resetScrollLock(\n el: HTMLElement\n): void {\n const value = -1 * parseInt(el.style.top, 10)\n el.removeAttribute(\"data-md-state\")\n el.style.top = \"\"\n if (value)\n window.scrollTo(0, value)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set anchor state\n *\n * @param el - Anchor element\n * @param state - Anchor state\n */\nexport function setAnchorState(\n el: HTMLElement, state: \"blur\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset anchor state\n *\n * @param el - Anchor element\n */\nexport function resetAnchorState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set anchor active\n *\n * @param el - Anchor element\n * @param value - Whether the anchor is active\n */\nexport function setAnchorActive(\n el: HTMLElement, value: boolean\n): void {\n el.classList.toggle(\"md-nav__link--active\", value)\n}\n\n/**\n * Reset anchor active\n *\n * @param el - Anchor element\n */\nexport function resetAnchorActive(\n el: HTMLElement\n): void {\n el.classList.remove(\"md-nav__link--active\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set dialog message\n *\n * @param el - Dialog element\n * @param value - Dialog message\n */\nexport function setDialogMessage(\n el: HTMLElement, value: string\n): void {\n el.firstElementChild!.innerHTML = value\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set dialog state\n *\n * @param el - Dialog element\n * @param state - Dialog state\n */\nexport function setDialogState(\n el: HTMLElement, state: \"open\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset dialog state\n *\n * @param el - Dialog element\n */\nexport function resetDialogState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header state\n *\n * @param el - Header element\n * @param state - Header state\n */\nexport function setHeaderState(\n el: HTMLElement, state: \"shadow\" | \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset header state\n *\n * @param el - Header element\n */\nexport function resetHeaderState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header title state\n *\n * @param el - Header title element\n * @param state - Header title state\n */\nexport function setHeaderTitleState(\n el: HTMLElement, state: \"active\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset header title state\n *\n * @param el - Header title element\n */\nexport function resetHeaderTitleState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set search query placeholder\n *\n * @param el - Search query element\n * @param value - Placeholder\n */\nexport function setSearchQueryPlaceholder(\n el: HTMLInputElement, value: string\n): void {\n el.placeholder = value\n}\n\n/**\n * Reset search query placeholder\n *\n * @param el - Search query element\n */\nexport function resetSearchQueryPlaceholder(\n el: HTMLInputElement\n): void {\n el.placeholder = translation(\"search.placeholder\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\nimport { round } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set number of search results\n *\n * @param el - Search result metadata element\n * @param value - Number of results\n */\nexport function setSearchResultMeta(\n el: HTMLElement, value: number\n): void {\n switch (value) {\n\n /* No results */\n case 0:\n el.textContent = translation(\"search.result.none\")\n break\n\n /* One result */\n case 1:\n el.textContent = translation(\"search.result.one\")\n break\n\n /* Multiple result */\n default:\n el.textContent = translation(\"search.result.other\", round(value))\n }\n}\n\n/**\n * Reset number of search results\n *\n * @param el - Search result metadata element\n */\nexport function resetSearchResultMeta(\n el: HTMLElement\n): void {\n el.textContent = translation(\"search.result.placeholder\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Add an element to the search result list\n *\n * @param el - Search result list element\n * @param child - Search result element\n */\nexport function addToSearchResultList(\n el: HTMLElement, child: Element\n): void {\n el.appendChild(child)\n}\n\n/**\n * Reset search result list\n *\n * @param el - Search result list element\n */\nexport function resetSearchResultList(\n el: HTMLElement\n): void {\n el.innerHTML = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar offset\n *\n * @param el - Sidebar element\n * @param value - Sidebar offset\n */\nexport function setSidebarOffset(\n el: HTMLElement, value: number\n): void {\n el.style.top = `${value}px`\n}\n\n/**\n * Reset sidebar offset\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarOffset(\n el: HTMLElement\n): void {\n el.style.top = \"\"\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar height\n *\n * This function doesn't set the height of the actual sidebar, but of its first\n * child \u2013 the `.md-sidebar__scrollwrap` element in order to mitigiate jittery\n * sidebars when the footer is scrolled into view. At some point we switched\n * from `absolute` / `fixed` positioning to `sticky` positioning, significantly\n * reducing jitter in some browsers (respectively Firefox and Safari) when\n * scrolling from the top. However, top-aligned sticky positioning means that\n * the sidebar snaps to the bottom when the end of the container is reached.\n * This is what leads to the mentioned jitter, as the sidebar's height may be\n * updated too slowly.\n *\n * This behaviour can be mitigiated by setting the height of the sidebar to `0`\n * while preserving the padding, and the height on its first element.\n *\n * @param el - Sidebar element\n * @param value - Sidebar height\n */\nexport function setSidebarHeight(\n el: HTMLElement, value: number\n): void {\n const scrollwrap = el.firstElementChild as HTMLElement\n scrollwrap.style.height = `${value - 2 * scrollwrap.offsetTop}px`\n}\n\n/**\n * Reset sidebar height\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarHeight(\n el: HTMLElement\n): void {\n const scrollwrap = el.firstElementChild as HTMLElement\n scrollwrap.style.height = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set repository facts\n *\n * @param el - Repository element\n * @param child - Repository facts element\n */\nexport function setSourceFacts(\n el: HTMLElement, child: Element\n): void {\n el.lastElementChild!.appendChild(child)\n}\n\n/**\n * Set repository state\n *\n * @param el - Repository element\n * @param state - Repository state\n */\nexport function setSourceState(\n el: HTMLElement, state: \"done\"\n): void {\n el.lastElementChild!.setAttribute(\"data-md-state\", state)\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set tabs state\n *\n * @param el - Tabs element\n * @param state - Tabs state\n */\nexport function setTabsState(\n el: HTMLElement, state: \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset tabs state\n *\n * @param el - Tabs element\n */\nexport function resetTabsState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set back-to-top state\n *\n * @param el - Back-to-top element\n * @param state - Back-to-top state\n */\nexport function setBackToTopState(\n el: HTMLElement, state: \"hidden\"\n): void {\n el.setAttribute(\"data-md-state\", state)\n}\n\n/**\n * Reset back-to-top state\n *\n * @param el - Back-to-top element\n */\nexport function resetBackToTopState(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set back-to-top offset\n *\n * @param el - Back-to-top element\n * @param value - Back-to-top offset\n */\nexport function setBackToTopOffset(\n el: HTMLElement, value: number\n): void {\n el.style.top = `${value}px`\n}\n\n/**\n * Reset back-to-top offset\n *\n * @param el - Back-to-top element\n */\nexport function resetBackToTopOffset(\n el: HTMLElement\n): void {\n el.style.top = \"\"\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translation } from \"~/_\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a 'copy-to-clipboard' button\n *\n * @param id - Unique identifier\n *\n * @returns Element\n */\nexport function renderClipboardButton(id: string): HTMLElement {\n return (\n code`}\n >\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { feature, translation } from \"~/_\"\nimport {\n SearchDocument,\n SearchMetadata,\n SearchResultItem\n} from \"~/integrations/search\"\nimport { h, truncate } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Render flag\n */\nconst enum Flag {\n TEASER = 1, /* Render teaser */\n PARENT = 2 /* Render as parent */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper function\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search document\n *\n * @param document - Search document\n * @param flag - Render flags\n *\n * @returns Element\n */\nfunction renderSearchDocument(\n document: SearchDocument & SearchMetadata, flag: Flag\n): HTMLElement {\n const parent = flag & Flag.PARENT\n const teaser = flag & Flag.TEASER\n\n /* Render missing query terms */\n const missing = Object.keys(document.terms)\n .filter(key => !document.terms[key])\n .map(key => [{key}, \" \"])\n .flat()\n .slice(0, -1)\n\n /* Assemble query string for highlighting */\n const url = new URL(document.location)\n if (feature(\"search.highlight\"))\n url.searchParams.set(\"h\", Object.entries(document.terms)\n .filter(([, match]) => match)\n .reduce((highlight, [value]) => `${highlight} ${value}`.trim(), \"\")\n )\n\n /* Render article or section, depending on flags */\n return (\n \n \n {parent > 0 &&
    }\n

    {document.title}

    \n {teaser > 0 && document.text.length > 0 &&\n

    \n {truncate(document.text, 320)}\n

    \n }\n {teaser > 0 && missing.length > 0 &&\n

    \n {translation(\"search.result.term.missing\")}: {...missing}\n

    \n }\n \n
    \n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search result\n *\n * @param result - Search result\n *\n * @returns Element\n */\nexport function renderSearchResultItem(\n result: SearchResultItem\n): HTMLElement {\n const threshold = result[0].score\n const docs = [...result]\n\n /* Find and extract parent article */\n const parent = docs.findIndex(doc => !doc.location.includes(\"#\"))\n const [article] = docs.splice(parent, 1)\n\n /* Determine last index above threshold */\n let index = docs.findIndex(doc => doc.score < threshold)\n if (index === -1)\n index = docs.length\n\n /* Partition sections */\n const best = docs.slice(0, index)\n const more = docs.slice(index)\n\n /* Render children */\n const children = [\n renderSearchDocument(article, Flag.PARENT | +(!parent && index === 0)),\n ...best.map(section => renderSearchDocument(section, Flag.TEASER)),\n ...more.length ? [\n
    \n \n {more.length > 0 && more.length === 1\n ? translation(\"search.result.more.one\")\n : translation(\"search.result.more.other\", more.length)\n }\n \n {...more.map(section => renderSearchDocument(section, Flag.TEASER))}\n
    \n ] : []\n ]\n\n /* Render search result */\n return (\n
  • \n {children}\n
  • \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SourceFacts } from \"~/components\"\nimport { h, round } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render repository facts\n *\n * @param facts - Repository facts\n *\n * @returns Element\n */\nexport function renderSourceFacts(facts: SourceFacts): HTMLElement {\n return (\n
      \n {Object.entries(facts).map(([key, value]) => (\n
    • \n {typeof value === \"number\" ? round(value) : value}\n
    • \n ))}\n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a table inside a wrapper to improve scrolling on mobile\n *\n * @param table - Table element\n *\n * @returns Element\n */\nexport function renderTable(table: HTMLElement): HTMLElement {\n return (\n
    \n
    \n {table}\n
    \n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { configuration, translation } from \"~/_\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Version\n */\nexport interface Version {\n version: string /* Version identifier */\n title: string /* Version title */\n aliases: string[] /* Version aliases */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a version\n *\n * @param version - Version\n *\n * @returns Element\n */\nfunction renderVersion(version: Version): HTMLElement {\n const config = configuration()\n\n /* Ensure trailing slash, see https://bit.ly/3rL5u3f */\n const url = new URL(`../${version.version}/`, config.base)\n return (\n
  • \n \n {version.title}\n \n
  • \n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a version selector\n *\n * @param versions - Versions\n *\n * @returns Element\n */\nexport function renderVersionSelector(versions: Version[]): HTMLElement {\n const config = configuration()\n\n /* Determine active version */\n const [, current] = config.base.match(/([^/]+)\\/?$/)!\n const active =\n versions.find(({ version, aliases }) => (\n version === current || aliases.includes(current)\n )) || versions[0]\n\n /* Render version selector */\n return (\n
    \n \n {active.title}\n \n
      \n {versions.map(renderVersion)}\n
    \n
    \n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject } from \"rxjs\"\nimport {\n filter,\n finalize,\n map,\n mapTo,\n mergeWith,\n tap\n} from \"rxjs/operators\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Details\n */\nexport interface Details {\n scroll?: boolean /* Scroll into view */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n target$: Observable /* Location target observable */\n print$: Observable /* Print mode observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n target$: Observable /* Location target observable */\n print$: Observable /* Print mode observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch details\n *\n * @param el - Details element\n * @param options - Options\n *\n * @returns Details observable\n */\nexport function watchDetails(\n el: HTMLDetailsElement, { target$, print$ }: WatchOptions\n): Observable
    {\n return target$\n .pipe(\n map(target => target.closest(\"details:not([open])\")!),\n filter(details => el === details),\n mapTo({ scroll: true }),\n mergeWith(print$.pipe(mapTo({})))\n )\n}\n\n/**\n * Mount details\n *\n * This function ensures that `details` tags are opened on anchor jumps and\n * prior to printing, so the whole content of the page is visible.\n *\n * @param el - Details element\n * @param options - Options\n *\n * @returns Details component observable\n */\nexport function mountDetails(\n el: HTMLDetailsElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject
    ()\n internal$.subscribe(({ scroll }) => {\n el.setAttribute(\"open\", \"\")\n if (scroll)\n el.scrollIntoView()\n })\n\n /* Create and return component */\n return watchDetails(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n mapTo({ ref: el })\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, of } from \"rxjs\"\n\nimport { replaceElement } from \"~/browser\"\nimport { renderTable } from \"~/templates\"\nimport { h } from \"~/utilities\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Data table\n */\nexport interface DataTable {}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Sentinel for replacement\n */\nconst sentinel = h(\"table\")\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount data table\n *\n * This function wraps a data table in another scrollable container, so it can\n * be smoothly scrolled on smaller screen sizes and won't break the layout.\n *\n * @param el - Data table element\n *\n * @returns Data table component observable\n */\nexport function mountDataTable(\n el: HTMLElement\n): Observable> {\n replaceElement(el, sentinel)\n replaceElement(sentinel, renderTable(el))\n\n /* Create and return component */\n return of({ ref: el })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, Subject, fromEvent, merge } from \"rxjs\"\nimport { finalize, map, mapTo, tap } from \"rxjs/operators\"\n\nimport { getElementOrThrow, getElements } from \"~/browser\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Content tabs\n */\nexport interface ContentTabs {\n active: HTMLLabelElement /* Active tab label */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch content tabs\n *\n * @param el - Content tabs element\n *\n * @returns Content tabs observable\n */\nexport function watchContentTabs(\n el: HTMLElement\n): Observable {\n if (!el.classList.contains(\".tabbed-alternate\"))\n return NEVER\n else\n return merge(...getElements(\":scope > input\", el)\n .map(input => fromEvent(input, \"change\").pipe(mapTo(input.id)))\n )\n .pipe(\n map(id => ({\n active: getElementOrThrow(`label[for=${id}]`)\n }))\n )\n}\n\n/**\n * Mount content tabs\n *\n * @param el - Content tabs element\n *\n * @returns Content tabs component observable\n */\nexport function mountContentTabs(\n el: HTMLElement\n): Observable> {\n const internal$ = new Subject()\n internal$.subscribe(({ active }) => {\n active.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" })\n })\n\n /* Create and return component */\n return watchContentTabs(el)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, merge } from \"rxjs\"\n\nimport { Viewport, getElements } from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { CodeBlock, mountCodeBlock } from \"../code\"\nimport { Details, mountDetails } from \"../details\"\nimport { DataTable, mountDataTable } from \"../table\"\nimport { ContentTabs, mountContentTabs } from \"../tabs\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Content\n */\nexport type Content =\n | ContentTabs\n | CodeBlock\n | DataTable\n | Details\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n target$: Observable /* Location target observable */\n viewport$: Observable /* Viewport observable */\n print$: Observable /* Print mode observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount content\n *\n * This function mounts all components that are found in the content of the\n * actual article, including code blocks, data tables and details.\n *\n * @param el - Content element\n * @param options - Options\n *\n * @returns Content component observable\n */\nexport function mountContent(\n el: HTMLElement, { target$, viewport$, print$ }: MountOptions\n): Observable> {\n return merge(\n\n /* Code blocks */\n ...getElements(\"pre > code\", el)\n .map(child => mountCodeBlock(child, { viewport$ })),\n\n /* Data tables */\n ...getElements(\"table:not([class])\", el)\n .map(child => mountDataTable(child)),\n\n /* Details */\n ...getElements(\"details\", el)\n .map(child => mountDetails(child, { target$, print$ })),\n\n /* Content tabs */\n ...getElements(\"[data-tabs]\", el)\n .map(child => mountContentTabs(child))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n merge,\n of\n} from \"rxjs\"\nimport {\n delay,\n finalize,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetDialogState,\n setDialogMessage,\n setDialogState\n} from \"~/actions\"\n\nimport { Component } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Dialog\n */\nexport interface Dialog {\n message: string /* Dialog message */\n open: boolean /* Dialog is visible */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n alert$: Subject /* Alert subject */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n alert$: Subject /* Alert subject */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch dialog\n *\n * @param _el - Dialog element\n * @param options - Options\n *\n * @returns Dialog observable\n */\nexport function watchDialog(\n _el: HTMLElement, { alert$ }: WatchOptions\n): Observable {\n return alert$\n .pipe(\n switchMap(message => merge(\n of(true),\n of(false).pipe(delay(2000))\n )\n .pipe(\n map(open => ({ message, open }))\n )\n )\n )\n}\n\n/**\n * Mount dialog\n *\n * This function reveals the dialog in the right cornerwhen a new alert is\n * emitted through the subject that is passed as part of the options.\n *\n * @param el - Dialog element\n * @param options - Options\n *\n * @returns Dialog component observable\n */\nexport function mountDialog(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe(({ message, open }) => {\n setDialogMessage(el, message)\n if (open)\n setDialogState(el, \"open\")\n else\n resetDialogState(el)\n })\n\n /* Create and return component */\n return watchDialog(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest,\n defer,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n combineLatestWith,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n map,\n observeOn,\n shareReplay,\n startWith,\n switchMap\n} from \"rxjs/operators\"\n\nimport { feature } from \"~/_\"\nimport { resetHeaderState, setHeaderState } from \"~/actions\"\nimport {\n Viewport,\n watchElementSize,\n watchToggle\n} from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { Main } from \"../../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Header\n */\nexport interface Header {\n height: number /* Header visible height */\n sticky: boolean /* Header stickyness */\n hidden: boolean /* User scrolled past threshold */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Compute whether the header is hidden\n *\n * If the user scrolls past a certain threshold, the header can be hidden when\n * scrolling down, and shown when scrolling up.\n *\n * @param options - Options\n *\n * @returns Toggle observable\n */\nfunction isHidden({ viewport$ }: WatchOptions): Observable {\n if (!feature(\"header.autohide\"))\n return of(false)\n\n /* Compute direction and turning point */\n const direction$ = viewport$\n .pipe(\n map(({ offset: { y } }) => y),\n bufferCount(2, 1),\n map(([a, b]) => [a < b, b] as const),\n distinctUntilKeyChanged(0)\n )\n\n /* Compute whether header should be hidden */\n const hidden$ = combineLatest([viewport$, direction$])\n .pipe(\n filter(([{ offset }, [, y]]) => Math.abs(y - offset.y) > 100),\n map(([, [direction]]) => direction),\n distinctUntilChanged()\n )\n\n /* Compute threshold for hiding */\n const search$ = watchToggle(\"search\")\n return combineLatest([viewport$, search$])\n .pipe(\n map(([{ offset }, search]) => offset.y > 400 && !search),\n distinctUntilChanged(),\n switchMap(active => active ? hidden$ : of(false)),\n startWith(false)\n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch header\n *\n * @param el - Header element\n * @param options - Options\n *\n * @returns Header observable\n */\nexport function watchHeader(\n el: HTMLElement, options: WatchOptions\n): Observable
    {\n return defer(() => {\n const styles = getComputedStyle(el)\n return of(\n styles.position === \"sticky\" ||\n styles.position === \"-webkit-sticky\"\n )\n })\n .pipe(\n combineLatestWith(watchElementSize(el), isHidden(options)),\n map(([sticky, { height }, hidden]) => ({\n height: sticky ? height : 0,\n sticky,\n hidden\n })),\n distinctUntilChanged((a, b) => (\n a.sticky === b.sticky &&\n a.height === b.height &&\n a.hidden === b.hidden\n )),\n shareReplay(1)\n )\n}\n\n/**\n * Mount header\n *\n * This function manages the different states of the header, i.e. whether it's\n * hidden or rendered with a shadow. This depends heavily on the main area.\n *\n * @param el - Header element\n * @param options - Options\n *\n * @returns Header component observable\n */\nexport function mountHeader(\n el: HTMLElement, { header$, main$ }: MountOptions\n): Observable> {\n const internal$ = new Subject
    ()\n internal$\n .pipe(\n distinctUntilKeyChanged(\"active\"),\n combineLatestWith(header$),\n observeOn(animationFrameScheduler)\n )\n .subscribe(([{ active }, { hidden }]) => {\n if (active)\n setHeaderState(el, hidden ? \"hidden\" : \"shadow\")\n else\n resetHeaderState(el)\n })\n\n /* Connect to long-living subject and return component */\n main$.subscribe(main => internal$.next(main))\n return header$\n .pipe(\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n NEVER,\n Observable,\n Subject,\n animationFrameScheduler\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetHeaderTitleState,\n setHeaderTitleState\n} from \"~/actions\"\nimport {\n Viewport,\n getElement,\n getElementSize,\n watchViewportAt\n} from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { Header } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Header\n */\nexport interface HeaderTitle {\n active: boolean /* User scrolled past first headline */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch header title\n *\n * @param el - Heading element\n * @param options - Options\n *\n * @returns Header title observable\n */\nexport function watchHeaderTitle(\n el: HTMLHeadingElement, { viewport$, header$ }: WatchOptions\n): Observable {\n return watchViewportAt(el, { header$, viewport$ })\n .pipe(\n map(({ offset: { y } }) => {\n const { height } = getElementSize(el)\n return {\n active: y >= height\n }\n }),\n distinctUntilKeyChanged(\"active\")\n )\n}\n\n/**\n * Mount header title\n *\n * This function swaps the header title from the site title to the title of the\n * current page when the user scrolls past the first headline.\n *\n * @param el - Header title element\n * @param options - Options\n *\n * @returns Header title component observable\n */\nexport function mountHeaderTitle(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe(({ active }) => {\n if (active)\n setHeaderTitleState(el, \"active\")\n else\n resetHeaderTitleState(el)\n })\n\n /* Obtain headline, if any */\n const headline = getElement(\"article h1\")\n if (typeof headline === \"undefined\")\n return NEVER\n\n /* Create and return component */\n return watchHeaderTitle(headline, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n combineLatest\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n distinctUntilKeyChanged,\n map,\n switchMap\n} from \"rxjs/operators\"\n\nimport { Viewport, watchElementSize } from \"~/browser\"\n\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Main area\n */\nexport interface Main {\n offset: number /* Main area top offset */\n height: number /* Main area visible height */\n active: boolean /* User scrolled past header */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch main area\n *\n * This function returns an observable that computes the visual parameters of\n * the main area which depends on the viewport vertical offset and height, as\n * well as the height of the header element, if the header is fixed.\n *\n * @param el - Main area element\n * @param options - Options\n *\n * @returns Main area observable\n */\nexport function watchMain(\n el: HTMLElement, { viewport$, header$ }: WatchOptions\n): Observable
    {\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(({ height }) => height),\n distinctUntilChanged()\n )\n\n /* Compute the main area's top and bottom borders */\n const border$ = adjust$\n .pipe(\n switchMap(() => watchElementSize(el)\n .pipe(\n map(({ height }) => ({\n top: el.offsetTop,\n bottom: el.offsetTop + height\n })),\n distinctUntilKeyChanged(\"bottom\")\n )\n )\n )\n\n /* Compute the main area's offset, visible height and if we scrolled past */\n return combineLatest([adjust$, border$, viewport$])\n .pipe(\n map(([header, { top, bottom }, { offset: { y }, size: { height } }]) => {\n height = Math.max(0, height\n - Math.max(0, top - y, header)\n - Math.max(0, height + y - bottom)\n )\n return {\n offset: top - header,\n height,\n active: top - header <= y\n }\n }),\n distinctUntilChanged((a, b) => (\n a.offset === b.offset &&\n a.height === b.height &&\n a.active === b.active\n ))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n fromEvent,\n of\n} from \"rxjs\"\nimport {\n finalize,\n map,\n mapTo,\n mergeMap,\n shareReplay,\n startWith,\n tap\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\nimport { Component } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Palette colors\n */\nexport interface PaletteColor {\n scheme?: string /* Color scheme */\n primary?: string /* Primary color */\n accent?: string /* Accent color */\n}\n\n/**\n * Palette\n */\nexport interface Palette {\n index: number /* Palette index */\n color: PaletteColor /* Palette colors */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch color palette\n *\n * @param inputs - Color palette element\n *\n * @returns Color palette observable\n */\nexport function watchPalette(\n inputs: HTMLInputElement[]\n): Observable {\n const data = localStorage.getItem(__prefix(\"__palette\"))!\n const current = JSON.parse(data) || {\n index: inputs.findIndex(input => (\n matchMedia(input.getAttribute(\"data-md-color-media\")!).matches\n ))\n }\n\n /* Emit changes in color palette */\n const palette$ = of(...inputs)\n .pipe(\n mergeMap(input => fromEvent(input, \"change\")\n .pipe(\n mapTo(input)\n )\n ),\n startWith(inputs[Math.max(0, current.index)]),\n map(input => ({\n index: inputs.indexOf(input),\n color: {\n scheme: input.getAttribute(\"data-md-color-scheme\"),\n primary: input.getAttribute(\"data-md-color-primary\"),\n accent: input.getAttribute(\"data-md-color-accent\")\n }\n } as Palette)),\n shareReplay(1)\n )\n\n /* Persist preference in local storage */\n palette$.subscribe(palette => {\n localStorage.setItem(__prefix(\"__palette\"), JSON.stringify(palette))\n })\n\n /* Return palette */\n return palette$\n}\n\n/**\n * Mount color palette\n *\n * @param el - Color palette element\n *\n * @returns Color palette component observable\n */\nexport function mountPalette(\n el: HTMLElement\n): Observable> {\n const internal$ = new Subject()\n\n /* Set color palette */\n internal$.subscribe(palette => {\n for (const [key, value] of Object.entries(palette.color))\n if (typeof value === \"string\")\n document.body.setAttribute(`data-md-color-${key}`, value)\n\n /* Toggle visibility */\n for (let index = 0; index < inputs.length; index++) {\n const label = inputs[index].nextElementSibling\n if (label instanceof HTMLElement)\n label.hidden = palette.index !== index\n }\n })\n\n /* Create and return component */\n const inputs = getElements(\"input\", el)\n return watchPalette(inputs)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport ClipboardJS from \"clipboard\"\nimport { Observable, Subject } from \"rxjs\"\n\nimport { translation } from \"~/_\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n alert$: Subject /* Alert subject */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up Clipboard.js integration\n *\n * @param options - Options\n */\nexport function setupClipboardJS(\n { alert$ }: SetupOptions\n): void {\n if (ClipboardJS.isSupported()) {\n new Observable(subscriber => {\n new ClipboardJS(\"[data-clipboard-target], [data-clipboard-text]\")\n .on(\"success\", ev => subscriber.next(ev))\n })\n .subscribe(() => alert$.next(translation(\"clipboard.copied\")))\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n fromEvent,\n merge,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n catchError,\n concatMap,\n debounceTime,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n map,\n sample,\n share,\n skip,\n skipUntil,\n switchMap\n} from \"rxjs/operators\"\n\nimport { configuration, feature } from \"~/_\"\nimport {\n Viewport,\n ViewportOffset,\n getElement,\n getElements,\n replaceElement,\n request,\n requestXML,\n setLocation,\n setLocationHash,\n setViewportOffset\n} from \"~/browser\"\nimport { getComponentElement } from \"~/components\"\nimport { h } from \"~/utilities\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * History state\n */\nexport interface HistoryState {\n url: URL /* State URL */\n offset?: ViewportOffset /* State viewport offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n document$: Subject /* Document subject */\n location$: Subject /* Location subject */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Preprocess a list of URLs\n *\n * This function replaces the `site_url` in the sitemap with the actual base\n * URL, to allow instant loading to work in occasions like Netlify previews.\n *\n * @param urls - URLs\n *\n * @returns Processed URLs\n */\nfunction preprocess(urls: string[]): string[] {\n if (urls.length < 2)\n return urls\n\n /* Take the first two URLs and remove everything after the last slash */\n const [root, next] = urls\n .sort((a, b) => a.length - b.length)\n .map(url => url.replace(/[^/]+$/, \"\"))\n\n /* Compute common prefix */\n let index = 0\n if (root === next)\n index = root.length\n else\n while (root.charCodeAt(index) === next.charCodeAt(index))\n index++\n\n /* Replace common prefix (i.e. base) with effective base */\n const config = configuration()\n return urls.map(url => (\n url.replace(root.slice(0, index), config.base)\n ))\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up instant loading\n *\n * When fetching, theoretically, we could use `responseType: \"document\"`, but\n * since all MkDocs links are relative, we need to make sure that the current\n * location matches the document we just loaded. Otherwise any relative links\n * in the document could use the old location.\n *\n * This is the reason why we need to synchronize history events and the process\n * of fetching the document for navigation changes (except `popstate` events):\n *\n * 1. Fetch document via `XMLHTTPRequest`\n * 2. Set new location via `history.pushState`\n * 3. Parse and emit fetched document\n *\n * For `popstate` events, we must not use `history.pushState`, or the forward\n * history will be irreversibly overwritten. In case the request fails, the\n * location change is dispatched regularly.\n *\n * @param options - Options\n */\nexport function setupInstantLoading(\n { document$, location$, viewport$ }: SetupOptions\n): void {\n const config = configuration()\n if (location.protocol === \"file:\")\n return\n\n /* Disable automatic scroll restoration */\n if (\"scrollRestoration\" in history) {\n history.scrollRestoration = \"manual\"\n\n /* Hack: ensure that reloads restore viewport offset */\n fromEvent(window, \"beforeunload\")\n .subscribe(() => {\n history.scrollRestoration = \"auto\"\n })\n }\n\n /* Hack: ensure absolute favicon link to omit 404s when switching */\n const favicon = getElement(\"link[rel=icon]\")\n if (typeof favicon !== \"undefined\")\n favicon.href = favicon.href\n\n /* Intercept internal navigation */\n const push$ = requestXML(new URL(\"sitemap.xml\", config.base))\n .pipe(\n map(sitemap => preprocess(getElements(\"loc\", sitemap)\n .map(node => node.textContent!)\n )),\n switchMap(urls => fromEvent(document.body, \"click\")\n .pipe(\n filter(ev => !ev.metaKey && !ev.ctrlKey),\n switchMap(ev => {\n\n /* Handle HTML and SVG elements */\n if (ev.target instanceof Element) {\n const el = ev.target.closest(\"a\")\n if (el && !el.target) {\n const url = new URL(el.href)\n\n /* Canonicalize URL */\n url.search = \"\"\n url.hash = \"\"\n\n /* Check if URL should be intercepted */\n if (\n url.pathname !== location.pathname &&\n urls.includes(url.toString())\n ) {\n ev.preventDefault()\n return of({\n url: new URL(el.href)\n })\n }\n }\n }\n return NEVER\n })\n )\n ),\n share()\n )\n\n /* Intercept history back and forward */\n const pop$ = fromEvent(window, \"popstate\")\n .pipe(\n filter(ev => ev.state !== null),\n map(ev => ({\n url: new URL(location.href),\n offset: ev.state\n })),\n share()\n )\n\n /* Emit location change */\n merge(push$, pop$)\n .pipe(\n distinctUntilChanged((a, b) => a.url.href === b.url.href),\n map(({ url }) => url)\n )\n .subscribe(location$)\n\n /* Fetch document via `XMLHTTPRequest` */\n const response$ = location$\n .pipe(\n distinctUntilKeyChanged(\"pathname\"),\n switchMap(url => request(url.href)\n .pipe(\n catchError(() => {\n setLocation(url)\n return NEVER\n })\n )\n ),\n share()\n )\n\n /* Set new location via `history.pushState` */\n push$\n .pipe(\n sample(response$)\n )\n .subscribe(({ url }) => {\n history.pushState({}, \"\", `${url}`)\n })\n\n /* Parse and emit fetched document */\n const dom = new DOMParser()\n response$\n .pipe(\n switchMap(res => res.text()),\n map(res => dom.parseFromString(res, \"text/html\"))\n )\n .subscribe(document$)\n\n /* Replace meta tags and components */\n document$\n .pipe(\n skip(1)\n )\n .subscribe(replacement => {\n for (const selector of [\n\n /* Meta tags */\n \"title\",\n \"link[rel=canonical]\",\n \"meta[name=author]\",\n \"meta[name=description]\",\n\n /* Components */\n \"[data-md-component=announce]\",\n \"[data-md-component=container]\",\n \"[data-md-component=header-topic]\",\n \"[data-md-component=logo], .md-logo\", // compat\n \"[data-md-component=skip]\",\n ...feature(\"navigation.tabs.sticky\")\n ? [\"[data-md-component=tabs]\"]\n : []\n ]) {\n const source = getElement(selector)\n const target = getElement(selector, replacement)\n if (\n typeof source !== \"undefined\" &&\n typeof target !== \"undefined\"\n ) {\n replaceElement(source, target)\n }\n }\n })\n\n /* Re-evaluate scripts */\n document$\n .pipe(\n skip(1),\n map(() => getComponentElement(\"container\")),\n switchMap(el => of(...getElements(\"script\", el))),\n concatMap(el => {\n const script = h(\"script\")\n if (el.src) {\n for (const name of el.getAttributeNames())\n script.setAttribute(name, el.getAttribute(name)!)\n replaceElement(el, script)\n\n /* Complete when script is loaded */\n return new Observable(observer => {\n script.onload = () => observer.complete()\n })\n\n /* Complete immediately */\n } else {\n script.textContent = el.textContent\n replaceElement(el, script)\n return EMPTY\n }\n })\n )\n .subscribe()\n\n /* Emit history state change */\n merge(push$, pop$)\n .pipe(\n sample(document$),\n )\n .subscribe(({ url, offset }) => {\n if (url.hash && !offset) {\n setLocationHash(url.hash)\n } else {\n setViewportOffset(offset || { y: 0 })\n }\n })\n\n /* Debounce update of viewport offset */\n viewport$\n .pipe(\n skipUntil(push$),\n debounceTime(250),\n distinctUntilKeyChanged(\"offset\")\n )\n .subscribe(({ offset }) => {\n history.replaceState(offset, \"\")\n })\n\n /* Set viewport offset from history */\n merge(push$, pop$)\n .pipe(\n bufferCount(2, 1),\n filter(([a, b]) => a.url.pathname === b.url.pathname),\n map(([, state]) => state)\n )\n .subscribe(({ offset }) => {\n setViewportOffset(offset || { y: 0 })\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexDocument } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search document\n */\nexport interface SearchDocument extends SearchIndexDocument {\n parent?: SearchIndexDocument /* Parent article */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search document mapping\n */\nexport type SearchDocumentMap = Map\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search document mapping\n *\n * @param docs - Search index documents\n *\n * @returns Search document map\n */\nexport function setupSearchDocumentMap(\n docs: SearchIndexDocument[]\n): SearchDocumentMap {\n const documents = new Map()\n const parents = new Set()\n for (const doc of docs) {\n const [path, hash] = doc.location.split(\"#\")\n\n /* Extract location and title */\n const location = doc.location\n const title = doc.title\n\n /* Escape and cleanup text */\n const text = escapeHTML(doc.text)\n .replace(/\\s+(?=[,.:;!?])/g, \"\")\n .replace(/\\s+/g, \" \")\n\n /* Handle section */\n if (hash) {\n const parent = documents.get(path)!\n\n /* Ignore first section, override article */\n if (!parents.has(parent)) {\n parent.title = doc.title\n parent.text = text\n\n /* Remember that we processed the article */\n parents.add(parent)\n\n /* Add subsequent section */\n } else {\n documents.set(location, {\n location,\n title,\n text,\n parent\n })\n }\n\n /* Add article */\n } else {\n documents.set(location, {\n location,\n title,\n text\n })\n }\n }\n return documents\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexConfig } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlight function\n *\n * @param value - Value\n *\n * @returns Highlighted value\n */\nexport type SearchHighlightFn = (value: string) => string\n\n/**\n * Search highlight factory function\n *\n * @param query - Query value\n *\n * @returns Search highlight function\n */\nexport type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search highlighter\n *\n * @param config - Search index configuration\n * @param escape - Whether to escape HTML\n *\n * @returns Search highlight factory function\n */\nexport function setupSearchHighlighter(\n config: SearchIndexConfig, escape: boolean\n): SearchHighlightFactoryFn {\n const separator = new RegExp(config.separator, \"img\")\n const highlight = (_: unknown, data: string, term: string) => {\n return `${data}${term}`\n }\n\n /* Return factory function */\n return (query: string) => {\n query = query\n .replace(/[\\s*+\\-:~^]+/g, \" \")\n .trim()\n\n /* Create search term match expression */\n const match = new RegExp(`(^|${config.separator})(${\n query\n .replace(/[|\\\\{}()[\\]^$+*?.-]/g, \"\\\\$&\")\n .replace(separator, \"|\")\n })`, \"img\")\n\n /* Highlight string value */\n return value => (\n escape\n ? escapeHTML(value)\n : value\n )\n .replace(match, highlight)\n .replace(/<\\/mark>(\\s+)]*>/img, \"$1\")\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search transformation function\n *\n * @param value - Query value\n *\n * @returns Transformed query value\n */\nexport type SearchTransformFn = (value: string) => string\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Default transformation function\n *\n * 1. Search for terms in quotation marks and prepend a `+` modifier to denote\n * that the resulting document must contain all terms, converting the query\n * to an `AND` query (as opposed to the default `OR` behavior). While users\n * may expect terms enclosed in quotation marks to map to span queries, i.e.\n * for which order is important, Lunr.js doesn't support them, so the best\n * we can do is to convert the terms to an `AND` query.\n *\n * 2. Replace control characters which are not located at the beginning of the\n * query or preceded by white space, or are not followed by a non-whitespace\n * character or are at the end of the query string. Furthermore, filter\n * unmatched quotation marks.\n *\n * 3. Trim excess whitespace from left and right.\n *\n * @param query - Query value\n *\n * @returns Transformed query value\n */\nexport function defaultTransform(query: string): string {\n return query\n .split(/\"([^\"]+)\"/g) /* => 1 */\n .map((terms, index) => index & 1\n ? terms.replace(/^\\b|^(?![^\\x00-\\x7F]|$)|\\s+/g, \" +\")\n : terms\n )\n .join(\"\")\n .replace(/\"|(?:^|\\s+)[*+\\-:^~]+(?=\\s+|$)/g, \"\") /* => 2 */\n .trim() /* => 3 */\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchResult } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search message type\n */\nexport const enum SearchMessageType {\n SETUP, /* Search index setup */\n READY, /* Search index ready */\n QUERY, /* Search query */\n RESULT /* Search results */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message containing the data necessary to setup the search index\n */\nexport interface SearchSetupMessage {\n type: SearchMessageType.SETUP /* Message type */\n data: SearchIndex /* Message data */\n}\n\n/**\n * Message indicating the search index is ready\n */\nexport interface SearchReadyMessage {\n type: SearchMessageType.READY /* Message type */\n}\n\n/**\n * Message containing a search query\n */\nexport interface SearchQueryMessage {\n type: SearchMessageType.QUERY /* Message type */\n data: string /* Message data */\n}\n\n/**\n * Message containing results for a search query\n */\nexport interface SearchResultMessage {\n type: SearchMessageType.RESULT /* Message type */\n data: SearchResult /* Message data */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message exchanged with the search worker\n */\nexport type SearchMessage =\n | SearchSetupMessage\n | SearchReadyMessage\n | SearchQueryMessage\n | SearchResultMessage\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Type guard for search setup messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchSetupMessage(\n message: SearchMessage\n): message is SearchSetupMessage {\n return message.type === SearchMessageType.SETUP\n}\n\n/**\n * Type guard for search ready messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchReadyMessage(\n message: SearchMessage\n): message is SearchReadyMessage {\n return message.type === SearchMessageType.READY\n}\n\n/**\n * Type guard for search query messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchQueryMessage(\n message: SearchMessage\n): message is SearchQueryMessage {\n return message.type === SearchMessageType.QUERY\n}\n\n/**\n * Type guard for search result messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchResultMessage(\n message: SearchMessage\n): message is SearchResultMessage {\n return message.type === SearchMessageType.RESULT\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ObservableInput, Subject, from } from \"rxjs\"\nimport { map, share } from \"rxjs/operators\"\n\nimport { configuration, feature, translation } from \"~/_\"\nimport { WorkerHandler, watchWorker } from \"~/browser\"\n\nimport { SearchIndex } from \"../../_\"\nimport {\n SearchOptions,\n SearchPipeline\n} from \"../../options\"\nimport {\n SearchMessage,\n SearchMessageType,\n SearchSetupMessage,\n isSearchResultMessage\n} from \"../message\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search worker\n */\nexport type SearchWorker = WorkerHandler\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search index\n *\n * @param data - Search index\n *\n * @returns Search index\n */\nfunction setupSearchIndex(\n { config, docs, index }: SearchIndex\n): SearchIndex {\n\n /* Override default language with value from translation */\n if (config.lang.length === 1 && config.lang[0] === \"en\")\n config.lang = [\n translation(\"search.config.lang\")\n ]\n\n /* Override default separator with value from translation */\n if (config.separator === \"[\\\\s\\\\-]+\")\n config.separator = translation(\"search.config.separator\")\n\n /* Set pipeline from translation */\n const pipeline = translation(\"search.config.pipeline\")\n .split(/\\s*,\\s*/)\n .filter(Boolean) as SearchPipeline\n\n /* Determine search options */\n const options: SearchOptions = {\n pipeline,\n suggestions: feature(\"search.suggest\")\n }\n\n /* Return search index after defaulting */\n return { config, docs, index, options }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search worker\n *\n * This function creates a web worker to set up and query the search index,\n * which is done using Lunr.js. The index must be passed as an observable to\n * enable hacks like _localsearch_ via search index embedding as JSON.\n *\n * @param url - Worker URL\n * @param index - Search index observable input\n *\n * @returns Search worker\n */\nexport function setupSearchWorker(\n url: string, index: ObservableInput\n): SearchWorker {\n const config = configuration()\n const worker = new Worker(url)\n\n /* Create communication channels and resolve relative links */\n const tx$ = new Subject()\n const rx$ = watchWorker(worker, { tx$ })\n .pipe(\n map(message => {\n if (isSearchResultMessage(message)) {\n for (const result of message.data.items)\n for (const document of result)\n document.location = `${new URL(document.location, config.base)}`\n }\n return message\n }),\n share()\n )\n\n /* Set up search index */\n from(index)\n .pipe(\n map(data => ({\n type: SearchMessageType.SETUP,\n data: setupSearchIndex(data)\n }))\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Return search worker */\n return { tx$, rx$ }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { configuration } from \"~/_\"\nimport { getElementOrThrow, requestJSON } from \"~/browser\"\nimport { Version, renderVersionSelector } from \"~/templates\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up version selector\n */\nexport function setupVersionSelector(): void {\n const config = configuration()\n requestJSON(new URL(\"../versions.json\", config.base))\n .subscribe(versions => {\n const topic = getElementOrThrow(\".md-header__topic\")\n topic.appendChild(renderVersionSelector(versions))\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n combineLatest,\n fromEvent,\n merge\n} from \"rxjs\"\nimport {\n delay,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n finalize,\n map,\n take,\n takeLast,\n takeUntil,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetSearchQueryPlaceholder,\n setSearchQueryPlaceholder\n} from \"~/actions\"\nimport {\n getLocation,\n setElementFocus,\n setToggle,\n watchElementFocus\n} from \"~/browser\"\nimport {\n SearchMessageType,\n SearchQueryMessage,\n SearchWorker,\n defaultTransform,\n isSearchReadyMessage\n} from \"~/integrations\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search query\n */\nexport interface SearchQuery {\n value: string /* Query value */\n focus: boolean /* Query focus */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch search query\n *\n * Note that the focus event which triggers re-reading the current query value\n * is delayed by `1ms` so the input's empty state is allowed to propagate.\n *\n * @param el - Search query element\n * @param worker - Search worker\n *\n * @returns Search query observable\n */\nexport function watchSearchQuery(\n el: HTMLInputElement, { rx$ }: SearchWorker\n): Observable {\n const fn = __search?.transform || defaultTransform\n\n /* Intercept focus and input events */\n const focus$ = watchElementFocus(el)\n const value$ = merge(\n fromEvent(el, \"keyup\"),\n fromEvent(el, \"focus\").pipe(delay(1))\n )\n .pipe(\n map(() => fn(el.value)),\n distinctUntilChanged()\n )\n\n /* Intercept deep links */\n const location = getLocation()\n if (location.searchParams.has(\"q\")) {\n setToggle(\"search\", true)\n rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n .subscribe(() => {\n el.value = location.searchParams.get(\"q\")!\n setElementFocus(el)\n })\n }\n\n /* Combine into single observable */\n return combineLatest([value$, focus$])\n .pipe(\n map(([value, focus]) => ({ value, focus }))\n )\n}\n\n/**\n * Mount search query\n *\n * @param el - Search query element\n * @param worker - Search worker\n *\n * @returns Search query component observable\n */\nexport function mountSearchQuery(\n el: HTMLInputElement, { tx$, rx$ }: SearchWorker\n): Observable> {\n const internal$ = new Subject()\n\n /* Handle value changes */\n internal$\n .pipe(\n distinctUntilKeyChanged(\"value\"),\n map(({ value }): SearchQueryMessage => ({\n type: SearchMessageType.QUERY,\n data: value\n }))\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Handle focus changes */\n internal$\n .pipe(\n distinctUntilKeyChanged(\"focus\")\n )\n .subscribe(({ focus }) => {\n if (focus) {\n setToggle(\"search\", focus)\n setSearchQueryPlaceholder(el, \"\")\n } else {\n resetSearchQueryPlaceholder(el)\n }\n })\n\n /* Handle reset */\n fromEvent(el.form!, \"reset\")\n .pipe(\n takeUntil(internal$.pipe(takeLast(1)))\n )\n .subscribe(() => setElementFocus(el))\n\n /* Create and return component */\n return watchSearchQuery(el, { tx$, rx$ })\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n merge,\n of\n} from \"rxjs\"\nimport {\n bufferCount,\n filter,\n finalize,\n map,\n observeOn,\n switchMap,\n take,\n tap,\n withLatestFrom,\n zipWith\n} from \"rxjs/operators\"\n\nimport {\n addToSearchResultList,\n resetSearchResultList,\n resetSearchResultMeta,\n setSearchResultMeta\n} from \"~/actions\"\nimport {\n getElementOrThrow,\n watchElementThreshold\n} from \"~/browser\"\nimport {\n SearchResult,\n SearchWorker,\n isSearchReadyMessage,\n isSearchResultMessage\n} from \"~/integrations\"\nimport { renderSearchResultItem } from \"~/templates\"\n\nimport { Component } from \"../../_\"\nimport { SearchQuery } from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search result list\n *\n * This function performs a lazy rendering of the search results, depending on\n * the vertical offset of the search result container.\n *\n * @param el - Search result list element\n * @param worker - Search worker\n * @param options - Options\n *\n * @returns Search result list component observable\n */\nexport function mountSearchResult(\n el: HTMLElement, { rx$ }: SearchWorker, { query$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n const boundary$ = watchElementThreshold(el.parentElement!)\n .pipe(\n filter(Boolean)\n )\n\n /* Retrieve nested components */\n const meta = getElementOrThrow(\":scope > :first-child\", el)\n const list = getElementOrThrow(\":scope > :last-child\", el)\n\n /* Update search result metadata when ready */\n rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n .subscribe(() => {\n resetSearchResultMeta(meta)\n })\n\n /* Update search result metadata */\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(query$)\n )\n .subscribe(([{ items }, { value }]) => {\n if (value)\n setSearchResultMeta(meta, items.length)\n else\n resetSearchResultMeta(meta)\n })\n\n /* Update search result list */\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n tap(() => resetSearchResultList(list)),\n switchMap(({ items }) => merge(\n of(...items.slice(0, 10)),\n of(...items.slice(10))\n .pipe(\n bufferCount(4),\n zipWith(boundary$),\n switchMap(([chunk]) => of(...chunk))\n )\n ))\n )\n .subscribe(result => {\n addToSearchResultList(list, renderSearchResultItem(result))\n })\n\n /* Filter search result message */\n const result$ = rx$\n .pipe(\n filter(isSearchResultMessage),\n map(({ data }) => data)\n )\n\n /* Create and return component */\n return result$\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n fromEvent\n} from \"rxjs\"\nimport {\n finalize,\n map,\n tap\n} from \"rxjs/operators\"\n\nimport { getLocation } from \"~/browser\"\n\nimport { Component } from \"../../_\"\nimport { SearchQuery } from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search sharing\n */\nexport interface SearchShare {\n url: URL /* Deep link for sharing */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n query$: Observable /* Search query observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search sharing\n *\n * @param _el - Search sharing element\n * @param options - Options\n *\n * @returns Search sharing observable\n */\nexport function watchSearchShare(\n _el: HTMLElement, { query$ }: WatchOptions\n): Observable {\n return query$\n .pipe(\n map(({ value }) => {\n const url = getLocation()\n url.hash = \"\"\n url.searchParams.delete(\"h\")\n url.searchParams.set(\"q\", value)\n return { url }\n })\n )\n}\n\n/**\n * Mount search sharing\n *\n * @param el - Search sharing element\n * @param options - Options\n *\n * @returns Search sharing component observable\n */\nexport function mountSearchShare(\n el: HTMLAnchorElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$.subscribe(({ url }) => {\n el.setAttribute(\"data-clipboard-text\", el.href)\n el.href = `${url}`\n })\n\n /* Prevent following of link */\n fromEvent(el, \"click\")\n .subscribe(ev => ev.preventDefault())\n\n /* Create and return component */\n return watchSearchShare(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n asyncScheduler,\n fromEvent\n} from \"rxjs\"\nimport {\n combineLatestWith,\n distinctUntilChanged,\n filter,\n finalize,\n map,\n observeOn,\n tap\n} from \"rxjs/operators\"\n\nimport { Keyboard } from \"~/browser\"\nimport {\n SearchResult,\n SearchWorker,\n isSearchResultMessage\n} from \"~/integrations\"\n\nimport { Component, getComponentElement } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search suggestions\n */\nexport interface SearchSuggest {}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n keyboard$: Observable /* Keyboard observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search suggestions\n *\n * This function will perform a lazy rendering of the search results, depending\n * on the vertical offset of the search result container.\n *\n * @param el - Search result list element\n * @param worker - Search worker\n * @param options - Options\n *\n * @returns Search result list component observable\n */\nexport function mountSearchSuggest(\n el: HTMLElement, { rx$ }: SearchWorker, { keyboard$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n\n /* Retrieve query component and track all changes */\n const query = getComponentElement(\"search-query\")\n const query$ = fromEvent(query, \"keydown\")\n .pipe(\n observeOn(asyncScheduler),\n map(() => query.value),\n distinctUntilChanged(),\n )\n\n /* Update search suggestions */\n internal$\n .pipe(\n combineLatestWith(query$),\n map(([{ suggestions }, value]) => {\n const words = value.split(/([\\s-]+)/)\n if (suggestions?.length && words[words.length - 1]) {\n const last = suggestions[suggestions.length - 1]\n if (last.startsWith(words[words.length - 1]))\n words[words.length - 1] = last\n } else {\n words.length = 0\n }\n return words\n })\n )\n .subscribe(words => el.innerHTML = words\n .join(\"\")\n .replace(/\\s/g, \" \")\n )\n\n /* Set up search keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"search\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Right arrow: accept current suggestion */\n case \"ArrowRight\":\n if (\n el.innerText.length &&\n query.selectionStart === query.value.length\n )\n query.value = el.innerText\n break\n }\n })\n\n /* Filter search result message */\n const result$ = rx$\n .pipe(\n filter(isSearchResultMessage),\n map(({ data }) => data)\n )\n\n /* Create and return component */\n return result$\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(() => ({ ref: el }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, ObservableInput, merge } from \"rxjs\"\nimport { filter, mergeWith, sample, take } from \"rxjs/operators\"\n\nimport { configuration } from \"~/_\"\nimport {\n Keyboard,\n getActiveElement,\n getElements,\n setElementFocus,\n setElementSelection,\n setToggle\n} from \"~/browser\"\nimport {\n SearchIndex,\n SearchResult,\n isSearchQueryMessage,\n isSearchReadyMessage,\n setupSearchWorker\n} from \"~/integrations\"\n\nimport {\n Component,\n getComponentElement,\n getComponentElements\n} from \"../../_\"\nimport { SearchQuery, mountSearchQuery } from \"../query\"\nimport { mountSearchResult } from \"../result\"\nimport { SearchShare, mountSearchShare } from \"../share\"\nimport { SearchSuggest, mountSearchSuggest } from \"../suggest\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search\n */\nexport type Search =\n | SearchQuery\n | SearchResult\n | SearchShare\n | SearchSuggest\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n index$: ObservableInput /* Search index observable */\n keyboard$: Observable /* Keyboard observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search\n *\n * This function sets up the search functionality, including the underlying\n * web worker and all keyboard bindings.\n *\n * @param el - Search element\n * @param options - Options\n *\n * @returns Search component observable\n */\nexport function mountSearch(\n el: HTMLElement, { index$, keyboard$ }: MountOptions\n): Observable> {\n const config = configuration()\n try {\n const url = __search?.worker || config.search\n const worker = setupSearchWorker(url, index$)\n\n /* Retrieve query and result components */\n const query = getComponentElement(\"search-query\", el)\n const result = getComponentElement(\"search-result\", el)\n\n /* Re-emit query when search is ready */\n const { tx$, rx$ } = worker\n tx$\n .pipe(\n filter(isSearchQueryMessage),\n sample(rx$\n .pipe(\n filter(isSearchReadyMessage),\n take(1)\n )\n )\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Set up search keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"search\")\n )\n .subscribe(key => {\n const active = getActiveElement()\n switch (key.type) {\n\n /* Enter: go to first (best) result */\n case \"Enter\":\n if (active === query) {\n const anchors = new Map()\n for (const anchor of getElements(\n \":first-child [href]\", result\n )) {\n const article = anchor.firstElementChild!\n anchors.set(anchor, parseFloat(\n article.getAttribute(\"data-md-score\")!\n ))\n }\n\n /* Go to result with highest score, if any */\n if (anchors.size) {\n const [[best]] = [...anchors].sort(([, a], [, b]) => b - a)\n best.click()\n }\n\n /* Otherwise omit form submission */\n key.claim()\n }\n break\n\n /* Escape or Tab: close search */\n case \"Escape\":\n case \"Tab\":\n setToggle(\"search\", false)\n setElementFocus(query, false)\n break\n\n /* Vertical arrows: select previous or next search result */\n case \"ArrowUp\":\n case \"ArrowDown\":\n if (typeof active === \"undefined\") {\n setElementFocus(query)\n } else {\n const els = [query, ...getElements(\n \":not(details) > [href], summary, details[open] [href]\",\n result\n )]\n const i = Math.max(0, (\n Math.max(0, els.indexOf(active)) + els.length + (\n key.type === \"ArrowUp\" ? -1 : +1\n )\n ) % els.length)\n setElementFocus(els[i])\n }\n\n /* Prevent scrolling of page */\n key.claim()\n break\n\n /* All other keys: hand to search query */\n default:\n if (query !== getActiveElement())\n setElementFocus(query)\n }\n })\n\n /* Set up global keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\"),\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Open search and select query */\n case \"f\":\n case \"s\":\n case \"/\":\n setElementFocus(query)\n setElementSelection(query)\n key.claim()\n break\n }\n })\n\n /* Create and return component */\n const query$ = mountSearchQuery(query, worker)\n const result$ = mountSearchResult(result, worker, { query$ })\n return merge(query$, result$)\n .pipe(\n mergeWith(\n\n /* Search sharing */\n ...getComponentElements(\"search-share\", el)\n .map(child => mountSearchShare(child, { query$ })),\n\n /* Search suggestions */\n ...getComponentElements(\"search-suggest\", el)\n .map(child => mountSearchSuggest(child, worker, { keyboard$ }))\n )\n )\n\n /* Gracefully handle broken search */\n } catch (err) {\n el.hidden = true\n return NEVER\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n ObservableInput,\n combineLatest\n} from \"rxjs\"\nimport { filter, map, startWith } from \"rxjs/operators\"\n\nimport { getLocation } from \"~/browser\"\nimport {\n SearchIndex,\n setupSearchHighlighter\n} from \"~/integrations\"\nimport { h } from \"~/utilities\"\n\nimport { Component } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlighting\n */\nexport interface SearchHighlight {\n nodes: Map /* Map of replacements */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n index$: ObservableInput /* Search index observable */\n location$: Observable /* Location observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search highlighting\n *\n * @param el - Content element\n * @param options - Options\n *\n * @returns Search highlighting component observable\n */\nexport function mountSearchHiglight(\n el: HTMLElement, { index$, location$ }: MountOptions\n): Observable> {\n return combineLatest([\n index$,\n location$\n .pipe(\n startWith(getLocation()),\n filter(url => url.searchParams.has(\"h\"))\n )\n ])\n .pipe(\n map(([index, url]) => setupSearchHighlighter(index.config, true)(\n url.searchParams.get(\"h\")!\n )),\n map(fn => {\n const nodes = new Map()\n\n /* Traverse text nodes and collect matches */\n const it = document.createNodeIterator(el, NodeFilter.SHOW_TEXT)\n for (let node = it.nextNode(); node; node = it.nextNode()) {\n if (node.parentElement?.offsetHeight) {\n const original = node.textContent!\n const replaced = fn(original)\n if (replaced.length > original.length)\n nodes.set(node as ChildNode, replaced)\n }\n }\n\n /* Replace original nodes with matches */\n for (const [node, text] of nodes) {\n const { childNodes } = h(\"span\", null, text)\n node.replaceWith(...Array.from(childNodes))\n }\n\n /* Return component */\n return { ref: el, nodes }\n })\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n finalize,\n map,\n observeOn,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n resetSidebarHeight,\n resetSidebarOffset,\n setSidebarHeight,\n setSidebarOffset\n} from \"~/actions\"\nimport { Viewport } from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\nimport { Main } from \"../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Sidebar\n */\nexport interface Sidebar {\n height: number /* Sidebar height */\n locked: boolean /* User scrolled past header */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n main$: Observable
    /* Main area observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch sidebar\n *\n * This function returns an observable that computes the visual parameters of\n * the sidebar which depends on the vertical viewport offset, as well as the\n * height of the main area. When the page is scrolled beyond the header, the\n * sidebar is locked and fills the remaining space.\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @returns Sidebar observable\n */\nexport function watchSidebar(\n el: HTMLElement, { viewport$, main$ }: WatchOptions\n): Observable {\n const adjust =\n el.parentElement!.offsetTop -\n el.parentElement!.parentElement!.offsetTop\n\n /* Compute the sidebar's available height and if it should be locked */\n return combineLatest([main$, viewport$])\n .pipe(\n map(([{ offset, height }, { offset: { y } }]) => {\n height = height\n + Math.min(adjust, Math.max(0, y - offset))\n - adjust\n return {\n height,\n locked: y >= offset + adjust\n }\n }),\n distinctUntilChanged((a, b) => (\n a.height === b.height &&\n a.locked === b.locked\n ))\n )\n}\n\n/**\n * Mount sidebar\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @returns Sidebar component observable\n */\nexport function mountSidebar(\n el: HTMLElement, { header$, ...options }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(header$)\n )\n .subscribe({\n\n /* Update height and offset */\n next([{ height }, { height: offset }]) {\n setSidebarHeight(el, height)\n setSidebarOffset(el, offset)\n },\n\n /* Reset on complete */\n complete() {\n resetSidebarOffset(el)\n resetSidebarHeight(el)\n }\n })\n\n /* Create and return component */\n return watchSidebar(el, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Repo, User } from \"github-types\"\nimport { Observable, zip } from \"rxjs\"\nimport { defaultIfEmpty, map } from \"rxjs/operators\"\n\nimport { requestJSON } from \"~/browser\"\n\nimport { SourceFacts } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * GitHub release (partial)\n */\ninterface Release {\n tag_name: string /* Tag name */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitHub repository facts\n *\n * @param user - GitHub user\n * @param repo - GitHub repository\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFactsFromGitHub(\n user: string, repo?: string\n): Observable {\n if (typeof repo !== \"undefined\") {\n const url = `https://api.github.com/repos/${user}/${repo}`\n return zip(\n\n /* Fetch version */\n requestJSON(`${url}/releases/latest`)\n .pipe(\n map(release => ({\n version: release.tag_name\n })),\n defaultIfEmpty({})\n ),\n\n /* Fetch stars and forks */\n requestJSON(url)\n .pipe(\n map(info => ({\n stars: info.stargazers_count,\n forks: info.forks_count\n })),\n defaultIfEmpty({})\n )\n )\n .pipe(\n map(([release, info]) => ({ ...release, ...info }))\n )\n\n /* User or organization */\n } else {\n const url = `https://api.github.com/users/${user}`\n return requestJSON(url)\n .pipe(\n map(info => ({\n repositories: info.public_repos\n })),\n defaultIfEmpty({})\n )\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ProjectSchema } from \"gitlab\"\nimport { Observable } from \"rxjs\"\nimport { defaultIfEmpty, map } from \"rxjs/operators\"\n\nimport { requestJSON } from \"~/browser\"\n\nimport { SourceFacts } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitLab repository facts\n *\n * @param base - GitLab base\n * @param project - GitLab project\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFactsFromGitLab(\n base: string, project: string\n): Observable {\n const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}`\n return requestJSON(url)\n .pipe(\n map(({ star_count, forks_count }) => ({\n stars: star_count,\n forks: forks_count\n })),\n defaultIfEmpty({})\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable } from \"rxjs\"\n\nimport { fetchSourceFactsFromGitHub } from \"../github\"\nimport { fetchSourceFactsFromGitLab } from \"../gitlab\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository facts for repositories\n */\nexport interface RepositoryFacts {\n stars?: number /* Number of stars */\n forks?: number /* Number of forks */\n version?: string /* Latest version */\n}\n\n/**\n * Repository facts for organizations\n */\nexport interface OrganizationFacts {\n repositories?: number /* Number of repositories */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Repository facts\n */\nexport type SourceFacts =\n | RepositoryFacts\n | OrganizationFacts\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch repository facts\n *\n * @param url - Repository URL\n *\n * @returns Repository facts observable\n */\nexport function fetchSourceFacts(\n url: string\n): Observable {\n const [type] = url.match(/(git(?:hub|lab))/i) || []\n switch (type.toLowerCase()) {\n\n /* GitHub repository */\n case \"github\":\n const [, user, repo] = url.match(/^.+github\\.com\\/([^/]+)\\/?([^/]+)?/i)!\n return fetchSourceFactsFromGitHub(user, repo)\n\n /* GitLab repository */\n case \"gitlab\":\n const [, base, slug] = url.match(/^.+?([^/]*gitlab[^/]+)\\/(.+?)\\/?$/i)!\n return fetchSourceFactsFromGitLab(base, slug)\n\n /* Everything else */\n default:\n return NEVER\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, Subject, defer, of } from \"rxjs\"\nimport {\n catchError,\n filter,\n finalize,\n map,\n shareReplay,\n tap\n} from \"rxjs/operators\"\n\nimport { setSourceFacts, setSourceState } from \"~/actions\"\nimport { renderSourceFacts } from \"~/templates\"\n\nimport { Component } from \"../../_\"\nimport { SourceFacts, fetchSourceFacts } from \"../facts\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository information\n */\nexport interface Source {\n facts: SourceFacts /* Repository facts */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Repository information observable\n */\nlet fetch$: Observable\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch repository information\n *\n * This function tries to read the repository facts from session storage, and\n * if unsuccessful, fetches them from the underlying provider.\n *\n * @param el - Repository information element\n *\n * @returns Repository information observable\n */\nexport function watchSource(\n el: HTMLAnchorElement\n): Observable {\n return fetch$ ||= defer(() => {\n const data = sessionStorage.getItem(__prefix(\"__source\"))\n if (data) {\n return of(JSON.parse(data))\n } else {\n const value$ = fetchSourceFacts(el.href)\n value$.subscribe(value => {\n try {\n sessionStorage.setItem(__prefix(\"__source\"), JSON.stringify(value))\n } catch (err) {\n /* Uncritical, just swallow */\n }\n })\n\n /* Return value */\n return value$\n }\n })\n .pipe(\n catchError(() => NEVER),\n filter(facts => Object.keys(facts).length > 0),\n map(facts => ({ facts })),\n shareReplay(1)\n )\n}\n\n/**\n * Mount repository information\n *\n * @param el - Repository information element\n *\n * @returns Repository information component observable\n */\nexport function mountSource(\n el: HTMLAnchorElement\n): Observable> {\n const internal$ = new Subject()\n internal$.subscribe(({ facts }) => {\n setSourceFacts(el, renderSourceFacts(facts))\n setSourceState(el, \"done\")\n })\n\n /* Create and return component */\n return watchSource(el)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n of\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { feature } from \"~/_\"\nimport { resetTabsState, setTabsState } from \"~/actions\"\nimport {\n Viewport,\n watchElementSize,\n watchViewportAt\n} from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Navigation tabs\n */\nexport interface Tabs {\n hidden: boolean /* User scrolled past tabs */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch navigation tabs\n *\n * @param el - Navigation tabs element\n * @param options - Options\n *\n * @returns Navigation tabs observable\n */\nexport function watchTabs(\n el: HTMLElement, { viewport$, header$ }: WatchOptions\n): Observable {\n return watchElementSize(document.body)\n .pipe(\n switchMap(() => watchViewportAt(el, { header$, viewport$ })),\n map(({ offset: { y } }) => {\n return {\n hidden: y >= 10\n }\n }),\n distinctUntilKeyChanged(\"hidden\")\n )\n}\n\n/**\n * Mount navigation tabs\n *\n * This function hides the navigation tabs when scrolling past the threshold\n * and makes them reappear in a nice CSS animation when scrolling back up.\n *\n * @param el - Navigation tabs element\n * @param options - Options\n *\n * @returns Navigation tabs component observable\n */\nexport function mountTabs(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler)\n )\n .subscribe({\n\n /* Update state */\n next({ hidden }) {\n if (hidden)\n setTabsState(el, \"hidden\")\n else\n resetTabsState(el)\n },\n\n /* Reset on complete */\n complete() {\n resetTabsState(el)\n }\n })\n\n /* Create and return component */\n return (\n feature(\"navigation.tabs.sticky\")\n ? of({ hidden: false })\n : watchTabs(el, options)\n )\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n bufferCount,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n scan,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport {\n resetAnchorActive,\n resetAnchorState,\n setAnchorActive,\n setAnchorState\n} from \"~/actions\"\nimport {\n Viewport,\n getElement,\n getElements,\n watchElementSize\n} from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Table of contents\n */\nexport interface TableOfContents {\n prev: HTMLAnchorElement[][] /* Anchors (previous) */\n next: HTMLAnchorElement[][] /* Anchors (next) */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch table of contents\n *\n * This is effectively a scroll spy implementation which will account for the\n * fixed header and automatically re-calculate anchor offsets when the viewport\n * is resized. The returned observable will only emit if the table of contents\n * needs to be repainted.\n *\n * This implementation tracks an anchor element's entire path starting from its\n * level up to the top-most anchor element, e.g. `[h3, h2, h1]`. Although the\n * Material theme currently doesn't make use of this information, it enables\n * the styling of the entire hierarchy through customization.\n *\n * Note that the current anchor is the last item of the `prev` anchor list.\n *\n * @param anchors - Anchor elements\n * @param options - Options\n *\n * @returns Table of contents observable\n */\nexport function watchTableOfContents(\n anchors: HTMLAnchorElement[], { viewport$, header$ }: WatchOptions\n): Observable {\n const table = new Map()\n for (const anchor of anchors) {\n const id = decodeURIComponent(anchor.hash.substring(1))\n const target = getElement(`[id=\"${id}\"]`)\n if (typeof target !== \"undefined\")\n table.set(anchor, target)\n }\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(header => 24 + header.height)\n )\n\n /* Compute partition of previous and next anchors */\n const partition$ = watchElementSize(document.body)\n .pipe(\n distinctUntilKeyChanged(\"height\"),\n\n /* Build index to map anchor paths to vertical offsets */\n map(() => {\n let path: HTMLAnchorElement[] = []\n return [...table].reduce((index, [anchor, target]) => {\n while (path.length) {\n const last = table.get(path[path.length - 1])!\n if (last.tagName >= target.tagName) {\n path.pop()\n } else {\n break\n }\n }\n\n /* If the current anchor is hidden, continue with its parent */\n let offset = target.offsetTop\n while (!offset && target.parentElement) {\n target = target.parentElement\n offset = target.offsetTop\n }\n\n /* Map reversed anchor path to vertical offset */\n return index.set(\n [...path = [...path, anchor]].reverse(),\n offset\n )\n }, new Map())\n }),\n\n /* Sort index by vertical offset (see https://bit.ly/30z6QSO) */\n map(index => new Map([...index].sort(([, a], [, b]) => a - b))),\n\n /* Re-compute partition when viewport offset changes */\n switchMap(index => combineLatest([adjust$, viewport$])\n .pipe(\n scan(([prev, next], [adjust, { offset: { y } }]) => {\n\n /* Look forward */\n while (next.length) {\n const [, offset] = next[0]\n if (offset - adjust < y) {\n prev = [...prev, next.shift()!]\n } else {\n break\n }\n }\n\n /* Look backward */\n while (prev.length) {\n const [, offset] = prev[prev.length - 1]\n if (offset - adjust >= y) {\n next = [prev.pop()!, ...next]\n } else {\n break\n }\n }\n\n /* Return partition */\n return [prev, next]\n }, [[], [...index]]),\n distinctUntilChanged((a, b) => (\n a[0] === b[0] &&\n a[1] === b[1]\n ))\n )\n )\n )\n\n /* Compute and return anchor list migrations */\n return partition$\n .pipe(\n map(([prev, next]) => ({\n prev: prev.map(([path]) => path),\n next: next.map(([path]) => path)\n })),\n\n /* Extract anchor list migrations */\n startWith({ prev: [], next: [] }),\n bufferCount(2, 1),\n map(([a, b]) => {\n\n /* Moving down */\n if (a.prev.length < b.prev.length) {\n return {\n prev: b.prev.slice(Math.max(0, a.prev.length - 1), b.prev.length),\n next: []\n }\n\n /* Moving up */\n } else {\n return {\n prev: b.prev.slice(-1),\n next: b.next.slice(0, b.next.length - a.next.length)\n }\n }\n })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Mount table of contents\n *\n * @param el - Anchor list element\n * @param options - Options\n *\n * @returns Table of contents component observable\n */\nexport function mountTableOfContents(\n el: HTMLElement, options: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n )\n .subscribe(({ prev, next }) => {\n\n /* Look forward */\n for (const [anchor] of next) {\n resetAnchorActive(anchor)\n resetAnchorState(anchor)\n }\n\n /* Look backward */\n for (const [index, [anchor]] of prev.entries()) {\n setAnchorActive(anchor, index === prev.length - 1)\n setAnchorState(anchor, \"blur\")\n }\n })\n\n /* Create and return component */\n const anchors = getElements(\"[href^=\\\\#]\", el)\n return watchTableOfContents(anchors, options)\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n Subject,\n animationFrameScheduler,\n combineLatest\n} from \"rxjs\"\nimport {\n bufferCount,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n resetBackToTopOffset,\n resetBackToTopState,\n resetFocusable,\n setBackToTopOffset,\n setBackToTopState,\n setFocusable\n} from \"~/actions\"\nimport { Viewport, setElementFocus } from \"~/browser\"\n\nimport { Component } from \"../_\"\nimport { Header } from \"../header\"\nimport { Main } from \"../main\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Back-to-top button\n */\nexport interface BackToTop {\n hidden: boolean /* User scrolled up */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/**\n * Mount options\n */\ninterface MountOptions {\n viewport$: Observable /* Viewport observable */\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch back-to-top\n *\n * @param _el - Back-to-top element\n * @param options - Options\n *\n * @returns Back-to-top observable\n */\nexport function watchBackToTop(\n _el: HTMLElement, { viewport$, main$ }: WatchOptions\n): Observable {\n\n /* Compute direction */\n const direction$ = viewport$\n .pipe(\n map(({ offset: { y } }) => y),\n bufferCount(2, 1),\n map(([a, b]) => a > b && b),\n distinctUntilChanged()\n )\n\n /* Compute whether button should be hidden */\n const hidden$ = main$\n .pipe(\n distinctUntilKeyChanged(\"active\")\n )\n\n /* Compute threshold for hiding */\n return combineLatest([hidden$, direction$])\n .pipe(\n map(([{ active }, direction]) => ({\n hidden: !(active && direction)\n })),\n distinctUntilChanged((a, b) => (\n a.hidden === b.hidden\n ))\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Mount back-to-top\n *\n * @param el - Back-to-top element\n * @param options - Options\n *\n * @returns Back-to-top component observable\n */\nexport function mountBackToTop(\n el: HTMLElement, { viewport$, header$, main$ }: MountOptions\n): Observable> {\n const internal$ = new Subject()\n internal$\n .pipe(\n observeOn(animationFrameScheduler),\n withLatestFrom(header$\n .pipe(\n distinctUntilKeyChanged(\"height\")\n )\n )\n )\n .subscribe({\n\n /* Update state */\n next([{ hidden }, { height }]) {\n setBackToTopOffset(el, height + 16)\n if (hidden) {\n setBackToTopState(el, \"hidden\")\n setElementFocus(el, false)\n setFocusable(el, -1)\n } else {\n resetBackToTopState(el)\n resetFocusable(el)\n }\n },\n\n /* Reset on complete */\n complete() {\n resetBackToTopOffset(el)\n resetBackToTopState(el)\n resetFocusable(el)\n }\n })\n\n /* Create and return component */\n return watchBackToTop(el, { viewport$, header$, main$ })\n .pipe(\n tap(state => internal$.next(state)),\n finalize(() => internal$.complete()),\n map(state => ({ ref: el, ...state }))\n )\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, of } from \"rxjs\"\nimport {\n mapTo,\n mergeMap,\n switchMap,\n takeWhile,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n tablet$: Observable /* Tablet breakpoint observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch indeterminate checkboxes\n *\n * This function replaces the indeterminate \"pseudo state\" with the actual\n * indeterminate state, which is used to keep navigation always expanded.\n *\n * @param options - Options\n */\nexport function patchIndeterminate(\n { document$, tablet$ }: PatchOptions\n): void {\n document$\n .pipe(\n switchMap(() => of(...getElements(\n \"[data-md-state=indeterminate]\"\n ))),\n tap(el => {\n el.indeterminate = true\n el.checked = false\n }),\n mergeMap(el => fromEvent(el, \"change\")\n .pipe(\n takeWhile(() => el.hasAttribute(\"data-md-state\")),\n mapTo(el)\n )\n ),\n withLatestFrom(tablet$)\n )\n .subscribe(([el, tablet]) => {\n el.removeAttribute(\"data-md-state\")\n if (tablet)\n el.checked = false\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, of } from \"rxjs\"\nimport {\n filter,\n mapTo,\n mergeMap,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { getElements } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether the given device is an Apple device\n *\n * @returns Test result\n */\nfunction isAppleDevice(): boolean {\n return /(iPad|iPhone|iPod)/.test(navigator.userAgent)\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all elements with `data-md-scrollfix` attributes\n *\n * This is a year-old patch which ensures that overflow scrolling works at the\n * top and bottom of containers on iOS by ensuring a `1px` scroll offset upon\n * the start of a touch event.\n *\n * @see https://bit.ly/2SCtAOO - Original source\n *\n * @param options - Options\n */\nexport function patchScrollfix(\n { document$ }: PatchOptions\n): void {\n document$\n .pipe(\n switchMap(() => of(...getElements(\"[data-md-scrollfix]\"))),\n tap(el => el.removeAttribute(\"data-md-scrollfix\")),\n filter(isAppleDevice),\n mergeMap(el => fromEvent(el, \"touchstart\")\n .pipe(\n mapTo(el)\n )\n )\n )\n .subscribe(el => {\n const top = el.scrollTop\n\n /* We're at the top of the container */\n if (top === 0) {\n el.scrollTop = 1\n\n /* We're at the bottom of the container */\n } else if (top + el.offsetHeight === el.scrollHeight) {\n el.scrollTop = top - 1\n }\n })\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n animationFrameScheduler,\n combineLatest,\n of\n} from \"rxjs\"\nimport {\n delay,\n map,\n observeOn,\n switchMap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { resetScrollLock, setScrollLock } from \"~/actions\"\nimport { Viewport, watchToggle } from \"~/browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n viewport$: Observable /* Viewport observable */\n tablet$: Observable /* Tablet breakpoint observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch the document body to lock when search is open\n *\n * For mobile and tablet viewports, the search is rendered full screen, which\n * leads to scroll leaking when at the top or bottom of the search result. This\n * function locks the body when the search is in full screen mode, and restores\n * the scroll position when leaving.\n *\n * @param options - Options\n */\nexport function patchScrolllock(\n { viewport$, tablet$ }: PatchOptions\n): void {\n combineLatest([watchToggle(\"search\"), tablet$])\n .pipe(\n map(([active, tablet]) => active && !tablet),\n switchMap(active => of(active)\n .pipe(\n delay(active ? 400 : 100),\n observeOn(animationFrameScheduler)\n )\n ),\n withLatestFrom(viewport$)\n )\n .subscribe(([active, { offset: { y }}]) => {\n if (active)\n setScrollLock(document.body, y)\n else\n resetScrollLock(document.body)\n })\n}\n"], + "mappings": "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", "names": [] } diff --git a/assets/javascripts/workers/search.f8263e09.min.js b/assets/javascripts/workers/search.e99e714c.min.js similarity index 97% rename from assets/javascripts/workers/search.f8263e09.min.js rename to assets/javascripts/workers/search.e99e714c.min.js index 647eb287f..9f562b4ea 100644 --- a/assets/javascripts/workers/search.f8263e09.min.js +++ b/assets/javascripts/workers/search.e99e714c.min.js @@ -1,4 +1,4 @@ -(()=>{var ge=Object.create;var z=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames,G=Object.getOwnPropertySymbols,ve=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty,xe=Object.prototype.propertyIsEnumerable;var X=(t,e,r)=>e in t?z(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,Z=(t,e)=>{for(var r in e||(e={}))J.call(e,r)&&X(t,r,e[r]);if(G)for(var r of G(e))xe.call(e,r)&&X(t,r,e[r]);return t};var Se=t=>z(t,"__esModule",{value:!0});var Pe=typeof require!="undefined"?require:t=>{throw new Error('Dynamic require of "'+t+'" is not supported')};var K=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Qe=(t,e,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of me(e))!J.call(t,n)&&n!=="default"&&z(t,n,{get:()=>e[n],enumerable:!(r=ye(e,n))||r.enumerable});return t},W=t=>Qe(Se(z(t!=null?ge(ve(t)):{},"default",t&&t.__esModule&&"default"in t?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t);var U=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var re=K((ee,te)=>{/** +(()=>{var ge=Object.create;var z=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames,G=Object.getOwnPropertySymbols,ve=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty,xe=Object.prototype.propertyIsEnumerable;var X=(t,e,r)=>e in t?z(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,Z=(t,e)=>{for(var r in e||(e={}))J.call(e,r)&&X(t,r,e[r]);if(G)for(var r of G(e))xe.call(e,r)&&X(t,r,e[r]);return t};var Se=t=>z(t,"__esModule",{value:!0});var K=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Qe=(t,e,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of me(e))!J.call(t,n)&&n!=="default"&&z(t,n,{get:()=>e[n],enumerable:!(r=ye(e,n))||r.enumerable});return t},W=t=>Qe(Se(z(t!=null?ge(ve(t)):{},"default",t&&t.__esModule&&"default"in t?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t);var U=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var re=K((ee,te)=>{/** * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 * Copyright (C) 2020 Oliver Nightingale * @license MIT @@ -37,12 +37,12 @@ */t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(r){var n=new t.QueryParser(e,r);n.parse()})},t.Index.prototype.query=function(e){for(var r=new t.Query(this.fields),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,r){typeof define=="function"&&define.amd?define(r):typeof ee=="object"?te.exports=r():e.lunr=r()}(this,function(){return t})})()});var H=K((Re,ne)=>{/*! + */t.Builder=function(){this._ref="id",this._fields=Object.create(null),this._documents=Object.create(null),this.invertedIndex=Object.create(null),this.fieldTermFrequencies={},this.fieldLengths={},this.tokenizer=t.tokenizer,this.pipeline=new t.Pipeline,this.searchPipeline=new t.Pipeline,this.documentCount=0,this._b=.75,this._k1=1.2,this.termIndex=0,this.metadataWhitelist=[]},t.Builder.prototype.ref=function(e){this._ref=e},t.Builder.prototype.field=function(e,r){if(/\//.test(e))throw new RangeError("Field '"+e+"' contains illegal character '/'");this._fields[e]=r||{}},t.Builder.prototype.b=function(e){e<0?this._b=0:e>1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,r){typeof define=="function"&&define.amd?define(r):typeof ee=="object"?te.exports=r():e.lunr=r()}(this,function(){return t})})()});var H=K((Ie,ne)=>{/*! * escape-html * Copyright(c) 2012-2013 TJ Holowaychuk * Copyright(c) 2015 Andreas Lubbe * Copyright(c) 2015 Tiancheng "Timothy" Gu * MIT Licensed */"use strict";var be=/["'&<>]/;ne.exports=we;function we(t){var e=""+t,r=be.exec(e);if(!r)return e;var n,i="",s=0,o=0;for(s=r.index;s`${s}${o}`;return i=>{i=i.replace(/[\s*+\-:~^]+/g," ").trim();let s=new RegExp(`(^|${t.separator})(${i.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return o=>(e?(0,oe.default)(o):o).replace(s,n).replace(/<\/mark>(\s+)]*>/img,"$1")}}function ue(t){let e=new lunr.Query(["title","text"]);return new lunr.QueryParser(t,e).parse(),e.clauses}function ce(t,e){let r=new Set(t),n={};for(let i=0;i!n.has(i)))]}var q=class{constructor({config:e,docs:r,index:n,options:i}){this.options=i,this.documents=se(r),this.highlight=ae(e,!1),lunr.tokenizer.separator=new RegExp(e.separator),typeof n=="undefined"?this.index=lunr(function(){e.lang.length===1&&e.lang[0]!=="en"?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang));let s=Le(["trimmer","stopWordFilter","stemmer"],i.pipeline);for(let o of e.lang.map(a=>a==="en"?lunr:lunr[a]))for(let a of s)this.pipeline.remove(o[a]),this.searchPipeline.remove(o[a]);this.ref("location"),this.field("title",{boost:1e3}),this.field("text");for(let o of r)this.add(o)}):this.index=lunr.Index.load(n)}search(e){if(e)try{let r=this.highlight(e),n=ue(e).filter(o=>o.presence!==lunr.Query.presence.PROHIBITED),i=this.index.search(`${e}*`).reduce((o,{ref:a,score:u,matchData:c})=>{let h=this.documents.get(a);if(typeof h!="undefined"){let{location:y,title:g,text:b,parent:v}=h,Q=ce(n,Object.keys(c.metadata)),f=+!v+ +Object.values(Q).every(p=>p);o.push({location:y,title:r(g),text:r(b),score:u*(1+f),terms:Q})}return o},[]).sort((o,a)=>a.score-o.score).reduce((o,a)=>{let u=this.documents.get(a.location);if(typeof u!="undefined"){let c="parent"in u?u.parent.location:u.location;o.set(c,[...o.get(c)||[],a])}return o},new Map),s;if(this.options.suggestions){let o=this.index.query(a=>{for(let u of n)a.term(u.term,{fields:["title"],presence:lunr.Query.presence.REQUIRED,wildcard:lunr.Query.wildcard.TRAILING})});s=o.length?Object.keys(o[0].matchData.metadata):[]}return Z({items:[...i.values()]},typeof s!="undefined"&&{suggestions:s})}catch(r){console.warn(`Invalid query: ${e} \u2013 see https://bit.ly/2s3ChXG`)}return{items:[]}}};var T;(function(i){i[i.SETUP=0]="SETUP",i[i.READY=1]="READY",i[i.QUERY=2]="QUERY",i[i.RESULT=3]="RESULT"})(T||(T={}));var Y;function Ee(t){return U(this,null,function*(){let e="../lunr";if(typeof parent!="undefined"&&"IFrameWorker"in parent){let n=document.querySelector("script[src]"),[i]=n.src.split("/worker");e=e.replace("..",i)}let r=[];for(let n of t.lang){switch(n){case"ja":r.push(`${e}/tinyseg.js`);break;case"hi":case"th":r.push(`${e}/wordcut.js`);break}n!=="en"&&r.push(`${e}/min/lunr.${n}.min.js`)}t.lang.length>1&&r.push(`${e}/min/lunr.multi.min.js`),r.length&&(yield importScripts(`${e}/min/lunr.stemmer.support.min.js`,...r))})}function ke(t){return U(this,null,function*(){switch(t.type){case T.SETUP:return yield Ee(t.data.config),Y=new q(t.data),{type:T.READY};case T.QUERY:return{type:T.RESULT,data:Y?Y.search(t.data):{items:[]}};default:throw new TypeError("Invalid message type")}})}self.lunr=le.default;addEventListener("message",t=>U(void 0,null,function*(){postMessage(yield ke(t.data))}));})(); -//# sourceMappingURL=search.f8263e09.min.js.map +//# sourceMappingURL=search.e99e714c.min.js.map diff --git a/assets/javascripts/workers/search.f8263e09.min.js.map b/assets/javascripts/workers/search.e99e714c.min.js.map similarity index 99% rename from assets/javascripts/workers/search.f8263e09.min.js.map rename to assets/javascripts/workers/search.e99e714c.min.js.map index 4066e4479..41606b17c 100644 --- a/assets/javascripts/workers/search.f8263e09.min.js.map +++ b/assets/javascripts/workers/search.e99e714c.min.js.map @@ -2,6 +2,6 @@ "version": 3, "sources": ["node_modules/lunr/lunr.js", "node_modules/escape-html/index.js", "src/assets/javascripts/integrations/search/worker/main/index.ts", "src/assets/javascripts/integrations/search/document/index.ts", "src/assets/javascripts/integrations/search/highlighter/index.ts", "src/assets/javascripts/integrations/search/query/_/index.ts", "src/assets/javascripts/integrations/search/_/index.ts", "src/assets/javascripts/integrations/search/worker/message/index.ts"], "sourcesContent": ["/**\n * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9\n * Copyright (C) 2020 Oliver Nightingale\n * @license MIT\n */\n\n;(function(){\n\n/**\n * A convenience function for configuring and constructing\n * a new lunr Index.\n *\n * A lunr.Builder instance is created and the pipeline setup\n * with a trimmer, stop word filter and stemmer.\n *\n * This builder object is yielded to the configuration function\n * that is passed as a parameter, allowing the list of fields\n * and other builder parameters to be customised.\n *\n * All documents _must_ be added within the passed config function.\n *\n * @example\n * var idx = lunr(function () {\n * this.field('title')\n * this.field('body')\n * this.ref('id')\n *\n * documents.forEach(function (doc) {\n * this.add(doc)\n * }, this)\n * })\n *\n * @see {@link lunr.Builder}\n * @see {@link lunr.Pipeline}\n * @see {@link lunr.trimmer}\n * @see {@link lunr.stopWordFilter}\n * @see {@link lunr.stemmer}\n * @namespace {function} lunr\n */\nvar lunr = function (config) {\n var builder = new lunr.Builder\n\n builder.pipeline.add(\n lunr.trimmer,\n lunr.stopWordFilter,\n lunr.stemmer\n )\n\n builder.searchPipeline.add(\n lunr.stemmer\n )\n\n config.call(builder, builder)\n return builder.build()\n}\n\nlunr.version = \"2.3.9\"\n/*!\n * lunr.utils\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A namespace containing utils for the rest of the lunr library\n * @namespace lunr.utils\n */\nlunr.utils = {}\n\n/**\n * Print a warning message to the console.\n *\n * @param {String} message The message to be printed.\n * @memberOf lunr.utils\n * @function\n */\nlunr.utils.warn = (function (global) {\n /* eslint-disable no-console */\n return function (message) {\n if (global.console && console.warn) {\n console.warn(message)\n }\n }\n /* eslint-enable no-console */\n})(this)\n\n/**\n * Convert an object to a string.\n *\n * In the case of `null` and `undefined` the function returns\n * the empty string, in all other cases the result of calling\n * `toString` on the passed object is returned.\n *\n * @param {Any} obj The object to convert to a string.\n * @return {String} string representation of the passed object.\n * @memberOf lunr.utils\n */\nlunr.utils.asString = function (obj) {\n if (obj === void 0 || obj === null) {\n return \"\"\n } else {\n return obj.toString()\n }\n}\n\n/**\n * Clones an object.\n *\n * Will create a copy of an existing object such that any mutations\n * on the copy cannot affect the original.\n *\n * Only shallow objects are supported, passing a nested object to this\n * function will cause a TypeError.\n *\n * Objects with primitives, and arrays of primitives are supported.\n *\n * @param {Object} obj The object to clone.\n * @return {Object} a clone of the passed object.\n * @throws {TypeError} when a nested object is passed.\n * @memberOf Utils\n */\nlunr.utils.clone = function (obj) {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n var clone = Object.create(null),\n keys = Object.keys(obj)\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i],\n val = obj[key]\n\n if (Array.isArray(val)) {\n clone[key] = val.slice()\n continue\n }\n\n if (typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean') {\n clone[key] = val\n continue\n }\n\n throw new TypeError(\"clone is not deep and does not support nested objects\")\n }\n\n return clone\n}\nlunr.FieldRef = function (docRef, fieldName, stringValue) {\n this.docRef = docRef\n this.fieldName = fieldName\n this._stringValue = stringValue\n}\n\nlunr.FieldRef.joiner = \"/\"\n\nlunr.FieldRef.fromString = function (s) {\n var n = s.indexOf(lunr.FieldRef.joiner)\n\n if (n === -1) {\n throw \"malformed field ref string\"\n }\n\n var fieldRef = s.slice(0, n),\n docRef = s.slice(n + 1)\n\n return new lunr.FieldRef (docRef, fieldRef, s)\n}\n\nlunr.FieldRef.prototype.toString = function () {\n if (this._stringValue == undefined) {\n this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef\n }\n\n return this._stringValue\n}\n/*!\n * lunr.Set\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A lunr set.\n *\n * @constructor\n */\nlunr.Set = function (elements) {\n this.elements = Object.create(null)\n\n if (elements) {\n this.length = elements.length\n\n for (var i = 0; i < this.length; i++) {\n this.elements[elements[i]] = true\n }\n } else {\n this.length = 0\n }\n}\n\n/**\n * A complete set that contains all elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.complete = {\n intersect: function (other) {\n return other\n },\n\n union: function () {\n return this\n },\n\n contains: function () {\n return true\n }\n}\n\n/**\n * An empty set that contains no elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.empty = {\n intersect: function () {\n return this\n },\n\n union: function (other) {\n return other\n },\n\n contains: function () {\n return false\n }\n}\n\n/**\n * Returns true if this set contains the specified object.\n *\n * @param {object} object - Object whose presence in this set is to be tested.\n * @returns {boolean} - True if this set contains the specified object.\n */\nlunr.Set.prototype.contains = function (object) {\n return !!this.elements[object]\n}\n\n/**\n * Returns a new set containing only the elements that are present in both\n * this set and the specified set.\n *\n * @param {lunr.Set} other - set to intersect with this set.\n * @returns {lunr.Set} a new set that is the intersection of this and the specified set.\n */\n\nlunr.Set.prototype.intersect = function (other) {\n var a, b, elements, intersection = []\n\n if (other === lunr.Set.complete) {\n return this\n }\n\n if (other === lunr.Set.empty) {\n return other\n }\n\n if (this.length < other.length) {\n a = this\n b = other\n } else {\n a = other\n b = this\n }\n\n elements = Object.keys(a.elements)\n\n for (var i = 0; i < elements.length; i++) {\n var element = elements[i]\n if (element in b.elements) {\n intersection.push(element)\n }\n }\n\n return new lunr.Set (intersection)\n}\n\n/**\n * Returns a new set combining the elements of this and the specified set.\n *\n * @param {lunr.Set} other - set to union with this set.\n * @return {lunr.Set} a new set that is the union of this and the specified set.\n */\n\nlunr.Set.prototype.union = function (other) {\n if (other === lunr.Set.complete) {\n return lunr.Set.complete\n }\n\n if (other === lunr.Set.empty) {\n return this\n }\n\n return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements)))\n}\n/**\n * A function to calculate the inverse document frequency for\n * a posting. This is shared between the builder and the index\n *\n * @private\n * @param {object} posting - The posting for a given term\n * @param {number} documentCount - The total number of documents.\n */\nlunr.idf = function (posting, documentCount) {\n var documentsWithTerm = 0\n\n for (var fieldName in posting) {\n if (fieldName == '_index') continue // Ignore the term index, its not a field\n documentsWithTerm += Object.keys(posting[fieldName]).length\n }\n\n var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5)\n\n return Math.log(1 + Math.abs(x))\n}\n\n/**\n * A token wraps a string representation of a token\n * as it is passed through the text processing pipeline.\n *\n * @constructor\n * @param {string} [str=''] - The string token being wrapped.\n * @param {object} [metadata={}] - Metadata associated with this token.\n */\nlunr.Token = function (str, metadata) {\n this.str = str || \"\"\n this.metadata = metadata || {}\n}\n\n/**\n * Returns the token string that is being wrapped by this object.\n *\n * @returns {string}\n */\nlunr.Token.prototype.toString = function () {\n return this.str\n}\n\n/**\n * A token update function is used when updating or optionally\n * when cloning a token.\n *\n * @callback lunr.Token~updateFunction\n * @param {string} str - The string representation of the token.\n * @param {Object} metadata - All metadata associated with this token.\n */\n\n/**\n * Applies the given function to the wrapped string token.\n *\n * @example\n * token.update(function (str, metadata) {\n * return str.toUpperCase()\n * })\n *\n * @param {lunr.Token~updateFunction} fn - A function to apply to the token string.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.update = function (fn) {\n this.str = fn(this.str, this.metadata)\n return this\n}\n\n/**\n * Creates a clone of this token. Optionally a function can be\n * applied to the cloned token.\n *\n * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.clone = function (fn) {\n fn = fn || function (s) { return s }\n return new lunr.Token (fn(this.str, this.metadata), this.metadata)\n}\n/*!\n * lunr.tokenizer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A function for splitting a string into tokens ready to be inserted into\n * the search index. Uses `lunr.tokenizer.separator` to split strings, change\n * the value of this property to change how strings are split into tokens.\n *\n * This tokenizer will convert its parameter to a string by calling `toString` and\n * then will split this string on the character in `lunr.tokenizer.separator`.\n * Arrays will have their elements converted to strings and wrapped in a lunr.Token.\n *\n * Optional metadata can be passed to the tokenizer, this metadata will be cloned and\n * added as metadata to every token that is created from the object to be tokenized.\n *\n * @static\n * @param {?(string|object|object[])} obj - The object to convert into tokens\n * @param {?object} metadata - Optional metadata to associate with every token\n * @returns {lunr.Token[]}\n * @see {@link lunr.Pipeline}\n */\nlunr.tokenizer = function (obj, metadata) {\n if (obj == null || obj == undefined) {\n return []\n }\n\n if (Array.isArray(obj)) {\n return obj.map(function (t) {\n return new lunr.Token(\n lunr.utils.asString(t).toLowerCase(),\n lunr.utils.clone(metadata)\n )\n })\n }\n\n var str = obj.toString().toLowerCase(),\n len = str.length,\n tokens = []\n\n for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) {\n var char = str.charAt(sliceEnd),\n sliceLength = sliceEnd - sliceStart\n\n if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {\n\n if (sliceLength > 0) {\n var tokenMetadata = lunr.utils.clone(metadata) || {}\n tokenMetadata[\"position\"] = [sliceStart, sliceLength]\n tokenMetadata[\"index\"] = tokens.length\n\n tokens.push(\n new lunr.Token (\n str.slice(sliceStart, sliceEnd),\n tokenMetadata\n )\n )\n }\n\n sliceStart = sliceEnd + 1\n }\n\n }\n\n return tokens\n}\n\n/**\n * The separator used to split a string into tokens. Override this property to change the behaviour of\n * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.\n *\n * @static\n * @see lunr.tokenizer\n */\nlunr.tokenizer.separator = /[\\s\\-]+/\n/*!\n * lunr.Pipeline\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Pipelines maintain an ordered list of functions to be applied to all\n * tokens in documents entering the search index and queries being ran against\n * the index.\n *\n * An instance of lunr.Index created with the lunr shortcut will contain a\n * pipeline with a stop word filter and an English language stemmer. Extra\n * functions can be added before or after either of these functions or these\n * default functions can be removed.\n *\n * When run the pipeline will call each function in turn, passing a token, the\n * index of that token in the original list of all tokens and finally a list of\n * all the original tokens.\n *\n * The output of functions in the pipeline will be passed to the next function\n * in the pipeline. To exclude a token from entering the index the function\n * should return undefined, the rest of the pipeline will not be called with\n * this token.\n *\n * For serialisation of pipelines to work, all functions used in an instance of\n * a pipeline should be registered with lunr.Pipeline. Registered functions can\n * then be loaded. If trying to load a serialised pipeline that uses functions\n * that are not registered an error will be thrown.\n *\n * If not planning on serialising the pipeline then registering pipeline functions\n * is not necessary.\n *\n * @constructor\n */\nlunr.Pipeline = function () {\n this._stack = []\n}\n\nlunr.Pipeline.registeredFunctions = Object.create(null)\n\n/**\n * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token\n * string as well as all known metadata. A pipeline function can mutate the token string\n * or mutate (or add) metadata for a given token.\n *\n * A pipeline function can indicate that the passed token should be discarded by returning\n * null, undefined or an empty string. This token will not be passed to any downstream pipeline\n * functions and will not be added to the index.\n *\n * Multiple tokens can be returned by returning an array of tokens. Each token will be passed\n * to any downstream pipeline functions and all will returned tokens will be added to the index.\n *\n * Any number of pipeline functions may be chained together using a lunr.Pipeline.\n *\n * @interface lunr.PipelineFunction\n * @param {lunr.Token} token - A token from the document being processed.\n * @param {number} i - The index of this token in the complete list of tokens for this document/field.\n * @param {lunr.Token[]} tokens - All tokens for this document/field.\n * @returns {(?lunr.Token|lunr.Token[])}\n */\n\n/**\n * Register a function with the pipeline.\n *\n * Functions that are used in the pipeline should be registered if the pipeline\n * needs to be serialised, or a serialised pipeline needs to be loaded.\n *\n * Registering a function does not add it to a pipeline, functions must still be\n * added to instances of the pipeline for them to be used when running a pipeline.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @param {String} label - The label to register this function with\n */\nlunr.Pipeline.registerFunction = function (fn, label) {\n if (label in this.registeredFunctions) {\n lunr.utils.warn('Overwriting existing registered function: ' + label)\n }\n\n fn.label = label\n lunr.Pipeline.registeredFunctions[fn.label] = fn\n}\n\n/**\n * Warns if the function is not registered as a Pipeline function.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @private\n */\nlunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {\n var isRegistered = fn.label && (fn.label in this.registeredFunctions)\n\n if (!isRegistered) {\n lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\\n', fn)\n }\n}\n\n/**\n * Loads a previously serialised pipeline.\n *\n * All functions to be loaded must already be registered with lunr.Pipeline.\n * If any function from the serialised data has not been registered then an\n * error will be thrown.\n *\n * @param {Object} serialised - The serialised pipeline to load.\n * @returns {lunr.Pipeline}\n */\nlunr.Pipeline.load = function (serialised) {\n var pipeline = new lunr.Pipeline\n\n serialised.forEach(function (fnName) {\n var fn = lunr.Pipeline.registeredFunctions[fnName]\n\n if (fn) {\n pipeline.add(fn)\n } else {\n throw new Error('Cannot load unregistered function: ' + fnName)\n }\n })\n\n return pipeline\n}\n\n/**\n * Adds new functions to the end of the pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline.\n */\nlunr.Pipeline.prototype.add = function () {\n var fns = Array.prototype.slice.call(arguments)\n\n fns.forEach(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n this._stack.push(fn)\n }, this)\n}\n\n/**\n * Adds a single function after a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.after = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n pos = pos + 1\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Adds a single function before a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.before = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Removes a function from the pipeline.\n *\n * @param {lunr.PipelineFunction} fn The function to remove from the pipeline.\n */\nlunr.Pipeline.prototype.remove = function (fn) {\n var pos = this._stack.indexOf(fn)\n if (pos == -1) {\n return\n }\n\n this._stack.splice(pos, 1)\n}\n\n/**\n * Runs the current list of functions that make up the pipeline against the\n * passed tokens.\n *\n * @param {Array} tokens The tokens to run through the pipeline.\n * @returns {Array}\n */\nlunr.Pipeline.prototype.run = function (tokens) {\n var stackLength = this._stack.length\n\n for (var i = 0; i < stackLength; i++) {\n var fn = this._stack[i]\n var memo = []\n\n for (var j = 0; j < tokens.length; j++) {\n var result = fn(tokens[j], j, tokens)\n\n if (result === null || result === void 0 || result === '') continue\n\n if (Array.isArray(result)) {\n for (var k = 0; k < result.length; k++) {\n memo.push(result[k])\n }\n } else {\n memo.push(result)\n }\n }\n\n tokens = memo\n }\n\n return tokens\n}\n\n/**\n * Convenience method for passing a string through a pipeline and getting\n * strings out. This method takes care of wrapping the passed string in a\n * token and mapping the resulting tokens back to strings.\n *\n * @param {string} str - The string to pass through the pipeline.\n * @param {?object} metadata - Optional metadata to associate with the token\n * passed to the pipeline.\n * @returns {string[]}\n */\nlunr.Pipeline.prototype.runString = function (str, metadata) {\n var token = new lunr.Token (str, metadata)\n\n return this.run([token]).map(function (t) {\n return t.toString()\n })\n}\n\n/**\n * Resets the pipeline by removing any existing processors.\n *\n */\nlunr.Pipeline.prototype.reset = function () {\n this._stack = []\n}\n\n/**\n * Returns a representation of the pipeline ready for serialisation.\n *\n * Logs a warning if the function has not been registered.\n *\n * @returns {Array}\n */\nlunr.Pipeline.prototype.toJSON = function () {\n return this._stack.map(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n\n return fn.label\n })\n}\n/*!\n * lunr.Vector\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A vector is used to construct the vector space of documents and queries. These\n * vectors support operations to determine the similarity between two documents or\n * a document and a query.\n *\n * Normally no parameters are required for initializing a vector, but in the case of\n * loading a previously dumped vector the raw elements can be provided to the constructor.\n *\n * For performance reasons vectors are implemented with a flat array, where an elements\n * index is immediately followed by its value. E.g. [index, value, index, value]. This\n * allows the underlying array to be as sparse as possible and still offer decent\n * performance when being used for vector calculations.\n *\n * @constructor\n * @param {Number[]} [elements] - The flat list of element index and element value pairs.\n */\nlunr.Vector = function (elements) {\n this._magnitude = 0\n this.elements = elements || []\n}\n\n\n/**\n * Calculates the position within the vector to insert a given index.\n *\n * This is used internally by insert and upsert. If there are duplicate indexes then\n * the position is returned as if the value for that index were to be updated, but it\n * is the callers responsibility to check whether there is a duplicate at that index\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @returns {Number}\n */\nlunr.Vector.prototype.positionForIndex = function (index) {\n // For an empty vector the tuple can be inserted at the beginning\n if (this.elements.length == 0) {\n return 0\n }\n\n var start = 0,\n end = this.elements.length / 2,\n sliceLength = end - start,\n pivotPoint = Math.floor(sliceLength / 2),\n pivotIndex = this.elements[pivotPoint * 2]\n\n while (sliceLength > 1) {\n if (pivotIndex < index) {\n start = pivotPoint\n }\n\n if (pivotIndex > index) {\n end = pivotPoint\n }\n\n if (pivotIndex == index) {\n break\n }\n\n sliceLength = end - start\n pivotPoint = start + Math.floor(sliceLength / 2)\n pivotIndex = this.elements[pivotPoint * 2]\n }\n\n if (pivotIndex == index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex > index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex < index) {\n return (pivotPoint + 1) * 2\n }\n}\n\n/**\n * Inserts an element at an index within the vector.\n *\n * Does not allow duplicates, will throw an error if there is already an entry\n * for this index.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n */\nlunr.Vector.prototype.insert = function (insertIdx, val) {\n this.upsert(insertIdx, val, function () {\n throw \"duplicate index\"\n })\n}\n\n/**\n * Inserts or updates an existing index within the vector.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n * @param {function} fn - A function that is called for updates, the existing value and the\n * requested value are passed as arguments\n */\nlunr.Vector.prototype.upsert = function (insertIdx, val, fn) {\n this._magnitude = 0\n var position = this.positionForIndex(insertIdx)\n\n if (this.elements[position] == insertIdx) {\n this.elements[position + 1] = fn(this.elements[position + 1], val)\n } else {\n this.elements.splice(position, 0, insertIdx, val)\n }\n}\n\n/**\n * Calculates the magnitude of this vector.\n *\n * @returns {Number}\n */\nlunr.Vector.prototype.magnitude = function () {\n if (this._magnitude) return this._magnitude\n\n var sumOfSquares = 0,\n elementsLength = this.elements.length\n\n for (var i = 1; i < elementsLength; i += 2) {\n var val = this.elements[i]\n sumOfSquares += val * val\n }\n\n return this._magnitude = Math.sqrt(sumOfSquares)\n}\n\n/**\n * Calculates the dot product of this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The vector to compute the dot product with.\n * @returns {Number}\n */\nlunr.Vector.prototype.dot = function (otherVector) {\n var dotProduct = 0,\n a = this.elements, b = otherVector.elements,\n aLen = a.length, bLen = b.length,\n aVal = 0, bVal = 0,\n i = 0, j = 0\n\n while (i < aLen && j < bLen) {\n aVal = a[i], bVal = b[j]\n if (aVal < bVal) {\n i += 2\n } else if (aVal > bVal) {\n j += 2\n } else if (aVal == bVal) {\n dotProduct += a[i + 1] * b[j + 1]\n i += 2\n j += 2\n }\n }\n\n return dotProduct\n}\n\n/**\n * Calculates the similarity between this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The other vector to calculate the\n * similarity with.\n * @returns {Number}\n */\nlunr.Vector.prototype.similarity = function (otherVector) {\n return this.dot(otherVector) / this.magnitude() || 0\n}\n\n/**\n * Converts the vector to an array of the elements within the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toArray = function () {\n var output = new Array (this.elements.length / 2)\n\n for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {\n output[j] = this.elements[i]\n }\n\n return output\n}\n\n/**\n * A JSON serializable representation of the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toJSON = function () {\n return this.elements\n}\n/* eslint-disable */\n/*!\n * lunr.stemmer\n * Copyright (C) 2020 Oliver Nightingale\n * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt\n */\n\n/**\n * lunr.stemmer is an english language stemmer, this is a JavaScript\n * implementation of the PorterStemmer taken from http://tartarus.org/~martin\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token - The string to stem\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n * @function\n */\nlunr.stemmer = (function(){\n var step2list = {\n \"ational\" : \"ate\",\n \"tional\" : \"tion\",\n \"enci\" : \"ence\",\n \"anci\" : \"ance\",\n \"izer\" : \"ize\",\n \"bli\" : \"ble\",\n \"alli\" : \"al\",\n \"entli\" : \"ent\",\n \"eli\" : \"e\",\n \"ousli\" : \"ous\",\n \"ization\" : \"ize\",\n \"ation\" : \"ate\",\n \"ator\" : \"ate\",\n \"alism\" : \"al\",\n \"iveness\" : \"ive\",\n \"fulness\" : \"ful\",\n \"ousness\" : \"ous\",\n \"aliti\" : \"al\",\n \"iviti\" : \"ive\",\n \"biliti\" : \"ble\",\n \"logi\" : \"log\"\n },\n\n step3list = {\n \"icate\" : \"ic\",\n \"ative\" : \"\",\n \"alize\" : \"al\",\n \"iciti\" : \"ic\",\n \"ical\" : \"ic\",\n \"ful\" : \"\",\n \"ness\" : \"\"\n },\n\n c = \"[^aeiou]\", // consonant\n v = \"[aeiouy]\", // vowel\n C = c + \"[^aeiouy]*\", // consonant sequence\n V = v + \"[aeiou]*\", // vowel sequence\n\n mgr0 = \"^(\" + C + \")?\" + V + C, // [C]VC... is m>0\n meq1 = \"^(\" + C + \")?\" + V + C + \"(\" + V + \")?$\", // [C]VC[V] is m=1\n mgr1 = \"^(\" + C + \")?\" + V + C + V + C, // [C]VCVC... is m>1\n s_v = \"^(\" + C + \")?\" + v; // vowel in stem\n\n var re_mgr0 = new RegExp(mgr0);\n var re_mgr1 = new RegExp(mgr1);\n var re_meq1 = new RegExp(meq1);\n var re_s_v = new RegExp(s_v);\n\n var re_1a = /^(.+?)(ss|i)es$/;\n var re2_1a = /^(.+?)([^s])s$/;\n var re_1b = /^(.+?)eed$/;\n var re2_1b = /^(.+?)(ed|ing)$/;\n var re_1b_2 = /.$/;\n var re2_1b_2 = /(at|bl|iz)$/;\n var re3_1b_2 = new RegExp(\"([^aeiouylsz])\\\\1$\");\n var re4_1b_2 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var re_1c = /^(.+?[^aeiou])y$/;\n var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;\n\n var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;\n\n var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;\n var re2_4 = /^(.+?)(s|t)(ion)$/;\n\n var re_5 = /^(.+?)e$/;\n var re_5_1 = /ll$/;\n var re3_5 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var porterStemmer = function porterStemmer(w) {\n var stem,\n suffix,\n firstch,\n re,\n re2,\n re3,\n re4;\n\n if (w.length < 3) { return w; }\n\n firstch = w.substr(0,1);\n if (firstch == \"y\") {\n w = firstch.toUpperCase() + w.substr(1);\n }\n\n // Step 1a\n re = re_1a\n re2 = re2_1a;\n\n if (re.test(w)) { w = w.replace(re,\"$1$2\"); }\n else if (re2.test(w)) { w = w.replace(re2,\"$1$2\"); }\n\n // Step 1b\n re = re_1b;\n re2 = re2_1b;\n if (re.test(w)) {\n var fp = re.exec(w);\n re = re_mgr0;\n if (re.test(fp[1])) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1];\n re2 = re_s_v;\n if (re2.test(stem)) {\n w = stem;\n re2 = re2_1b_2;\n re3 = re3_1b_2;\n re4 = re4_1b_2;\n if (re2.test(w)) { w = w + \"e\"; }\n else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,\"\"); }\n else if (re4.test(w)) { w = w + \"e\"; }\n }\n }\n\n // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)\n re = re_1c;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n w = stem + \"i\";\n }\n\n // Step 2\n re = re_2;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step2list[suffix];\n }\n }\n\n // Step 3\n re = re_3;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step3list[suffix];\n }\n }\n\n // Step 4\n re = re_4;\n re2 = re2_4;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n if (re.test(stem)) {\n w = stem;\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1] + fp[2];\n re2 = re_mgr1;\n if (re2.test(stem)) {\n w = stem;\n }\n }\n\n // Step 5\n re = re_5;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n re2 = re_meq1;\n re3 = re3_5;\n if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {\n w = stem;\n }\n }\n\n re = re_5_1;\n re2 = re_mgr1;\n if (re.test(w) && re2.test(w)) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n\n // and turn initial Y back to y\n\n if (firstch == \"y\") {\n w = firstch.toLowerCase() + w.substr(1);\n }\n\n return w;\n };\n\n return function (token) {\n return token.update(porterStemmer);\n }\n})();\n\nlunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')\n/*!\n * lunr.stopWordFilter\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.generateStopWordFilter builds a stopWordFilter function from the provided\n * list of stop words.\n *\n * The built in lunr.stopWordFilter is built using this generator and can be used\n * to generate custom stopWordFilters for applications or non English languages.\n *\n * @function\n * @param {Array} token The token to pass through the filter\n * @returns {lunr.PipelineFunction}\n * @see lunr.Pipeline\n * @see lunr.stopWordFilter\n */\nlunr.generateStopWordFilter = function (stopWords) {\n var words = stopWords.reduce(function (memo, stopWord) {\n memo[stopWord] = stopWord\n return memo\n }, {})\n\n return function (token) {\n if (token && words[token.toString()] !== token.toString()) return token\n }\n}\n\n/**\n * lunr.stopWordFilter is an English language stop word list filter, any words\n * contained in the list will not be passed through the filter.\n *\n * This is intended to be used in the Pipeline. If the token does not pass the\n * filter then undefined will be returned.\n *\n * @function\n * @implements {lunr.PipelineFunction}\n * @params {lunr.Token} token - A token to check for being a stop word.\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n */\nlunr.stopWordFilter = lunr.generateStopWordFilter([\n 'a',\n 'able',\n 'about',\n 'across',\n 'after',\n 'all',\n 'almost',\n 'also',\n 'am',\n 'among',\n 'an',\n 'and',\n 'any',\n 'are',\n 'as',\n 'at',\n 'be',\n 'because',\n 'been',\n 'but',\n 'by',\n 'can',\n 'cannot',\n 'could',\n 'dear',\n 'did',\n 'do',\n 'does',\n 'either',\n 'else',\n 'ever',\n 'every',\n 'for',\n 'from',\n 'get',\n 'got',\n 'had',\n 'has',\n 'have',\n 'he',\n 'her',\n 'hers',\n 'him',\n 'his',\n 'how',\n 'however',\n 'i',\n 'if',\n 'in',\n 'into',\n 'is',\n 'it',\n 'its',\n 'just',\n 'least',\n 'let',\n 'like',\n 'likely',\n 'may',\n 'me',\n 'might',\n 'most',\n 'must',\n 'my',\n 'neither',\n 'no',\n 'nor',\n 'not',\n 'of',\n 'off',\n 'often',\n 'on',\n 'only',\n 'or',\n 'other',\n 'our',\n 'own',\n 'rather',\n 'said',\n 'say',\n 'says',\n 'she',\n 'should',\n 'since',\n 'so',\n 'some',\n 'than',\n 'that',\n 'the',\n 'their',\n 'them',\n 'then',\n 'there',\n 'these',\n 'they',\n 'this',\n 'tis',\n 'to',\n 'too',\n 'twas',\n 'us',\n 'wants',\n 'was',\n 'we',\n 'were',\n 'what',\n 'when',\n 'where',\n 'which',\n 'while',\n 'who',\n 'whom',\n 'why',\n 'will',\n 'with',\n 'would',\n 'yet',\n 'you',\n 'your'\n])\n\nlunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')\n/*!\n * lunr.trimmer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.trimmer is a pipeline function for trimming non word\n * characters from the beginning and end of tokens before they\n * enter the index.\n *\n * This implementation may not work correctly for non latin\n * characters and should either be removed or adapted for use\n * with languages with non-latin characters.\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token The token to pass through the filter\n * @returns {lunr.Token}\n * @see lunr.Pipeline\n */\nlunr.trimmer = function (token) {\n return token.update(function (s) {\n return s.replace(/^\\W+/, '').replace(/\\W+$/, '')\n })\n}\n\nlunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')\n/*!\n * lunr.TokenSet\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A token set is used to store the unique list of all tokens\n * within an index. Token sets are also used to represent an\n * incoming query to the index, this query token set and index\n * token set are then intersected to find which tokens to look\n * up in the inverted index.\n *\n * A token set can hold multiple tokens, as in the case of the\n * index token set, or it can hold a single token as in the\n * case of a simple query token set.\n *\n * Additionally token sets are used to perform wildcard matching.\n * Leading, contained and trailing wildcards are supported, and\n * from this edit distance matching can also be provided.\n *\n * Token sets are implemented as a minimal finite state automata,\n * where both common prefixes and suffixes are shared between tokens.\n * This helps to reduce the space used for storing the token set.\n *\n * @constructor\n */\nlunr.TokenSet = function () {\n this.final = false\n this.edges = {}\n this.id = lunr.TokenSet._nextId\n lunr.TokenSet._nextId += 1\n}\n\n/**\n * Keeps track of the next, auto increment, identifier to assign\n * to a new tokenSet.\n *\n * TokenSets require a unique identifier to be correctly minimised.\n *\n * @private\n */\nlunr.TokenSet._nextId = 1\n\n/**\n * Creates a TokenSet instance from the given sorted array of words.\n *\n * @param {String[]} arr - A sorted array of strings to create the set from.\n * @returns {lunr.TokenSet}\n * @throws Will throw an error if the input array is not sorted.\n */\nlunr.TokenSet.fromArray = function (arr) {\n var builder = new lunr.TokenSet.Builder\n\n for (var i = 0, len = arr.length; i < len; i++) {\n builder.insert(arr[i])\n }\n\n builder.finish()\n return builder.root\n}\n\n/**\n * Creates a token set from a query clause.\n *\n * @private\n * @param {Object} clause - A single clause from lunr.Query.\n * @param {string} clause.term - The query clause term.\n * @param {number} [clause.editDistance] - The optional edit distance for the term.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromClause = function (clause) {\n if ('editDistance' in clause) {\n return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance)\n } else {\n return lunr.TokenSet.fromString(clause.term)\n }\n}\n\n/**\n * Creates a token set representing a single string with a specified\n * edit distance.\n *\n * Insertions, deletions, substitutions and transpositions are each\n * treated as an edit distance of 1.\n *\n * Increasing the allowed edit distance will have a dramatic impact\n * on the performance of both creating and intersecting these TokenSets.\n * It is advised to keep the edit distance less than 3.\n *\n * @param {string} str - The string to create the token set from.\n * @param {number} editDistance - The allowed edit distance to match.\n * @returns {lunr.Vector}\n */\nlunr.TokenSet.fromFuzzyString = function (str, editDistance) {\n var root = new lunr.TokenSet\n\n var stack = [{\n node: root,\n editsRemaining: editDistance,\n str: str\n }]\n\n while (stack.length) {\n var frame = stack.pop()\n\n // no edit\n if (frame.str.length > 0) {\n var char = frame.str.charAt(0),\n noEditNode\n\n if (char in frame.node.edges) {\n noEditNode = frame.node.edges[char]\n } else {\n noEditNode = new lunr.TokenSet\n frame.node.edges[char] = noEditNode\n }\n\n if (frame.str.length == 1) {\n noEditNode.final = true\n }\n\n stack.push({\n node: noEditNode,\n editsRemaining: frame.editsRemaining,\n str: frame.str.slice(1)\n })\n }\n\n if (frame.editsRemaining == 0) {\n continue\n }\n\n // insertion\n if (\"*\" in frame.node.edges) {\n var insertionNode = frame.node.edges[\"*\"]\n } else {\n var insertionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = insertionNode\n }\n\n if (frame.str.length == 0) {\n insertionNode.final = true\n }\n\n stack.push({\n node: insertionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str\n })\n\n // deletion\n // can only do a deletion if we have enough edits remaining\n // and if there are characters left to delete in the string\n if (frame.str.length > 1) {\n stack.push({\n node: frame.node,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // deletion\n // just removing the last character from the str\n if (frame.str.length == 1) {\n frame.node.final = true\n }\n\n // substitution\n // can only do a substitution if we have enough edits remaining\n // and if there are characters left to substitute\n if (frame.str.length >= 1) {\n if (\"*\" in frame.node.edges) {\n var substitutionNode = frame.node.edges[\"*\"]\n } else {\n var substitutionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = substitutionNode\n }\n\n if (frame.str.length == 1) {\n substitutionNode.final = true\n }\n\n stack.push({\n node: substitutionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // transposition\n // can only do a transposition if there are edits remaining\n // and there are enough characters to transpose\n if (frame.str.length > 1) {\n var charA = frame.str.charAt(0),\n charB = frame.str.charAt(1),\n transposeNode\n\n if (charB in frame.node.edges) {\n transposeNode = frame.node.edges[charB]\n } else {\n transposeNode = new lunr.TokenSet\n frame.node.edges[charB] = transposeNode\n }\n\n if (frame.str.length == 1) {\n transposeNode.final = true\n }\n\n stack.push({\n node: transposeNode,\n editsRemaining: frame.editsRemaining - 1,\n str: charA + frame.str.slice(2)\n })\n }\n }\n\n return root\n}\n\n/**\n * Creates a TokenSet from a string.\n *\n * The string may contain one or more wildcard characters (*)\n * that will allow wildcard matching when intersecting with\n * another TokenSet.\n *\n * @param {string} str - The string to create a TokenSet from.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromString = function (str) {\n var node = new lunr.TokenSet,\n root = node\n\n /*\n * Iterates through all characters within the passed string\n * appending a node for each character.\n *\n * When a wildcard character is found then a self\n * referencing edge is introduced to continually match\n * any number of any characters.\n */\n for (var i = 0, len = str.length; i < len; i++) {\n var char = str[i],\n final = (i == len - 1)\n\n if (char == \"*\") {\n node.edges[char] = node\n node.final = final\n\n } else {\n var next = new lunr.TokenSet\n next.final = final\n\n node.edges[char] = next\n node = next\n }\n }\n\n return root\n}\n\n/**\n * Converts this TokenSet into an array of strings\n * contained within the TokenSet.\n *\n * This is not intended to be used on a TokenSet that\n * contains wildcards, in these cases the results are\n * undefined and are likely to cause an infinite loop.\n *\n * @returns {string[]}\n */\nlunr.TokenSet.prototype.toArray = function () {\n var words = []\n\n var stack = [{\n prefix: \"\",\n node: this\n }]\n\n while (stack.length) {\n var frame = stack.pop(),\n edges = Object.keys(frame.node.edges),\n len = edges.length\n\n if (frame.node.final) {\n /* In Safari, at this point the prefix is sometimes corrupted, see:\n * https://github.com/olivernn/lunr.js/issues/279 Calling any\n * String.prototype method forces Safari to \"cast\" this string to what\n * it's supposed to be, fixing the bug. */\n frame.prefix.charAt(0)\n words.push(frame.prefix)\n }\n\n for (var i = 0; i < len; i++) {\n var edge = edges[i]\n\n stack.push({\n prefix: frame.prefix.concat(edge),\n node: frame.node.edges[edge]\n })\n }\n }\n\n return words\n}\n\n/**\n * Generates a string representation of a TokenSet.\n *\n * This is intended to allow TokenSets to be used as keys\n * in objects, largely to aid the construction and minimisation\n * of a TokenSet. As such it is not designed to be a human\n * friendly representation of the TokenSet.\n *\n * @returns {string}\n */\nlunr.TokenSet.prototype.toString = function () {\n // NOTE: Using Object.keys here as this.edges is very likely\n // to enter 'hash-mode' with many keys being added\n //\n // avoiding a for-in loop here as it leads to the function\n // being de-optimised (at least in V8). From some simple\n // benchmarks the performance is comparable, but allowing\n // V8 to optimize may mean easy performance wins in the future.\n\n if (this._str) {\n return this._str\n }\n\n var str = this.final ? '1' : '0',\n labels = Object.keys(this.edges).sort(),\n len = labels.length\n\n for (var i = 0; i < len; i++) {\n var label = labels[i],\n node = this.edges[label]\n\n str = str + label + node.id\n }\n\n return str\n}\n\n/**\n * Returns a new TokenSet that is the intersection of\n * this TokenSet and the passed TokenSet.\n *\n * This intersection will take into account any wildcards\n * contained within the TokenSet.\n *\n * @param {lunr.TokenSet} b - An other TokenSet to intersect with.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.prototype.intersect = function (b) {\n var output = new lunr.TokenSet,\n frame = undefined\n\n var stack = [{\n qNode: b,\n output: output,\n node: this\n }]\n\n while (stack.length) {\n frame = stack.pop()\n\n // NOTE: As with the #toString method, we are using\n // Object.keys and a for loop instead of a for-in loop\n // as both of these objects enter 'hash' mode, causing\n // the function to be de-optimised in V8\n var qEdges = Object.keys(frame.qNode.edges),\n qLen = qEdges.length,\n nEdges = Object.keys(frame.node.edges),\n nLen = nEdges.length\n\n for (var q = 0; q < qLen; q++) {\n var qEdge = qEdges[q]\n\n for (var n = 0; n < nLen; n++) {\n var nEdge = nEdges[n]\n\n if (nEdge == qEdge || qEdge == '*') {\n var node = frame.node.edges[nEdge],\n qNode = frame.qNode.edges[qEdge],\n final = node.final && qNode.final,\n next = undefined\n\n if (nEdge in frame.output.edges) {\n // an edge already exists for this character\n // no need to create a new node, just set the finality\n // bit unless this node is already final\n next = frame.output.edges[nEdge]\n next.final = next.final || final\n\n } else {\n // no edge exists yet, must create one\n // set the finality bit and insert it\n // into the output\n next = new lunr.TokenSet\n next.final = final\n frame.output.edges[nEdge] = next\n }\n\n stack.push({\n qNode: qNode,\n output: next,\n node: node\n })\n }\n }\n }\n }\n\n return output\n}\nlunr.TokenSet.Builder = function () {\n this.previousWord = \"\"\n this.root = new lunr.TokenSet\n this.uncheckedNodes = []\n this.minimizedNodes = {}\n}\n\nlunr.TokenSet.Builder.prototype.insert = function (word) {\n var node,\n commonPrefix = 0\n\n if (word < this.previousWord) {\n throw new Error (\"Out of order word insertion\")\n }\n\n for (var i = 0; i < word.length && i < this.previousWord.length; i++) {\n if (word[i] != this.previousWord[i]) break\n commonPrefix++\n }\n\n this.minimize(commonPrefix)\n\n if (this.uncheckedNodes.length == 0) {\n node = this.root\n } else {\n node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child\n }\n\n for (var i = commonPrefix; i < word.length; i++) {\n var nextNode = new lunr.TokenSet,\n char = word[i]\n\n node.edges[char] = nextNode\n\n this.uncheckedNodes.push({\n parent: node,\n char: char,\n child: nextNode\n })\n\n node = nextNode\n }\n\n node.final = true\n this.previousWord = word\n}\n\nlunr.TokenSet.Builder.prototype.finish = function () {\n this.minimize(0)\n}\n\nlunr.TokenSet.Builder.prototype.minimize = function (downTo) {\n for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) {\n var node = this.uncheckedNodes[i],\n childKey = node.child.toString()\n\n if (childKey in this.minimizedNodes) {\n node.parent.edges[node.char] = this.minimizedNodes[childKey]\n } else {\n // Cache the key for this node since\n // we know it can't change anymore\n node.child._str = childKey\n\n this.minimizedNodes[childKey] = node.child\n }\n\n this.uncheckedNodes.pop()\n }\n}\n/*!\n * lunr.Index\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * An index contains the built index of all documents and provides a query interface\n * to the index.\n *\n * Usually instances of lunr.Index will not be created using this constructor, instead\n * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be\n * used to load previously built and serialized indexes.\n *\n * @constructor\n * @param {Object} attrs - The attributes of the built search index.\n * @param {Object} attrs.invertedIndex - An index of term/field to document reference.\n * @param {Object} attrs.fieldVectors - Field vectors\n * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.\n * @param {string[]} attrs.fields - The names of indexed document fields.\n * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.\n */\nlunr.Index = function (attrs) {\n this.invertedIndex = attrs.invertedIndex\n this.fieldVectors = attrs.fieldVectors\n this.tokenSet = attrs.tokenSet\n this.fields = attrs.fields\n this.pipeline = attrs.pipeline\n}\n\n/**\n * A result contains details of a document matching a search query.\n * @typedef {Object} lunr.Index~Result\n * @property {string} ref - The reference of the document this result represents.\n * @property {number} score - A number between 0 and 1 representing how similar this document is to the query.\n * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match.\n */\n\n/**\n * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple\n * query language which itself is parsed into an instance of lunr.Query.\n *\n * For programmatically building queries it is advised to directly use lunr.Query, the query language\n * is best used for human entered text rather than program generated text.\n *\n * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported\n * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello'\n * or 'world', though those that contain both will rank higher in the results.\n *\n * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can\n * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding\n * wildcards will increase the number of documents that will be found but can also have a negative\n * impact on query performance, especially with wildcards at the beginning of a term.\n *\n * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term\n * hello in the title field will match this query. Using a field not present in the index will lead\n * to an error being thrown.\n *\n * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term\n * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported\n * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.\n * Avoid large values for edit distance to improve query performance.\n *\n * Each term also supports a presence modifier. By default a term's presence in document is optional, however\n * this can be changed to either required or prohibited. For a term's presence to be required in a document the\n * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and\n * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not\n * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'.\n *\n * To escape special characters the backslash character '\\' can be used, this allows searches to include\n * characters that would normally be considered modifiers, e.g. `foo\\~2` will search for a term \"foo~2\" instead\n * of attempting to apply a boost of 2 to the search term \"foo\".\n *\n * @typedef {string} lunr.Index~QueryString\n * @example Simple single term query\n * hello\n * @example Multiple term query\n * hello world\n * @example term scoped to a field\n * title:hello\n * @example term with a boost of 10\n * hello^10\n * @example term with an edit distance of 2\n * hello~2\n * @example terms with presence modifiers\n * -foo +bar baz\n */\n\n/**\n * Performs a search against the index using lunr query syntax.\n *\n * Results will be returned sorted by their score, the most relevant results\n * will be returned first. For details on how the score is calculated, please see\n * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}.\n *\n * For more programmatic querying use lunr.Index#query.\n *\n * @param {lunr.Index~QueryString} queryString - A string containing a lunr query.\n * @throws {lunr.QueryParseError} If the passed query string cannot be parsed.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.search = function (queryString) {\n return this.query(function (query) {\n var parser = new lunr.QueryParser(queryString, query)\n parser.parse()\n })\n}\n\n/**\n * A query builder callback provides a query object to be used to express\n * the query to perform on the index.\n *\n * @callback lunr.Index~queryBuilder\n * @param {lunr.Query} query - The query object to build up.\n * @this lunr.Query\n */\n\n/**\n * Performs a query against the index using the yielded lunr.Query object.\n *\n * If performing programmatic queries against the index, this method is preferred\n * over lunr.Index#search so as to avoid the additional query parsing overhead.\n *\n * A query object is yielded to the supplied function which should be used to\n * express the query to be run against the index.\n *\n * Note that although this function takes a callback parameter it is _not_ an\n * asynchronous operation, the callback is just yielded a query object to be\n * customized.\n *\n * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.query = function (fn) {\n // for each query clause\n // * process terms\n // * expand terms from token set\n // * find matching documents and metadata\n // * get document vectors\n // * score documents\n\n var query = new lunr.Query(this.fields),\n matchingFields = Object.create(null),\n queryVectors = Object.create(null),\n termFieldCache = Object.create(null),\n requiredMatches = Object.create(null),\n prohibitedMatches = Object.create(null)\n\n /*\n * To support field level boosts a query vector is created per\n * field. An empty vector is eagerly created to support negated\n * queries.\n */\n for (var i = 0; i < this.fields.length; i++) {\n queryVectors[this.fields[i]] = new lunr.Vector\n }\n\n fn.call(query, query)\n\n for (var i = 0; i < query.clauses.length; i++) {\n /*\n * Unless the pipeline has been disabled for this term, which is\n * the case for terms with wildcards, we need to pass the clause\n * term through the search pipeline. A pipeline returns an array\n * of processed terms. Pipeline functions may expand the passed\n * term, which means we may end up performing multiple index lookups\n * for a single query term.\n */\n var clause = query.clauses[i],\n terms = null,\n clauseMatches = lunr.Set.empty\n\n if (clause.usePipeline) {\n terms = this.pipeline.runString(clause.term, {\n fields: clause.fields\n })\n } else {\n terms = [clause.term]\n }\n\n for (var m = 0; m < terms.length; m++) {\n var term = terms[m]\n\n /*\n * Each term returned from the pipeline needs to use the same query\n * clause object, e.g. the same boost and or edit distance. The\n * simplest way to do this is to re-use the clause object but mutate\n * its term property.\n */\n clause.term = term\n\n /*\n * From the term in the clause we create a token set which will then\n * be used to intersect the indexes token set to get a list of terms\n * to lookup in the inverted index\n */\n var termTokenSet = lunr.TokenSet.fromClause(clause),\n expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()\n\n /*\n * If a term marked as required does not exist in the tokenSet it is\n * impossible for the search to return any matches. We set all the field\n * scoped required matches set to empty and stop examining any further\n * clauses.\n */\n if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = lunr.Set.empty\n }\n\n break\n }\n\n for (var j = 0; j < expandedTerms.length; j++) {\n /*\n * For each term get the posting and termIndex, this is required for\n * building the query vector.\n */\n var expandedTerm = expandedTerms[j],\n posting = this.invertedIndex[expandedTerm],\n termIndex = posting._index\n\n for (var k = 0; k < clause.fields.length; k++) {\n /*\n * For each field that this query term is scoped by (by default\n * all fields are in scope) we need to get all the document refs\n * that have this term in that field.\n *\n * The posting is the entry in the invertedIndex for the matching\n * term from above.\n */\n var field = clause.fields[k],\n fieldPosting = posting[field],\n matchingDocumentRefs = Object.keys(fieldPosting),\n termField = expandedTerm + \"/\" + field,\n matchingDocumentsSet = new lunr.Set(matchingDocumentRefs)\n\n /*\n * if the presence of this term is required ensure that the matching\n * documents are added to the set of required matches for this clause.\n *\n */\n if (clause.presence == lunr.Query.presence.REQUIRED) {\n clauseMatches = clauseMatches.union(matchingDocumentsSet)\n\n if (requiredMatches[field] === undefined) {\n requiredMatches[field] = lunr.Set.complete\n }\n }\n\n /*\n * if the presence of this term is prohibited ensure that the matching\n * documents are added to the set of prohibited matches for this field,\n * creating that set if it does not yet exist.\n */\n if (clause.presence == lunr.Query.presence.PROHIBITED) {\n if (prohibitedMatches[field] === undefined) {\n prohibitedMatches[field] = lunr.Set.empty\n }\n\n prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet)\n\n /*\n * Prohibited matches should not be part of the query vector used for\n * similarity scoring and no metadata should be extracted so we continue\n * to the next field\n */\n continue\n }\n\n /*\n * The query field vector is populated using the termIndex found for\n * the term and a unit value with the appropriate boost applied.\n * Using upsert because there could already be an entry in the vector\n * for the term we are working with. In that case we just add the scores\n * together.\n */\n queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b })\n\n /**\n * If we've already seen this term, field combo then we've already collected\n * the matching documents and metadata, no need to go through all that again\n */\n if (termFieldCache[termField]) {\n continue\n }\n\n for (var l = 0; l < matchingDocumentRefs.length; l++) {\n /*\n * All metadata for this term/field/document triple\n * are then extracted and collected into an instance\n * of lunr.MatchData ready to be returned in the query\n * results\n */\n var matchingDocumentRef = matchingDocumentRefs[l],\n matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field),\n metadata = fieldPosting[matchingDocumentRef],\n fieldMatch\n\n if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) {\n matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata)\n } else {\n fieldMatch.add(expandedTerm, field, metadata)\n }\n\n }\n\n termFieldCache[termField] = true\n }\n }\n }\n\n /**\n * If the presence was required we need to update the requiredMatches field sets.\n * We do this after all fields for the term have collected their matches because\n * the clause terms presence is required in _any_ of the fields not _all_ of the\n * fields.\n */\n if (clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = requiredMatches[field].intersect(clauseMatches)\n }\n }\n }\n\n /**\n * Need to combine the field scoped required and prohibited\n * matching documents into a global set of required and prohibited\n * matches\n */\n var allRequiredMatches = lunr.Set.complete,\n allProhibitedMatches = lunr.Set.empty\n\n for (var i = 0; i < this.fields.length; i++) {\n var field = this.fields[i]\n\n if (requiredMatches[field]) {\n allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field])\n }\n\n if (prohibitedMatches[field]) {\n allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field])\n }\n }\n\n var matchingFieldRefs = Object.keys(matchingFields),\n results = [],\n matches = Object.create(null)\n\n /*\n * If the query is negated (contains only prohibited terms)\n * we need to get _all_ fieldRefs currently existing in the\n * index. This is only done when we know that the query is\n * entirely prohibited terms to avoid any cost of getting all\n * fieldRefs unnecessarily.\n *\n * Additionally, blank MatchData must be created to correctly\n * populate the results.\n */\n if (query.isNegated()) {\n matchingFieldRefs = Object.keys(this.fieldVectors)\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n var matchingFieldRef = matchingFieldRefs[i]\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRef)\n matchingFields[matchingFieldRef] = new lunr.MatchData\n }\n }\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n /*\n * Currently we have document fields that match the query, but we\n * need to return documents. The matchData and scores are combined\n * from multiple fields belonging to the same document.\n *\n * Scores are calculated by field, using the query vectors created\n * above, and combined into a final document score using addition.\n */\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),\n docRef = fieldRef.docRef\n\n if (!allRequiredMatches.contains(docRef)) {\n continue\n }\n\n if (allProhibitedMatches.contains(docRef)) {\n continue\n }\n\n var fieldVector = this.fieldVectors[fieldRef],\n score = queryVectors[fieldRef.fieldName].similarity(fieldVector),\n docMatch\n\n if ((docMatch = matches[docRef]) !== undefined) {\n docMatch.score += score\n docMatch.matchData.combine(matchingFields[fieldRef])\n } else {\n var match = {\n ref: docRef,\n score: score,\n matchData: matchingFields[fieldRef]\n }\n matches[docRef] = match\n results.push(match)\n }\n }\n\n /*\n * Sort the results objects by score, highest first.\n */\n return results.sort(function (a, b) {\n return b.score - a.score\n })\n}\n\n/**\n * Prepares the index for JSON serialization.\n *\n * The schema for this JSON blob will be described in a\n * separate JSON schema file.\n *\n * @returns {Object}\n */\nlunr.Index.prototype.toJSON = function () {\n var invertedIndex = Object.keys(this.invertedIndex)\n .sort()\n .map(function (term) {\n return [term, this.invertedIndex[term]]\n }, this)\n\n var fieldVectors = Object.keys(this.fieldVectors)\n .map(function (ref) {\n return [ref, this.fieldVectors[ref].toJSON()]\n }, this)\n\n return {\n version: lunr.version,\n fields: this.fields,\n fieldVectors: fieldVectors,\n invertedIndex: invertedIndex,\n pipeline: this.pipeline.toJSON()\n }\n}\n\n/**\n * Loads a previously serialized lunr.Index\n *\n * @param {Object} serializedIndex - A previously serialized lunr.Index\n * @returns {lunr.Index}\n */\nlunr.Index.load = function (serializedIndex) {\n var attrs = {},\n fieldVectors = {},\n serializedVectors = serializedIndex.fieldVectors,\n invertedIndex = Object.create(null),\n serializedInvertedIndex = serializedIndex.invertedIndex,\n tokenSetBuilder = new lunr.TokenSet.Builder,\n pipeline = lunr.Pipeline.load(serializedIndex.pipeline)\n\n if (serializedIndex.version != lunr.version) {\n lunr.utils.warn(\"Version mismatch when loading serialised index. Current version of lunr '\" + lunr.version + \"' does not match serialized index '\" + serializedIndex.version + \"'\")\n }\n\n for (var i = 0; i < serializedVectors.length; i++) {\n var tuple = serializedVectors[i],\n ref = tuple[0],\n elements = tuple[1]\n\n fieldVectors[ref] = new lunr.Vector(elements)\n }\n\n for (var i = 0; i < serializedInvertedIndex.length; i++) {\n var tuple = serializedInvertedIndex[i],\n term = tuple[0],\n posting = tuple[1]\n\n tokenSetBuilder.insert(term)\n invertedIndex[term] = posting\n }\n\n tokenSetBuilder.finish()\n\n attrs.fields = serializedIndex.fields\n\n attrs.fieldVectors = fieldVectors\n attrs.invertedIndex = invertedIndex\n attrs.tokenSet = tokenSetBuilder.root\n attrs.pipeline = pipeline\n\n return new lunr.Index(attrs)\n}\n/*!\n * lunr.Builder\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Builder performs indexing on a set of documents and\n * returns instances of lunr.Index ready for querying.\n *\n * All configuration of the index is done via the builder, the\n * fields to index, the document reference, the text processing\n * pipeline and document scoring parameters are all set on the\n * builder before indexing.\n *\n * @constructor\n * @property {string} _ref - Internal reference to the document reference field.\n * @property {string[]} _fields - Internal reference to the document fields to index.\n * @property {object} invertedIndex - The inverted index maps terms to document fields.\n * @property {object} documentTermFrequencies - Keeps track of document term frequencies.\n * @property {object} documentLengths - Keeps track of the length of documents added to the index.\n * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing.\n * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing.\n * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index.\n * @property {number} documentCount - Keeps track of the total number of documents indexed.\n * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75.\n * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2.\n * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space.\n * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index.\n */\nlunr.Builder = function () {\n this._ref = \"id\"\n this._fields = Object.create(null)\n this._documents = Object.create(null)\n this.invertedIndex = Object.create(null)\n this.fieldTermFrequencies = {}\n this.fieldLengths = {}\n this.tokenizer = lunr.tokenizer\n this.pipeline = new lunr.Pipeline\n this.searchPipeline = new lunr.Pipeline\n this.documentCount = 0\n this._b = 0.75\n this._k1 = 1.2\n this.termIndex = 0\n this.metadataWhitelist = []\n}\n\n/**\n * Sets the document field used as the document reference. Every document must have this field.\n * The type of this field in the document should be a string, if it is not a string it will be\n * coerced into a string by calling toString.\n *\n * The default ref is 'id'.\n *\n * The ref should _not_ be changed during indexing, it should be set before any documents are\n * added to the index. Changing it during indexing can lead to inconsistent results.\n *\n * @param {string} ref - The name of the reference field in the document.\n */\nlunr.Builder.prototype.ref = function (ref) {\n this._ref = ref\n}\n\n/**\n * A function that is used to extract a field from a document.\n *\n * Lunr expects a field to be at the top level of a document, if however the field\n * is deeply nested within a document an extractor function can be used to extract\n * the right field for indexing.\n *\n * @callback fieldExtractor\n * @param {object} doc - The document being added to the index.\n * @returns {?(string|object|object[])} obj - The object that will be indexed for this field.\n * @example Extracting a nested field\n * function (doc) { return doc.nested.field }\n */\n\n/**\n * Adds a field to the list of document fields that will be indexed. Every document being\n * indexed should have this field. Null values for this field in indexed documents will\n * not cause errors but will limit the chance of that document being retrieved by searches.\n *\n * All fields should be added before adding documents to the index. Adding fields after\n * a document has been indexed will have no effect on already indexed documents.\n *\n * Fields can be boosted at build time. This allows terms within that field to have more\n * importance when ranking search results. Use a field boost to specify that matches within\n * one field are more important than other fields.\n *\n * @param {string} fieldName - The name of a field to index in all documents.\n * @param {object} attributes - Optional attributes associated with this field.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this field.\n * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document.\n * @throws {RangeError} fieldName cannot contain unsupported characters '/'\n */\nlunr.Builder.prototype.field = function (fieldName, attributes) {\n if (/\\//.test(fieldName)) {\n throw new RangeError (\"Field '\" + fieldName + \"' contains illegal character '/'\")\n }\n\n this._fields[fieldName] = attributes || {}\n}\n\n/**\n * A parameter to tune the amount of field length normalisation that is applied when\n * calculating relevance scores. A value of 0 will completely disable any normalisation\n * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b\n * will be clamped to the range 0 - 1.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.b = function (number) {\n if (number < 0) {\n this._b = 0\n } else if (number > 1) {\n this._b = 1\n } else {\n this._b = number\n }\n}\n\n/**\n * A parameter that controls the speed at which a rise in term frequency results in term\n * frequency saturation. The default value is 1.2. Setting this to a higher value will give\n * slower saturation levels, a lower value will result in quicker saturation.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.k1 = function (number) {\n this._k1 = number\n}\n\n/**\n * Adds a document to the index.\n *\n * Before adding fields to the index the index should have been fully setup, with the document\n * ref and all fields to index already having been specified.\n *\n * The document must have a field name as specified by the ref (by default this is 'id') and\n * it should have all fields defined for indexing, though null or undefined values will not\n * cause errors.\n *\n * Entire documents can be boosted at build time. Applying a boost to a document indicates that\n * this document should rank higher in search results than other documents.\n *\n * @param {object} doc - The document to add to the index.\n * @param {object} attributes - Optional attributes associated with this document.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this document.\n */\nlunr.Builder.prototype.add = function (doc, attributes) {\n var docRef = doc[this._ref],\n fields = Object.keys(this._fields)\n\n this._documents[docRef] = attributes || {}\n this.documentCount += 1\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i],\n extractor = this._fields[fieldName].extractor,\n field = extractor ? extractor(doc) : doc[fieldName],\n tokens = this.tokenizer(field, {\n fields: [fieldName]\n }),\n terms = this.pipeline.run(tokens),\n fieldRef = new lunr.FieldRef (docRef, fieldName),\n fieldTerms = Object.create(null)\n\n this.fieldTermFrequencies[fieldRef] = fieldTerms\n this.fieldLengths[fieldRef] = 0\n\n // store the length of this field for this document\n this.fieldLengths[fieldRef] += terms.length\n\n // calculate term frequencies for this field\n for (var j = 0; j < terms.length; j++) {\n var term = terms[j]\n\n if (fieldTerms[term] == undefined) {\n fieldTerms[term] = 0\n }\n\n fieldTerms[term] += 1\n\n // add to inverted index\n // create an initial posting if one doesn't exist\n if (this.invertedIndex[term] == undefined) {\n var posting = Object.create(null)\n posting[\"_index\"] = this.termIndex\n this.termIndex += 1\n\n for (var k = 0; k < fields.length; k++) {\n posting[fields[k]] = Object.create(null)\n }\n\n this.invertedIndex[term] = posting\n }\n\n // add an entry for this term/fieldName/docRef to the invertedIndex\n if (this.invertedIndex[term][fieldName][docRef] == undefined) {\n this.invertedIndex[term][fieldName][docRef] = Object.create(null)\n }\n\n // store all whitelisted metadata about this token in the\n // inverted index\n for (var l = 0; l < this.metadataWhitelist.length; l++) {\n var metadataKey = this.metadataWhitelist[l],\n metadata = term.metadata[metadataKey]\n\n if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) {\n this.invertedIndex[term][fieldName][docRef][metadataKey] = []\n }\n\n this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata)\n }\n }\n\n }\n}\n\n/**\n * Calculates the average document length for this index\n *\n * @private\n */\nlunr.Builder.prototype.calculateAverageFieldLengths = function () {\n\n var fieldRefs = Object.keys(this.fieldLengths),\n numberOfFields = fieldRefs.length,\n accumulator = {},\n documentsWithField = {}\n\n for (var i = 0; i < numberOfFields; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n field = fieldRef.fieldName\n\n documentsWithField[field] || (documentsWithField[field] = 0)\n documentsWithField[field] += 1\n\n accumulator[field] || (accumulator[field] = 0)\n accumulator[field] += this.fieldLengths[fieldRef]\n }\n\n var fields = Object.keys(this._fields)\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i]\n accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName]\n }\n\n this.averageFieldLength = accumulator\n}\n\n/**\n * Builds a vector space model of every document using lunr.Vector\n *\n * @private\n */\nlunr.Builder.prototype.createFieldVectors = function () {\n var fieldVectors = {},\n fieldRefs = Object.keys(this.fieldTermFrequencies),\n fieldRefsLength = fieldRefs.length,\n termIdfCache = Object.create(null)\n\n for (var i = 0; i < fieldRefsLength; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n fieldName = fieldRef.fieldName,\n fieldLength = this.fieldLengths[fieldRef],\n fieldVector = new lunr.Vector,\n termFrequencies = this.fieldTermFrequencies[fieldRef],\n terms = Object.keys(termFrequencies),\n termsLength = terms.length\n\n\n var fieldBoost = this._fields[fieldName].boost || 1,\n docBoost = this._documents[fieldRef.docRef].boost || 1\n\n for (var j = 0; j < termsLength; j++) {\n var term = terms[j],\n tf = termFrequencies[term],\n termIndex = this.invertedIndex[term]._index,\n idf, score, scoreWithPrecision\n\n if (termIdfCache[term] === undefined) {\n idf = lunr.idf(this.invertedIndex[term], this.documentCount)\n termIdfCache[term] = idf\n } else {\n idf = termIdfCache[term]\n }\n\n score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf)\n score *= fieldBoost\n score *= docBoost\n scoreWithPrecision = Math.round(score * 1000) / 1000\n // Converts 1.23456789 to 1.234.\n // Reducing the precision so that the vectors take up less\n // space when serialised. Doing it now so that they behave\n // the same before and after serialisation. Also, this is\n // the fastest approach to reducing a number's precision in\n // JavaScript.\n\n fieldVector.insert(termIndex, scoreWithPrecision)\n }\n\n fieldVectors[fieldRef] = fieldVector\n }\n\n this.fieldVectors = fieldVectors\n}\n\n/**\n * Creates a token set of all tokens in the index using lunr.TokenSet\n *\n * @private\n */\nlunr.Builder.prototype.createTokenSet = function () {\n this.tokenSet = lunr.TokenSet.fromArray(\n Object.keys(this.invertedIndex).sort()\n )\n}\n\n/**\n * Builds the index, creating an instance of lunr.Index.\n *\n * This completes the indexing process and should only be called\n * once all documents have been added to the index.\n *\n * @returns {lunr.Index}\n */\nlunr.Builder.prototype.build = function () {\n this.calculateAverageFieldLengths()\n this.createFieldVectors()\n this.createTokenSet()\n\n return new lunr.Index({\n invertedIndex: this.invertedIndex,\n fieldVectors: this.fieldVectors,\n tokenSet: this.tokenSet,\n fields: Object.keys(this._fields),\n pipeline: this.searchPipeline\n })\n}\n\n/**\n * Applies a plugin to the index builder.\n *\n * A plugin is a function that is called with the index builder as its context.\n * Plugins can be used to customise or extend the behaviour of the index\n * in some way. A plugin is just a function, that encapsulated the custom\n * behaviour that should be applied when building the index.\n *\n * The plugin function will be called with the index builder as its argument, additional\n * arguments can also be passed when calling use. The function will be called\n * with the index builder as its context.\n *\n * @param {Function} plugin The plugin to apply.\n */\nlunr.Builder.prototype.use = function (fn) {\n var args = Array.prototype.slice.call(arguments, 1)\n args.unshift(this)\n fn.apply(this, args)\n}\n/**\n * Contains and collects metadata about a matching document.\n * A single instance of lunr.MatchData is returned as part of every\n * lunr.Index~Result.\n *\n * @constructor\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n * @property {object} metadata - A cloned collection of metadata associated with this document.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData = function (term, field, metadata) {\n var clonedMetadata = Object.create(null),\n metadataKeys = Object.keys(metadata || {})\n\n // Cloning the metadata to prevent the original\n // being mutated during match data combination.\n // Metadata is kept in an array within the inverted\n // index so cloning the data can be done with\n // Array#slice\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n clonedMetadata[key] = metadata[key].slice()\n }\n\n this.metadata = Object.create(null)\n\n if (term !== undefined) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = clonedMetadata\n }\n}\n\n/**\n * An instance of lunr.MatchData will be created for every term that matches a\n * document. However only one instance is required in a lunr.Index~Result. This\n * method combines metadata from another instance of lunr.MatchData with this\n * objects metadata.\n *\n * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData.prototype.combine = function (otherMatchData) {\n var terms = Object.keys(otherMatchData.metadata)\n\n for (var i = 0; i < terms.length; i++) {\n var term = terms[i],\n fields = Object.keys(otherMatchData.metadata[term])\n\n if (this.metadata[term] == undefined) {\n this.metadata[term] = Object.create(null)\n }\n\n for (var j = 0; j < fields.length; j++) {\n var field = fields[j],\n keys = Object.keys(otherMatchData.metadata[term][field])\n\n if (this.metadata[term][field] == undefined) {\n this.metadata[term][field] = Object.create(null)\n }\n\n for (var k = 0; k < keys.length; k++) {\n var key = keys[k]\n\n if (this.metadata[term][field][key] == undefined) {\n this.metadata[term][field][key] = otherMatchData.metadata[term][field][key]\n } else {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key])\n }\n\n }\n }\n }\n}\n\n/**\n * Add metadata for a term/field pair to this instance of match data.\n *\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n */\nlunr.MatchData.prototype.add = function (term, field, metadata) {\n if (!(term in this.metadata)) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = metadata\n return\n }\n\n if (!(field in this.metadata[term])) {\n this.metadata[term][field] = metadata\n return\n }\n\n var metadataKeys = Object.keys(metadata)\n\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n\n if (key in this.metadata[term][field]) {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key])\n } else {\n this.metadata[term][field][key] = metadata[key]\n }\n }\n}\n/**\n * A lunr.Query provides a programmatic way of defining queries to be performed\n * against a {@link lunr.Index}.\n *\n * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method\n * so the query object is pre-initialized with the right index fields.\n *\n * @constructor\n * @property {lunr.Query~Clause[]} clauses - An array of query clauses.\n * @property {string[]} allFields - An array of all available fields in a lunr.Index.\n */\nlunr.Query = function (allFields) {\n this.clauses = []\n this.allFields = allFields\n}\n\n/**\n * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.\n *\n * This allows wildcards to be added to the beginning and end of a term without having to manually do any string\n * concatenation.\n *\n * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.\n *\n * @constant\n * @default\n * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour\n * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists\n * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example query term with trailing wildcard\n * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING })\n * @example query term with leading and trailing wildcard\n * query.term('foo', {\n * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING\n * })\n */\n\nlunr.Query.wildcard = new String (\"*\")\nlunr.Query.wildcard.NONE = 0\nlunr.Query.wildcard.LEADING = 1\nlunr.Query.wildcard.TRAILING = 2\n\n/**\n * Constants for indicating what kind of presence a term must have in matching documents.\n *\n * @constant\n * @enum {number}\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example query term with required presence\n * query.term('foo', { presence: lunr.Query.presence.REQUIRED })\n */\nlunr.Query.presence = {\n /**\n * Term's presence in a document is optional, this is the default value.\n */\n OPTIONAL: 1,\n\n /**\n * Term's presence in a document is required, documents that do not contain\n * this term will not be returned.\n */\n REQUIRED: 2,\n\n /**\n * Term's presence in a document is prohibited, documents that do contain\n * this term will not be returned.\n */\n PROHIBITED: 3\n}\n\n/**\n * A single clause in a {@link lunr.Query} contains a term and details on how to\n * match that term against a {@link lunr.Index}.\n *\n * @typedef {Object} lunr.Query~Clause\n * @property {string[]} fields - The fields in an index this clause should be matched against.\n * @property {number} [boost=1] - Any boost that should be applied when matching this clause.\n * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.\n * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.\n * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.\n * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.\n */\n\n/**\n * Adds a {@link lunr.Query~Clause} to this query.\n *\n * Unless the clause contains the fields to be matched all fields will be matched. In addition\n * a default boost of 1 is applied to the clause.\n *\n * @param {lunr.Query~Clause} clause - The clause to add to this query.\n * @see lunr.Query~Clause\n * @returns {lunr.Query}\n */\nlunr.Query.prototype.clause = function (clause) {\n if (!('fields' in clause)) {\n clause.fields = this.allFields\n }\n\n if (!('boost' in clause)) {\n clause.boost = 1\n }\n\n if (!('usePipeline' in clause)) {\n clause.usePipeline = true\n }\n\n if (!('wildcard' in clause)) {\n clause.wildcard = lunr.Query.wildcard.NONE\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {\n clause.term = \"*\" + clause.term\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {\n clause.term = \"\" + clause.term + \"*\"\n }\n\n if (!('presence' in clause)) {\n clause.presence = lunr.Query.presence.OPTIONAL\n }\n\n this.clauses.push(clause)\n\n return this\n}\n\n/**\n * A negated query is one in which every clause has a presence of\n * prohibited. These queries require some special processing to return\n * the expected results.\n *\n * @returns boolean\n */\nlunr.Query.prototype.isNegated = function () {\n for (var i = 0; i < this.clauses.length; i++) {\n if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}\n * to the list of clauses that make up this query.\n *\n * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion\n * to a token or token-like string should be done before calling this method.\n *\n * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an\n * array, each term in the array will share the same options.\n *\n * @param {object|object[]} term - The term(s) to add to the query.\n * @param {object} [options] - Any additional properties to add to the query clause.\n * @returns {lunr.Query}\n * @see lunr.Query#clause\n * @see lunr.Query~Clause\n * @example adding a single term to a query\n * query.term(\"foo\")\n * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard\n * query.term(\"foo\", {\n * fields: [\"title\"],\n * boost: 10,\n * wildcard: lunr.Query.wildcard.TRAILING\n * })\n * @example using lunr.tokenizer to convert a string to tokens before using them as terms\n * query.term(lunr.tokenizer(\"foo bar\"))\n */\nlunr.Query.prototype.term = function (term, options) {\n if (Array.isArray(term)) {\n term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)\n return this\n }\n\n var clause = options || {}\n clause.term = term.toString()\n\n this.clause(clause)\n\n return this\n}\nlunr.QueryParseError = function (message, start, end) {\n this.name = \"QueryParseError\"\n this.message = message\n this.start = start\n this.end = end\n}\n\nlunr.QueryParseError.prototype = new Error\nlunr.QueryLexer = function (str) {\n this.lexemes = []\n this.str = str\n this.length = str.length\n this.pos = 0\n this.start = 0\n this.escapeCharPositions = []\n}\n\nlunr.QueryLexer.prototype.run = function () {\n var state = lunr.QueryLexer.lexText\n\n while (state) {\n state = state(this)\n }\n}\n\nlunr.QueryLexer.prototype.sliceString = function () {\n var subSlices = [],\n sliceStart = this.start,\n sliceEnd = this.pos\n\n for (var i = 0; i < this.escapeCharPositions.length; i++) {\n sliceEnd = this.escapeCharPositions[i]\n subSlices.push(this.str.slice(sliceStart, sliceEnd))\n sliceStart = sliceEnd + 1\n }\n\n subSlices.push(this.str.slice(sliceStart, this.pos))\n this.escapeCharPositions.length = 0\n\n return subSlices.join('')\n}\n\nlunr.QueryLexer.prototype.emit = function (type) {\n this.lexemes.push({\n type: type,\n str: this.sliceString(),\n start: this.start,\n end: this.pos\n })\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.escapeCharacter = function () {\n this.escapeCharPositions.push(this.pos - 1)\n this.pos += 1\n}\n\nlunr.QueryLexer.prototype.next = function () {\n if (this.pos >= this.length) {\n return lunr.QueryLexer.EOS\n }\n\n var char = this.str.charAt(this.pos)\n this.pos += 1\n return char\n}\n\nlunr.QueryLexer.prototype.width = function () {\n return this.pos - this.start\n}\n\nlunr.QueryLexer.prototype.ignore = function () {\n if (this.start == this.pos) {\n this.pos += 1\n }\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.backup = function () {\n this.pos -= 1\n}\n\nlunr.QueryLexer.prototype.acceptDigitRun = function () {\n var char, charCode\n\n do {\n char = this.next()\n charCode = char.charCodeAt(0)\n } while (charCode > 47 && charCode < 58)\n\n if (char != lunr.QueryLexer.EOS) {\n this.backup()\n }\n}\n\nlunr.QueryLexer.prototype.more = function () {\n return this.pos < this.length\n}\n\nlunr.QueryLexer.EOS = 'EOS'\nlunr.QueryLexer.FIELD = 'FIELD'\nlunr.QueryLexer.TERM = 'TERM'\nlunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'\nlunr.QueryLexer.BOOST = 'BOOST'\nlunr.QueryLexer.PRESENCE = 'PRESENCE'\n\nlunr.QueryLexer.lexField = function (lexer) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.FIELD)\n lexer.ignore()\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexTerm = function (lexer) {\n if (lexer.width() > 1) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.TERM)\n }\n\n lexer.ignore()\n\n if (lexer.more()) {\n return lunr.QueryLexer.lexText\n }\n}\n\nlunr.QueryLexer.lexEditDistance = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.EDIT_DISTANCE)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexBoost = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.BOOST)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexEOS = function (lexer) {\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n}\n\n// This matches the separator used when tokenising fields\n// within a document. These should match otherwise it is\n// not possible to search for some tokens within a document.\n//\n// It is possible for the user to change the separator on the\n// tokenizer so it _might_ clash with any other of the special\n// characters already used within the search string, e.g. :.\n//\n// This means that it is possible to change the separator in\n// such a way that makes some words unsearchable using a search\n// string.\nlunr.QueryLexer.termSeparator = lunr.tokenizer.separator\n\nlunr.QueryLexer.lexText = function (lexer) {\n while (true) {\n var char = lexer.next()\n\n if (char == lunr.QueryLexer.EOS) {\n return lunr.QueryLexer.lexEOS\n }\n\n // Escape character is '\\'\n if (char.charCodeAt(0) == 92) {\n lexer.escapeCharacter()\n continue\n }\n\n if (char == \":\") {\n return lunr.QueryLexer.lexField\n }\n\n if (char == \"~\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexEditDistance\n }\n\n if (char == \"^\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexBoost\n }\n\n // \"+\" indicates term presence is required\n // checking for length to ensure that only\n // leading \"+\" are considered\n if (char == \"+\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n // \"-\" indicates term presence is prohibited\n // checking for length to ensure that only\n // leading \"-\" are considered\n if (char == \"-\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n if (char.match(lunr.QueryLexer.termSeparator)) {\n return lunr.QueryLexer.lexTerm\n }\n }\n}\n\nlunr.QueryParser = function (str, query) {\n this.lexer = new lunr.QueryLexer (str)\n this.query = query\n this.currentClause = {}\n this.lexemeIdx = 0\n}\n\nlunr.QueryParser.prototype.parse = function () {\n this.lexer.run()\n this.lexemes = this.lexer.lexemes\n\n var state = lunr.QueryParser.parseClause\n\n while (state) {\n state = state(this)\n }\n\n return this.query\n}\n\nlunr.QueryParser.prototype.peekLexeme = function () {\n return this.lexemes[this.lexemeIdx]\n}\n\nlunr.QueryParser.prototype.consumeLexeme = function () {\n var lexeme = this.peekLexeme()\n this.lexemeIdx += 1\n return lexeme\n}\n\nlunr.QueryParser.prototype.nextClause = function () {\n var completedClause = this.currentClause\n this.query.clause(completedClause)\n this.currentClause = {}\n}\n\nlunr.QueryParser.parseClause = function (parser) {\n var lexeme = parser.peekLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.type) {\n case lunr.QueryLexer.PRESENCE:\n return lunr.QueryParser.parsePresence\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expected either a field or a term, found \" + lexeme.type\n\n if (lexeme.str.length >= 1) {\n errorMessage += \" with value '\" + lexeme.str + \"'\"\n }\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n}\n\nlunr.QueryParser.parsePresence = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.str) {\n case \"-\":\n parser.currentClause.presence = lunr.Query.presence.PROHIBITED\n break\n case \"+\":\n parser.currentClause.presence = lunr.Query.presence.REQUIRED\n break\n default:\n var errorMessage = \"unrecognised presence operator'\" + lexeme.str + \"'\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term or field, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term or field, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseField = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n if (parser.query.allFields.indexOf(lexeme.str) == -1) {\n var possibleFields = parser.query.allFields.map(function (f) { return \"'\" + f + \"'\" }).join(', '),\n errorMessage = \"unrecognised field '\" + lexeme.str + \"', possible fields: \" + possibleFields\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.fields = [lexeme.str]\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseTerm = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n parser.currentClause.term = lexeme.str.toLowerCase()\n\n if (lexeme.str.indexOf(\"*\") != -1) {\n parser.currentClause.usePipeline = false\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseEditDistance = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var editDistance = parseInt(lexeme.str, 10)\n\n if (isNaN(editDistance)) {\n var errorMessage = \"edit distance must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.editDistance = editDistance\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseBoost = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var boost = parseInt(lexeme.str, 10)\n\n if (isNaN(boost)) {\n var errorMessage = \"boost must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.boost = boost\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\n /**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js\n */\n ;(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(factory)\n } else if (typeof exports === 'object') {\n /**\n * Node. Does not work with strict CommonJS, but\n * only CommonJS-like enviroments that support module.exports,\n * like Node.\n */\n module.exports = factory()\n } else {\n // Browser globals (root is window)\n root.lunr = factory()\n }\n }(this, function () {\n /**\n * Just return a value to define the module export.\n * This example returns an object, but the module\n * can return a function as the exported value.\n */\n return lunr\n }))\n})();\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport lunr from \"lunr\"\n\nimport { Search, SearchIndexConfig } from \"../../_\"\nimport {\n SearchMessage,\n SearchMessageType\n} from \"../message\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Add support for usage with `iframe-worker` polyfill\n *\n * While `importScripts` is synchronous when executed inside of a web worker,\n * it's not possible to provide a synchronous polyfilled implementation. The\n * cool thing is that awaiting a non-Promise is a noop, so extending the type\n * definition to return a `Promise` shouldn't break anything.\n *\n * @see https://bit.ly/2PjDnXi - GitHub comment\n */\ndeclare global {\n function importScripts(...urls: string[]): Promise | void\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index\n */\nlet index: Search\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch (= import) multi-language support through `lunr-languages`\n *\n * This function automatically imports the stemmers necessary to process the\n * languages, which are defined through the search index configuration.\n *\n * If the worker runs inside of an `iframe` (when using `iframe-worker` as\n * a shim), the base URL for the stemmers to be loaded must be determined by\n * searching for the first `script` element with a `src` attribute, which will\n * contain the contents of this script.\n *\n * @param config - Search index configuration\n *\n * @returns Promise resolving with no result\n */\nasync function setupSearchLanguages(\n config: SearchIndexConfig\n): Promise {\n let base = \"../lunr\"\n\n /* Detect `iframe-worker` and fix base URL */\n if (typeof parent !== \"undefined\" && \"IFrameWorker\" in parent) {\n const worker = document.querySelector(\"script[src]\")!\n const [path] = worker.src.split(\"/worker\")\n\n /* Prefix base with path */\n base = base.replace(\"..\", path)\n }\n\n /* Add scripts for languages */\n const scripts = []\n for (const lang of config.lang) {\n switch (lang) {\n\n /* Add segmenter for Japanese */\n case \"ja\":\n scripts.push(`${base}/tinyseg.js`)\n break\n\n /* Add segmenter for Hindi and Thai */\n case \"hi\":\n case \"th\":\n scripts.push(`${base}/wordcut.js`)\n break\n }\n\n /* Add language support */\n if (lang !== \"en\")\n scripts.push(`${base}/min/lunr.${lang}.min.js`)\n }\n\n /* Add multi-language support */\n if (config.lang.length > 1)\n scripts.push(`${base}/min/lunr.multi.min.js`)\n\n /* Load scripts synchronously */\n if (scripts.length)\n await importScripts(\n `${base}/min/lunr.stemmer.support.min.js`,\n ...scripts\n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Message handler\n *\n * @param message - Source message\n *\n * @returns Target message\n */\nexport async function handler(\n message: SearchMessage\n): Promise {\n switch (message.type) {\n\n /* Search setup message */\n case SearchMessageType.SETUP:\n await setupSearchLanguages(message.data.config)\n index = new Search(message.data)\n return {\n type: SearchMessageType.READY\n }\n\n /* Search query message */\n case SearchMessageType.QUERY:\n return {\n type: SearchMessageType.RESULT,\n data: index ? index.search(message.data) : { items: [] }\n }\n\n /* All other messages */\n default:\n throw new TypeError(\"Invalid message type\")\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Worker\n * ------------------------------------------------------------------------- */\n\n/* @ts-ignore - expose Lunr.js in global scope, or stemmers will not work */\nself.lunr = lunr\n\n/* Handle messages */\naddEventListener(\"message\", async ev => {\n postMessage(await handler(ev.data))\n})\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexDocument } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search document\n */\nexport interface SearchDocument extends SearchIndexDocument {\n parent?: SearchIndexDocument /* Parent article */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search document mapping\n */\nexport type SearchDocumentMap = Map\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search document mapping\n *\n * @param docs - Search index documents\n *\n * @returns Search document map\n */\nexport function setupSearchDocumentMap(\n docs: SearchIndexDocument[]\n): SearchDocumentMap {\n const documents = new Map()\n const parents = new Set()\n for (const doc of docs) {\n const [path, hash] = doc.location.split(\"#\")\n\n /* Extract location and title */\n const location = doc.location\n const title = doc.title\n\n /* Escape and cleanup text */\n const text = escapeHTML(doc.text)\n .replace(/\\s+(?=[,.:;!?])/g, \"\")\n .replace(/\\s+/g, \" \")\n\n /* Handle section */\n if (hash) {\n const parent = documents.get(path)!\n\n /* Ignore first section, override article */\n if (!parents.has(parent)) {\n parent.title = doc.title\n parent.text = text\n\n /* Remember that we processed the article */\n parents.add(parent)\n\n /* Add subsequent section */\n } else {\n documents.set(location, {\n location,\n title,\n text,\n parent\n })\n }\n\n /* Add article */\n } else {\n documents.set(location, {\n location,\n title,\n text\n })\n }\n }\n return documents\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport escapeHTML from \"escape-html\"\n\nimport { SearchIndexConfig } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlight function\n *\n * @param value - Value\n *\n * @returns Highlighted value\n */\nexport type SearchHighlightFn = (value: string) => string\n\n/**\n * Search highlight factory function\n *\n * @param query - Query value\n *\n * @returns Search highlight function\n */\nexport type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search highlighter\n *\n * @param config - Search index configuration\n * @param escape - Whether to escape HTML\n *\n * @returns Search highlight factory function\n */\nexport function setupSearchHighlighter(\n config: SearchIndexConfig, escape: boolean\n): SearchHighlightFactoryFn {\n const separator = new RegExp(config.separator, \"img\")\n const highlight = (_: unknown, data: string, term: string) => {\n return `${data}${term}`\n }\n\n /* Return factory function */\n return (query: string) => {\n query = query\n .replace(/[\\s*+\\-:~^]+/g, \" \")\n .trim()\n\n /* Create search term match expression */\n const match = new RegExp(`(^|${config.separator})(${\n query\n .replace(/[|\\\\{}()[\\]^$+*?.-]/g, \"\\\\$&\")\n .replace(separator, \"|\")\n })`, \"img\")\n\n /* Highlight string value */\n return value => (\n escape\n ? escapeHTML(value)\n : value\n )\n .replace(match, highlight)\n .replace(/<\\/mark>(\\s+)]*>/img, \"$1\")\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search query clause\n */\nexport interface SearchQueryClause {\n presence: lunr.Query.presence /* Clause presence */\n term: string /* Clause term */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search query terms\n */\nexport type SearchQueryTerms = Record\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Parse a search query for analysis\n *\n * @param value - Query value\n *\n * @returns Search query clauses\n */\nexport function parseSearchQuery(\n value: string\n): SearchQueryClause[] {\n const query = new (lunr as any).Query([\"title\", \"text\"])\n const parser = new (lunr as any).QueryParser(value, query)\n\n /* Parse and return query clauses */\n parser.parse()\n return query.clauses\n}\n\n/**\n * Analyze the search query clauses in regard to the search terms found\n *\n * @param query - Search query clauses\n * @param terms - Search terms\n *\n * @returns Search query terms\n */\nexport function getSearchQueryTerms(\n query: SearchQueryClause[], terms: string[]\n): SearchQueryTerms {\n const clauses = new Set(query)\n\n /* Match query clauses against terms */\n const result: SearchQueryTerms = {}\n for (let t = 0; t < terms.length; t++)\n for (const clause of clauses)\n if (terms[t].startsWith(clause.term)) {\n result[clause.term] = true\n clauses.delete(clause)\n }\n\n /* Annotate unmatched query clauses */\n for (const clause of clauses)\n result[clause.term] = false\n\n /* Return query terms */\n return result\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n SearchDocument,\n SearchDocumentMap,\n setupSearchDocumentMap\n} from \"../document\"\nimport {\n SearchHighlightFactoryFn,\n setupSearchHighlighter\n} from \"../highlighter\"\nimport { SearchOptions } from \"../options\"\nimport {\n SearchQueryTerms,\n getSearchQueryTerms,\n parseSearchQuery\n} from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index configuration\n */\nexport interface SearchIndexConfig {\n lang: string[] /* Search languages */\n separator: string /* Search separator */\n}\n\n/**\n * Search index document\n */\nexport interface SearchIndexDocument {\n location: string /* Document location */\n title: string /* Document title */\n text: string /* Document text */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search index\n *\n * This interfaces describes the format of the `search_index.json` file which\n * is automatically built by the MkDocs search plugin.\n */\nexport interface SearchIndex {\n config: SearchIndexConfig /* Search index configuration */\n docs: SearchIndexDocument[] /* Search index documents */\n index?: object /* Prebuilt index */\n options: SearchOptions /* Search options */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search metadata\n */\nexport interface SearchMetadata {\n score: number /* Score (relevance) */\n terms: SearchQueryTerms /* Search query terms */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search result document\n */\nexport type SearchResultDocument = SearchDocument & SearchMetadata\n\n/**\n * Search result item\n */\nexport type SearchResultItem = SearchResultDocument[]\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search result\n */\nexport interface SearchResult {\n items: SearchResultItem[] /* Search result items */\n suggestions?: string[] /* Search suggestions */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Compute the difference of two lists of strings\n *\n * @param a - 1st list of strings\n * @param b - 2nd list of strings\n *\n * @returns Difference\n */\nfunction difference(a: string[], b: string[]): string[] {\n const [x, y] = [new Set(a), new Set(b)]\n return [\n ...new Set([...x].filter(value => !y.has(value)))\n ]\n}\n\n/* ----------------------------------------------------------------------------\n * Class\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index\n */\nexport class Search {\n\n /**\n * Search document mapping\n *\n * A mapping of URLs (including hash fragments) to the actual articles and\n * sections of the documentation. The search document mapping must be created\n * regardless of whether the index was prebuilt or not, as Lunr.js itself\n * only stores the actual index.\n */\n protected documents: SearchDocumentMap\n\n /**\n * Search highlight factory function\n */\n protected highlight: SearchHighlightFactoryFn\n\n /**\n * The underlying Lunr.js search index\n */\n protected index: lunr.Index\n\n /**\n * Search options\n */\n protected options: SearchOptions\n\n /**\n * Create the search integration\n *\n * @param data - Search index\n */\n public constructor({ config, docs, index, options }: SearchIndex) {\n this.options = options\n\n /* Set up document map and highlighter factory */\n this.documents = setupSearchDocumentMap(docs)\n this.highlight = setupSearchHighlighter(config, false)\n\n /* Set separator for tokenizer */\n lunr.tokenizer.separator = new RegExp(config.separator)\n\n /* If no index was given, create it */\n if (typeof index === \"undefined\") {\n this.index = lunr(function () {\n\n /* Set up multi-language support */\n if (config.lang.length === 1 && config.lang[0] !== \"en\") {\n this.use((lunr as any)[config.lang[0]])\n } else if (config.lang.length > 1) {\n this.use((lunr as any).multiLanguage(...config.lang))\n }\n\n /* Compute functions to be removed from the pipeline */\n const fns = difference([\n \"trimmer\", \"stopWordFilter\", \"stemmer\"\n ], options.pipeline)\n\n /* Remove functions from the pipeline for registered languages */\n for (const lang of config.lang.map(language => (\n language === \"en\" ? lunr : (lunr as any)[language]\n ))) {\n for (const fn of fns) {\n this.pipeline.remove(lang[fn])\n this.searchPipeline.remove(lang[fn])\n }\n }\n\n /* Set up reference */\n this.ref(\"location\")\n\n /* Set up fields */\n this.field(\"title\", { boost: 1e3 })\n this.field(\"text\")\n\n /* Index documents */\n for (const doc of docs)\n this.add(doc)\n })\n\n /* Handle prebuilt index */\n } else {\n this.index = lunr.Index.load(index)\n }\n }\n\n /**\n * Search for matching documents\n *\n * The search index which MkDocs provides is divided up into articles, which\n * contain the whole content of the individual pages, and sections, which only\n * contain the contents of the subsections obtained by breaking the individual\n * pages up at `h1` ... `h6`. As there may be many sections on different pages\n * with identical titles (for example within this very project, e.g. \"Usage\"\n * or \"Installation\"), they need to be put into the context of the containing\n * page. For this reason, section results are grouped within their respective\n * articles which are the top-level results that are returned.\n *\n * @param query - Query value\n *\n * @returns Search results\n */\n public search(query: string): SearchResult {\n if (query) {\n try {\n const highlight = this.highlight(query)\n\n /* Parse query to extract clauses for analysis */\n const clauses = parseSearchQuery(query)\n .filter(clause => (\n clause.presence !== lunr.Query.presence.PROHIBITED\n ))\n\n /* Perform search and post-process results */\n const groups = this.index.search(`${query}*`)\n\n /* Apply post-query boosts based on title and search query terms */\n .reduce((item, { ref, score, matchData }) => {\n const document = this.documents.get(ref)\n if (typeof document !== \"undefined\") {\n const { location, title, text, parent } = document\n\n /* Compute and analyze search query terms */\n const terms = getSearchQueryTerms(\n clauses,\n Object.keys(matchData.metadata)\n )\n\n /* Highlight title and text and apply post-query boosts */\n const boost = +!parent + +Object.values(terms).every(t => t)\n item.push({\n location,\n title: highlight(title),\n text: highlight(text),\n score: score * (1 + boost),\n terms\n })\n }\n return item\n }, [])\n\n /* Sort search results again after applying boosts */\n .sort((a, b) => b.score - a.score)\n\n /* Group search results by page */\n .reduce((items, result) => {\n const document = this.documents.get(result.location)\n if (typeof document !== \"undefined\") {\n const ref = \"parent\" in document\n ? document.parent!.location\n : document.location\n items.set(ref, [...items.get(ref) || [], result])\n }\n return items\n }, new Map())\n\n /* Generate search suggestions, if desired */\n let suggestions: string[] | undefined\n if (this.options.suggestions) {\n const titles = this.index.query(builder => {\n for (const clause of clauses)\n builder.term(clause.term, {\n fields: [\"title\"],\n presence: lunr.Query.presence.REQUIRED,\n wildcard: lunr.Query.wildcard.TRAILING\n })\n })\n\n /* Retrieve suggestions for best match */\n suggestions = titles.length\n ? Object.keys(titles[0].matchData.metadata)\n : []\n }\n\n /* Return items and suggestions */\n return {\n items: [...groups.values()],\n ...typeof suggestions !== \"undefined\" && { suggestions }\n }\n\n /* Log errors to console (for now) */\n } catch {\n console.warn(`Invalid query: ${query} \u2013 see https://bit.ly/2s3ChXG`)\n }\n }\n\n /* Return nothing in case of error or empty query */\n return { items: [] }\n }\n}\n", "/*\n * Copyright (c) 2016-2021 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchResult } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search message type\n */\nexport const enum SearchMessageType {\n SETUP, /* Search index setup */\n READY, /* Search index ready */\n QUERY, /* Search query */\n RESULT /* Search results */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message containing the data necessary to setup the search index\n */\nexport interface SearchSetupMessage {\n type: SearchMessageType.SETUP /* Message type */\n data: SearchIndex /* Message data */\n}\n\n/**\n * Message indicating the search index is ready\n */\nexport interface SearchReadyMessage {\n type: SearchMessageType.READY /* Message type */\n}\n\n/**\n * Message containing a search query\n */\nexport interface SearchQueryMessage {\n type: SearchMessageType.QUERY /* Message type */\n data: string /* Message data */\n}\n\n/**\n * Message containing results for a search query\n */\nexport interface SearchResultMessage {\n type: SearchMessageType.RESULT /* Message type */\n data: SearchResult /* Message data */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Message exchanged with the search worker\n */\nexport type SearchMessage =\n | SearchSetupMessage\n | SearchReadyMessage\n | SearchQueryMessage\n | SearchResultMessage\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Type guard for search setup messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchSetupMessage(\n message: SearchMessage\n): message is SearchSetupMessage {\n return message.type === SearchMessageType.SETUP\n}\n\n/**\n * Type guard for search ready messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchReadyMessage(\n message: SearchMessage\n): message is SearchReadyMessage {\n return message.type === SearchMessageType.READY\n}\n\n/**\n * Type guard for search query messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchQueryMessage(\n message: SearchMessage\n): message is SearchQueryMessage {\n return message.type === SearchMessageType.QUERY\n}\n\n/**\n * Type guard for search result messages\n *\n * @param message - Search worker message\n *\n * @returns Test result\n */\nexport function isSearchResultMessage(\n message: SearchMessage\n): message is SearchResultMessage {\n return message.type === SearchMessageType.RESULT\n}\n"], - "mappings": "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", + "mappings": "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", "names": [] } diff --git a/assets/stylesheets/main.1c75ef36.min.css b/assets/stylesheets/main.1c75ef36.min.css new file mode 100644 index 000000000..2e672fd97 --- /dev/null +++ b/assets/stylesheets/main.1c75ef36.min.css @@ -0,0 +1,2 @@ +@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:content-box;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:transparent;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-default-fg-color:rgba(0,0,0,0.87);--md-default-fg-color--light:rgba(0,0,0,0.54);--md-default-fg-color--lighter:rgba(0,0,0,0.32);--md-default-fg-color--lightest:rgba(0,0,0,0.07);--md-default-bg-color:#fff;--md-default-bg-color--light:hsla(0,0%,100%,0.7);--md-default-bg-color--lighter:hsla(0,0%,100%,0.3);--md-default-bg-color--lightest:hsla(0,0%,100%,0.12);--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:hsla(0,0%,100%,0.7);--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:rgba(82,108,254,0.1);--md-accent-bg-color:#fff;--md-accent-bg-color--light:hsla(0,0%,100%,0.7)}:root>*{--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:rgba(255,255,0,0.5);--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:rgba(255,255,0,0.5);--md-typeset-del-color:hsla(6,90%,60%,0.15);--md-typeset-ins-color:rgba(11,213,112,0.15);--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-table-color:rgba(0,0,0,0.12);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-fg-color:#fff;--md-footer-fg-color--light:hsla(0,0%,100%,0.7);--md-footer-fg-color--lighter:hsla(0,0%,100%,0.3);--md-footer-bg-color:rgba(0,0,0,0.87);--md-footer-bg-color--dark:rgba(0,0,0,0.32)}.md-icon svg{fill:currentColor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{font-feature-settings:"kern","liga";font-family:var(--md-text-font-family,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}body,code,input,kbd,pre{color:var(--md-typeset-color)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family,_),SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin:1em 0}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{background-color:transparent;box-shadow:none;margin:initial;padding:initial}.md-typeset a code{color:currentColor}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:44.9375em){.md-typeset>pre{margin:1em -.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}@media (hover:none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{background-color:var(--md-default-fg-color);border-radius:.1rem;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);color:var(--md-default-bg-color);content:attr(title);display:inline-block;font-size:.7rem;left:0;margin-top:2em;max-width:80%;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;padding:.2rem .3rem;position:absolute;width:auto}}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-left:0;margin-right:.078125em}.md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter);color:var(--md-default-fg-color--light);padding-left:.6rem}[dir=rtl] .md-typeset blockquote{border-left:initial;border-right:.2rem solid var(--md-default-fg-color--lighter);padding-left:0;padding-right:.6rem}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{display:flow-root;margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-left:0;margin-right:.625em}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-left:0;margin-right:1.25em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-left:0;margin-right:.625em}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-left:0;margin-right:1.875em}.md-typeset img,.md-typeset svg{height:auto;max-width:100%}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:0 auto;max-width:100%;text-align:center;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block}.md-typeset figcaption{font-style:italic;margin:1em auto 2em;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;margin-left:.5em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-state=lock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}.md-announce{background-color:var(--md-footer-bg-color);overflow:auto}@media print{.md-announce{display:none}}.md-announce__inner{color:var(--md-footer-fg-color);font-size:.7rem;margin:.6rem auto;padding:0 .8rem}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentColor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-content{flex-grow:1;overflow:hidden;scroll-padding-top:51.2rem}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){.md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:.8rem;margin-right:1.2rem}.md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem;margin-right:.8rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0 .4rem .4rem;padding:0}@media print{.md-content__button{display:none}}[dir=rtl] .md-content__button{float:left;margin-left:0;margin-right:.4rem}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);left:auto;min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;right:.8rem;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:3}@media print{.md-dialog{display:none}}[dir=rtl] .md-dialog{left:.8rem;right:auto}.md-dialog[data-md-state=open]{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-typeset .md-input{border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{box-shadow:0 .4rem 1rem rgba(0,0,0,.15),0 .025rem .05rem rgba(0,0,0,.15)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem transparent,0 .2rem .4rem transparent;color:var(--md-primary-bg-color);left:0;position:-webkit-sticky;position:sticky;right:0;top:0;z-index:3}@media print{.md-header{display:none}}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header[data-md-state=hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentColor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentColor;display:block;height:1.2rem;width:1.2rem}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem;margin-left:1rem;margin-right:.4rem}.md-header__title[data-md-state=active] .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic{transform:translateX(1.25rem)}.md-header__title[data-md-state=active] .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__source{display:none}@media screen and (min-width:60em){.md-header__source{display:block;margin-left:1rem;max-width:11.7rem;width:11.7rem}[dir=rtl] .md-header__source{margin-left:0;margin-right:1rem}}@media screen and (min-width:76.25em){.md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{overflow:auto;padding:.2rem}.md-footer__link{display:flex;outline-color:var(--md-accent-fg-color);padding-bottom:.4rem;padding-top:1.4rem;transition:opacity .25s}@media screen and (min-width:45em){.md-footer__link{width:50%}}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}.md-footer__link--prev{float:left}@media screen and (max-width:44.9375em){.md-footer__link--prev{width:25%}.md-footer__link--prev .md-footer__title{display:none}}[dir=rtl] .md-footer__link--prev{float:right}[dir=rtl] .md-footer__link--prev svg{transform:scaleX(-1)}.md-footer__link--next{float:right;text-align:right}@media screen and (max-width:44.9375em){.md-footer__link--next{width:75%}}[dir=rtl] .md-footer__link--next{float:left;text-align:left}[dir=rtl] .md-footer__link--next svg{transform:scaleX(-1)}.md-footer__title{flex-grow:1;font-size:.9rem;line-height:2.4rem;max-width:calc(100% - 2.4rem);padding:0 1rem;position:relative}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;left:0;margin-top:-1rem;opacity:.7;padding:0 1rem;position:absolute;right:0}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-footer-social__link:before{line-height:1.9}.md-footer-social__link svg{fill:currentColor;max-height:.8rem;vertical-align:-25%}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentColor;display:block;height:2.4rem;width:2.4rem}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__item{padding:0 .6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-left:0;padding-right:.6rem}.md-nav__link{align-items:center;cursor:pointer;display:flex;justify-content:space-between;margin-top:.625em;overflow:hidden;scroll-snap-align:start;text-overflow:ellipsis;transition:color 125ms}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item .md-nav__link--index [href]{width:100%}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentColor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__link>*{cursor:pointer;display:flex}.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;font-weight:400;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;left:.4rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{left:auto;right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentColor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;top:.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-logo{left:auto;right:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest);padding:0}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link .md-nav__icon{flex-shrink:0;font-size:1.2rem;height:1.2rem;margin-right:-.2rem;width:1.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem;margin-right:0}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentColor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:transparent;position:static}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:0;padding-right:1.4rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:2rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:2.6rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:3.2rem}.md-nav--secondary{background-color:transparent}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700;pointer-events:none}.md-nav__item--section>.md-nav__link--index [href]{pointer-events:auto}.md-nav__item--section>.md-nav__link .md-nav__icon{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{float:right;height:.9rem;transition:transform .25s;width:.9rem}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon:after{background-color:currentColor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:-.1rem;width:100%}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{font-weight:700;padding:0 .6rem;pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link--index [href]{pointer-events:auto}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link .md-nav__icon{display:none}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}.md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-right:.6rem}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{border-left:.05rem solid var(--md-primary-fg-color);display:block;margin-bottom:1.25em}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav>.md-nav__title{display:none}}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.9375em){.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;left:-2.2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[dir=rtl] .md-search__overlay{left:auto;right:-2.2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){.md-search__overlay{background-color:rgba(0,0,0,.54);cursor:pointer;height:0;left:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[dir=rtl] .md-search__overlay{left:auto;right:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){.md-search__inner{height:0;left:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{left:auto;right:0;transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){.md-search__inner{float:right;padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem transparent;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:rgba(0,0,0,.26);border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:hsla(0,0%,100%,.12)}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem rgba(0,0,0,.07);color:var(--md-default-fg-color)}.md-search__input{background:transparent;font-size:.9rem;height:100%;padding:0 2.2rem 0 3.6rem;position:relative;text-overflow:ellipsis;width:100%;z-index:2}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s;transition:color .25s}.md-search__input::-moz-placeholder{-moz-transition:color .25s;transition:color .25s}.md-search__input::-ms-input-placeholder{-ms-transition:color .25s;transition:color .25s}.md-search__input::placeholder{transition:color .25s}.md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.9375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){.md-search__input{color:inherit;font-size:.8rem;padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input::-webkit-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{left:.5rem;position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search]{left:auto;right:.5rem}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){.md-search__icon[for=__search]{left:.8rem;top:.6rem}[dir=rtl] .md-search__icon[for=__search]{left:auto;right:.8rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__options{pointer-events:none;position:absolute;right:.5rem;top:.3rem;z-index:2}[dir=rtl] .md-search__options{left:.5rem;right:auto}@media screen and (max-width:59.9375em){.md-search__options{right:.8rem;top:.6rem}[dir=rtl] .md-search__options{left:.8rem;right:auto}}.md-search__options>*{color:var(--md-default-fg-color--light);margin-left:.2rem;opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>*{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>:hover{opacity:.7}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;padding:0 2.2rem 0 3.6rem;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}[dir=rtl] .md-search__suggest{padding:0 3.6rem 0 2.2rem}@media screen and (min-width:60em){.md-search__suggest{font-size:.8rem;padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}.md-search__output{border-radius:0 0 .1rem .1rem;overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.9375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-left:0;padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0}.md-search-result__item{box-shadow:0 -.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{color:var(--md-typeset-a-color);cursor:pointer;display:block;font-size:.64rem;outline:none;padding:.75em .8rem;scroll-snap-align:start;transition:color .25s,background-color .25s}@media screen and (min-width:60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-left:.8rem;padding-right:2.2rem}}.md-search-result__more summary:focus,.md-search-result__more summary:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more summary::-webkit-details-marker,.md-search-result__more summary::marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-left:.8rem;padding-right:2.2rem}}.md-search-result__article--document .md-search-result__title{font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;left:0;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentColor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon{left:auto;right:0}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result__title{font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result__teaser{-webkit-box-orient:vertical;-webkit-line-clamp:2;color:var(--md-default-fg-color--light);display:-webkit-box;font-size:.64rem;line-height:1.6;margin:.5em 0;max-height:2rem;overflow:hidden;text-overflow:ellipsis}@media screen and (max-width:44.9375em){.md-search-result__teaser{-webkit-line-clamp:3;max-height:3rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search-result__teaser{-webkit-line-clamp:3;max-height:3rem}}.md-search-result__teaser mark{background-color:transparent;text-decoration:underline}.md-search-result__terms{font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:transparent;color:var(--md-accent-fg-color)}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid transparent;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid transparent;border-right:.2rem solid transparent;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}.md-select__link{cursor:pointer;display:block;outline:none;padding-left:.6rem;padding-right:1.2rem;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:-webkit-sticky;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;left:-12.1rem;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:4}[dir=rtl] .md-sidebar--primary{left:auto;right:-12.1rem}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;-ms-scroll-snap-type:none;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:76.1875em){.md-overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:4}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@-webkit-keyframes facts{0%{height:0}to{height:.65rem}}@keyframes facts{0%{height:0}to{height:.65rem}}@-webkit-keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}.md-source__icon svg{margin-left:.6rem;margin-top:.6rem}[dir=rtl] .md-source__icon svg{margin-left:0;margin-right:.6rem}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-left:0;margin-right:-2rem;padding-left:0;padding-right:2rem}.md-source__repository{display:inline-block;margin-left:.6rem;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{font-size:.55rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0}[data-md-state=done] .md-source__facts{-webkit-animation:facts .25s ease-in;animation:facts .25s ease-in}.md-source__fact{display:inline-block}[data-md-state=done] .md-source__fact{-webkit-animation:fact .4s ease-out;animation:fact .4s ease-out}.md-source__fact:before{background-color:currentColor;content:"";display:inline-block;height:.6rem;margin-right:.1rem;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem;margin-right:0}.md-source__fact:nth-child(1n+2):before{margin-left:.4rem}[dir=rtl] .md-source__fact:nth-child(1n+2):before{margin-left:.1rem;margin-right:.4rem}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);overflow:auto;width:100%}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs__list{contain:content;list-style:none;margin:0 0 0 .2rem;padding:0;white-space:nowrap}[dir=rtl] .md-tabs__list{margin-left:0;margin-right:.2rem}.md-tabs__item{display:inline-block;height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link--active,.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color--light);font-size:.7rem;margin-left:50%;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{float:left}.md-top[data-md-state=hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@-webkit-keyframes hoverfix{0%{pointer-events:none}}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}.md-version__current{color:inherit;cursor:pointer;margin-left:1.4rem;margin-right:.4rem;outline:none;position:relative;top:.05rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current:after{background-color:currentColor;content:"";display:inline-block;height:.6rem;margin-left:.4rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;width:.4rem}[dir=rtl] .md-version__current:after{margin-left:0;margin-right:.4rem}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:1}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (pointer:coarse){.md-version:hover .md-version__list{-webkit-animation:hoverfix .25s forwards;animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{-webkit-animation:none;animation:none}}.md-version__item{line-height:1.8rem}.md-version__link{cursor:pointer;display:block;outline:none;padding-left:.6rem;padding-right:1.2rem;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 .025rem .05rem rgba(0,0,0,.05);color:var(--md-admonition-fg-color);font-size:.64rem;margin:1.5625em 0;overflow:hidden;padding:0 .6rem;page-break-inside:avoid}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-left:none;border-right:.2rem solid #448aff}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition-title,.md-typeset summary{background-color:rgba(68,138,255,.1);border-left:.2rem solid #448aff;font-weight:700;margin:0 -.6rem 0 -.8rem;padding:.4rem .6rem .4rem 2rem;position:relative}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-left:none;border-right:.2rem solid #448aff;margin:0 -.8rem 0 -.6rem;padding:.4rem 2rem .4rem .6rem}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;left:.6rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;width:1rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{left:auto;right:.6rem}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1);border-color:#448aff}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary{background-color:rgba(0,176,255,.1);border-color:#00b0ff}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before,.md-typeset .summary>.admonition-title:before,.md-typeset .summary>summary:before,.md-typeset .tldr>.admonition-title:before,.md-typeset .tldr>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1);border-color:#00b8d4}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before,.md-typeset .todo>.admonition-title:before,.md-typeset .todo>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .hint>.admonition-title,.md-typeset .hint>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:rgba(0,191,165,.1);border-color:#00bfa5}.md-typeset .hint>.admonition-title:before,.md-typeset .hint>summary:before,.md-typeset .important>.admonition-title:before,.md-typeset .important>summary:before,.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-color:#00c853}.md-typeset .check>.admonition-title,.md-typeset .check>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:rgba(0,200,83,.1);border-color:#00c853}.md-typeset .check>.admonition-title:before,.md-typeset .check>summary:before,.md-typeset .done>.admonition-title:before,.md-typeset .done>summary:before,.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-color:#64dd17}.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary,.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:rgba(100,221,23,.1);border-color:#64dd17}.md-typeset .faq>.admonition-title:before,.md-typeset .faq>summary:before,.md-typeset .help>.admonition-title:before,.md-typeset .help>summary:before,.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-color:#ff9100}.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary,.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:rgba(255,145,0,.1);border-color:#ff9100}.md-typeset .attention>.admonition-title:before,.md-typeset .attention>summary:before,.md-typeset .caution>.admonition-title:before,.md-typeset .caution>summary:before,.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-color:#ff5252}.md-typeset .fail>.admonition-title,.md-typeset .fail>summary,.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary{background-color:rgba(255,82,82,.1);border-color:#ff5252}.md-typeset .fail>.admonition-title:before,.md-typeset .fail>summary:before,.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before,.md-typeset .missing>.admonition-title:before,.md-typeset .missing>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1);border-color:#ff1744}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before,.md-typeset .error>.admonition-title:before,.md-typeset .error>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1);border-color:#f50057}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(124,77,255,.1);border-color:#7c4dff}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .cite>.admonition-title,.md-typeset .cite>summary,.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:hsla(0,0%,62%,.1);border-color:#9e9e9e}.md-typeset .cite>.admonition-title:before,.md-typeset .cite>summary:before,.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote>ol{margin-left:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentColor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}.md-typeset [id^="fnref:"]:target{margin-top:-3.4rem;padding-top:3.4rem;scroll-margin-top:0}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset [id^="fn:"]:target{margin-top:-3.45rem;padding-top:3.45rem;scroll-margin-top:0}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;margin-left:.5rem;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}[dir=rtl] .md-typeset .headerlink{margin-left:0;margin-right:.5rem}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{scroll-margin-top:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{scroll-margin-top:0}.md-typeset h1:target:before,.md-typeset h2:target:before,.md-typeset h3:target:before{content:"";display:block;margin-top:-3.4rem;padding-top:3.4rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h1:target,.md-header--lifted~.md-container .md-typeset h2:target,.md-header--lifted~.md-container .md-typeset h3:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h1:target:before,.md-header--lifted~.md-container .md-typeset h2:target:before,.md-header--lifted~.md-container .md-typeset h3:target:before{margin-top:-5.8rem;padding-top:5.8rem}}.md-typeset h4:target{scroll-margin-top:0}.md-typeset h4:target:before{content:"";display:block;margin-top:-3.45rem;padding-top:3.45rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h4:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h4:target:before{margin-top:-5.85rem;padding-top:5.85rem}}.md-typeset h5:target,.md-typeset h6:target{scroll-margin-top:0}.md-typeset h5:target:before,.md-typeset h6:target:before{content:"";display:block;margin-top:-3.6rem;padding-top:3.6rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h5:target,.md-header--lifted~.md-container .md-typeset h6:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h5:target:before,.md-header--lifted~.md-container .md-typeset h6:target:before{margin-top:-6rem;padding-top:6rem}}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{margin:1em auto!important;padding:0 .8rem;touch-action:auto;width:-webkit-min-content;width:-moz-min-content;width:min-content}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details:after{content:"";display:table}.md-typeset summary{border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer;display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2rem}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset summary:after{background-color:currentColor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;right:.4rem;top:.4rem;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{left:.4rem;right:auto;transform:rotate(180deg)}.md-typeset summary::-webkit-details-marker,.md-typeset summary::marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentColor;max-height:100%;width:1.125em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color);display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:-webkit-sticky;position:sticky;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable{display:flow-root;overflow:hidden}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;overflow:hidden}.md-typeset .highlighttable{border-radius:.1rem;direction:ltr;margin:1em 0}.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width:44.9375em){.md-typeset>.highlight{margin:1em -.8rem}.md-typeset>.highlight .hll{margin:0 -.8rem;padding:0 .8rem}.md-typeset>.highlight code{border-radius:0}.md-typeset>.highlighttable{border-radius:0;margin:1em -.8rem}.md-typeset>.highlighttable .hll{margin:0 -.8rem;padding:0 .8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-left-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-left-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-right-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-right-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-left-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-right-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}.md-typeset .tabbed-content{box-shadow:0 -.05rem var(--md-default-fg-color--lightest);display:none;order:99;width:100%}@media print{.md-typeset .tabbed-content{display:block;order:0}}.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child,.md-typeset .tabbed-content>pre:only-child{margin:0}.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code,.md-typeset .tabbed-content>pre:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:checked+label{border-color:var(--md-accent-fg-color);color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-typeset .tabbed-set>input:not(.focus-visible)+label{-webkit-tap-highlight-color:transparent;outline:none}.md-typeset .tabbed-set>label{border-bottom:.1rem solid transparent;color:var(--md-default-fg-color--light);cursor:pointer;font-size:.64rem;font-weight:700;padding:.9375em 1.25em .78125em;transition:color .25s;width:auto;z-index:1}.md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}@media screen{.md-typeset .tabbed-alternate input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-alternate input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-alternate input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-alternate input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-alternate input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-alternate input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-alternate input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-alternate input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-alternate input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.md-typeset .tabbed-alternate input:nth-child(10):checked~.tabbed-labels>:nth-child(10){border-color:var(--md-accent-fg-color);color:var(--md-accent-fg-color)}}.md-typeset .tabbed-alternate input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-alternate input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-alternate input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-alternate input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-alternate input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-alternate input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-alternate input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-alternate input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-alternate input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9),.md-typeset .tabbed-alternate input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10){background-color:var(--md-accent-fg-color--transparent)}.md-typeset .tabbed-alternate input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-alternate input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-alternate input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-alternate input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-alternate input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-alternate input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-alternate input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-alternate input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-alternate input:nth-child(9):checked~.tabbed-content>:nth-child(9),.md-typeset .tabbed-alternate input:nth-child(10):checked~.tabbed-content>:nth-child(10){display:block}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100vw;overflow:auto;-ms-scroll-snap-type:x proximity;scroll-snap-type:x proximity;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid transparent;border-top-left-radius:.1rem;border-top-right-radius:.1rem;color:var(--md-default-fg-color--light);cursor:pointer;font-size:.64rem;font-weight:700;padding:.9375em 1.25em .78125em;scroll-snap-align:start;transition:background-color .25s,color .25s;white-space:nowrap;width:auto;z-index:1}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}}.md-typeset .tabbed-labels>label:hover{color:var(--md-accent-fg-color)}@media screen and (max-width:44.9375em){.md-typeset>.tabbed-alternate .tabbed-labels{margin:0 -.8rem;padding:0 .8rem;scroll-padding:0 .8rem}}.md-typeset .tabbed-alternate{flex-direction:column}.md-typeset .tabbed-alternate .tabbed-content{box-shadow:none;display:initial;order:0;width:100%}@media print{.md-typeset .tabbed-alternate .tabbed-content{display:contents}}.md-typeset .tabbed-alternate .tabbed-block{display:none}@media print{.md-typeset .tabbed-alternate .tabbed-block{display:block}.md-typeset .tabbed-alternate .tabbed-block:first-child{order:1}.md-typeset .tabbed-alternate .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-alternate .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-alternate .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-alternate .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-alternate .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-alternate .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-alternate .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-alternate .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-alternate .tabbed-block:nth-child(10){order:10}}.md-typeset .tabbed-alternate .tabbed-block>.highlight:only-child pre,.md-typeset .tabbed-alternate .tabbed-block>.highlighttable:only-child,.md-typeset .tabbed-alternate .tabbed-block>pre:only-child{margin:0}.md-typeset .tabbed-alternate .tabbed-block>.highlight:only-child pre>code,.md-typeset .tabbed-alternate .tabbed-block>.highlighttable:only-child>code,.md-typeset .tabbed-alternate .tabbed-block>pre:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-alternate .tabbed-block>.tabbed-set{margin:0}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}.md-typeset .task-list-item [type=checkbox]{left:-2em;position:absolute;top:.45em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{left:auto;right:-2em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;left:-1.5em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}[dir=rtl] .md-typeset .task-list-indicator:before{left:auto;right:-1.5em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media screen and (min-width:45em){.md-typeset .inline{float:left;margin-bottom:.8rem;margin-right:.8rem;margin-top:0;width:11.7rem}[dir=rtl] .md-typeset .inline{float:right;margin-left:.8rem;margin-right:0}.md-typeset .inline.end{float:right;margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{float:left;margin-left:0;margin-right:.8rem}} +/*# sourceMappingURL=main.1c75ef36.min.css.map */ \ No newline at end of file diff --git a/assets/stylesheets/main.1c75ef36.min.css.map b/assets/stylesheets/main.1c75ef36.min.css.map new file mode 100644 index 000000000..2b3a961fb --- /dev/null +++ b/assets/stylesheets/main.1c75ef36.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_reset.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","node_modules/material-shadows/material-shadows.scss","src/assets/stylesheets/main/layout/_base.scss","src/assets/stylesheets/main/layout/_announce.scss","src/assets/stylesheets/main/layout/_clipboard.scss","src/assets/stylesheets/main/layout/_content.scss","src/assets/stylesheets/main/layout/_dialog.scss","src/assets/stylesheets/main/layout/_form.scss","src/assets/stylesheets/main/layout/_header.scss","src/assets/stylesheets/main/layout/_footer.scss","src/assets/stylesheets/main/layout/_nav.scss","src/assets/stylesheets/main/layout/_search.scss","src/assets/stylesheets/main/layout/_select.scss","src/assets/stylesheets/main/layout/_sidebar.scss","src/assets/stylesheets/main/layout/_source.scss","src/assets/stylesheets/main/layout/_tabs.scss","src/assets/stylesheets/main/layout/_top.scss","src/assets/stylesheets/main/layout/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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","file":"src/assets/stylesheets/main.scss","sourcesContent":["////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Keyboard key\n .keys {\n\n // Keyboard key icon\n kbd::before,\n kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n }\n\n // Surrounding text\n span {\n padding: 0 px2em(3.2px);\n color: var(--md-default-fg-color--light);\n }\n\n // Define keyboard keys with left icon\n @each $name, $code in (\n\n // Modifiers\n \"alt\": \"\\2387\",\n \"left-alt\": \"\\2387\",\n \"right-alt\": \"\\2387\",\n \"command\": \"\\2318\",\n \"left-command\": \"\\2318\",\n \"right-command\": \"\\2318\",\n \"control\": \"\\2303\",\n \"left-control\": \"\\2303\",\n \"right-control\": \"\\2303\",\n \"meta\": \"\\25C6\",\n \"left-meta\": \"\\25C6\",\n \"right-meta\": \"\\25C6\",\n \"option\": \"\\2325\",\n \"left-option\": \"\\2325\",\n \"right-option\": \"\\2325\",\n \"shift\": \"\\21E7\",\n \"left-shift\": \"\\21E7\",\n \"right-shift\": \"\\21E7\",\n \"super\": \"\\2756\",\n \"left-super\": \"\\2756\",\n \"right-super\": \"\\2756\",\n \"windows\": \"\\229E\",\n \"left-windows\": \"\\229E\",\n \"right-windows\": \"\\229E\",\n\n // Other keys\n \"arrow-down\": \"\\2193\",\n \"arrow-left\": \"\\2190\",\n \"arrow-right\": \"\\2192\",\n \"arrow-up\": \"\\2191\",\n \"backspace\": \"\\232B\",\n \"backtab\": \"\\21E4\",\n \"caps-lock\": \"\\21EA\",\n \"clear\": \"\\2327\",\n \"context-menu\": \"\\2630\",\n \"delete\": \"\\2326\",\n \"eject\": \"\\23CF\",\n \"end\": \"\\2913\",\n \"escape\": \"\\238B\",\n \"home\": \"\\2912\",\n \"insert\": \"\\2380\",\n \"page-down\": \"\\21DF\",\n \"page-up\": \"\\21DE\",\n \"print-screen\": \"\\2399\"\n ) {\n .key-#{$name} {\n &::before {\n padding-right: px2em(6.4px);\n content: $code;\n }\n }\n }\n\n // Define keyboard keys with right icon\n @each $name, $code in (\n \"tab\": \"\\21E5\",\n \"num-enter\": \"\\2324\",\n \"enter\": \"\\23CE\"\n ) {\n .key-#{$name} {\n &::after {\n padding-left: px2em(6.4px);\n content: $code;\n }\n }\n }\n }\n}\n","@charset \"UTF-8\";\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\nbody {\n margin: 0;\n}\n\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\na {\n color: inherit;\n text-decoration: none;\n}\n\nhr {\n display: block;\n box-sizing: content-box;\n height: 0.05rem;\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n line-height: 1em;\n}\n\nimg {\n border-style: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\ninput {\n border: 0;\n outline: none;\n}\n\n:root {\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n --md-primary-fg-color: hsla(231, 48%, 48%, 1);\n --md-primary-fg-color--light: hsla(231, 44%, 56%, 1);\n --md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-accent-fg-color: hsla(231, 99%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n}\n:root > * {\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-hl-color: hsla(60, 100%, 50%, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n --md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n --md-typeset-table-color: hsla(0, 0%, 0%, 0.12);\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n}\n\n.md-icon svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family: var(--md-text-font-family, _), -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family: var(--md-code-font-family, _), SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n:root {\n --md-typeset-table-sort-icon: svg-load(\"material/sort.svg\");\n --md-typeset-table-sort-icon--asc: svg-load(\"material/sort-ascending.svg\");\n --md-typeset-table-sort-icon--desc: svg-load(\"material/sort-descending.svg\");\n}\n\n.md-typeset {\n font-size: 0.8rem;\n line-height: 1.6;\n color-adjust: exact;\n}\n@media print {\n .md-typeset {\n font-size: 0.68rem;\n }\n}\n.md-typeset ul,\n.md-typeset ol,\n.md-typeset dl,\n.md-typeset figure,\n.md-typeset blockquote,\n.md-typeset pre {\n margin: 1em 0;\n}\n.md-typeset h1 {\n margin: 0 0 1.25em;\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: 2em;\n line-height: 1.3;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 {\n margin: 1.6em 0 0.64em;\n font-weight: 300;\n font-size: 1.5625em;\n line-height: 1.4;\n letter-spacing: -0.01em;\n}\n.md-typeset h3 {\n margin: 1.6em 0 0.8em;\n font-weight: 400;\n font-size: 1.25em;\n line-height: 1.5;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 + h3 {\n margin-top: 0.8em;\n}\n.md-typeset h4 {\n margin: 1em 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n}\n.md-typeset h5,\n.md-typeset h6 {\n margin: 1.25em 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.8em;\n letter-spacing: -0.01em;\n}\n.md-typeset h5 {\n text-transform: uppercase;\n}\n.md-typeset hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n}\n.md-typeset a, .md-typeset a::before {\n transition: color 125ms;\n}\n.md-typeset a:focus, .md-typeset a:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset a.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n}\n@media print {\n .md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n white-space: pre-wrap;\n }\n}\n.md-typeset code {\n padding: 0 0.2941176471em;\n font-size: 0.85em;\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: 0.1rem;\n box-decoration-break: clone;\n}\n.md-typeset code:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset h1 code,\n.md-typeset h2 code,\n.md-typeset h3 code,\n.md-typeset h4 code,\n.md-typeset h5 code,\n.md-typeset h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n}\n.md-typeset a code {\n color: currentColor;\n}\n.md-typeset pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n}\n.md-typeset pre > code {\n display: block;\n margin: 0;\n padding: 0.7720588235em 1.1764705882em;\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-typeset pre > code:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-typeset pre > code::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > pre {\n margin: 1em -0.8rem;\n }\n .md-typeset > pre code {\n border-radius: 0;\n }\n}\n.md-typeset kbd {\n display: inline-block;\n padding: 0 0.6666666667em;\n color: var(--md-default-fg-color);\n font-size: 0.75em;\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.1rem 0 0.05rem var(--md-typeset-kbd-border-color), 0 0.1rem 0 var(--md-typeset-kbd-border-color), 0 -0.1rem 0.2rem var(--md-typeset-kbd-accent-color) inset;\n}\n.md-typeset mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n}\n.md-typeset abbr {\n text-decoration: none;\n border-bottom: 0.05rem dotted var(--md-default-fg-color--light);\n cursor: help;\n}\n@media (hover: none) {\n .md-typeset abbr {\n position: relative;\n }\n .md-typeset abbr[title]:focus::after, .md-typeset abbr[title]:hover::after {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: 0.2rem 0.3rem;\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n content: attr(title);\n }\n}\n.md-typeset small {\n opacity: 0.75;\n}\n.md-typeset sup,\n.md-typeset sub {\n margin-left: 0.078125em;\n}\n[dir=rtl] .md-typeset sup,\n[dir=rtl] .md-typeset sub {\n margin-right: 0.078125em;\n margin-left: initial;\n}\n.md-typeset blockquote {\n padding-left: 0.6rem;\n color: var(--md-default-fg-color--light);\n border-left: 0.2rem solid var(--md-default-fg-color--lighter);\n}\n[dir=rtl] .md-typeset blockquote {\n padding-right: 0.6rem;\n padding-left: initial;\n border-right: 0.2rem solid var(--md-default-fg-color--lighter);\n border-left: initial;\n}\n.md-typeset ul {\n list-style-type: disc;\n}\n.md-typeset ul,\n.md-typeset ol {\n display: flow-root;\n margin-left: 0.625em;\n padding: 0;\n}\n[dir=rtl] .md-typeset ul,\n[dir=rtl] .md-typeset ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset ul ol,\n.md-typeset ol ol {\n list-style-type: lower-alpha;\n}\n.md-typeset ul ol ol,\n.md-typeset ol ol ol {\n list-style-type: lower-roman;\n}\n.md-typeset ul li,\n.md-typeset ol li {\n margin-bottom: 0.5em;\n margin-left: 1.25em;\n}\n[dir=rtl] .md-typeset ul li,\n[dir=rtl] .md-typeset ol li {\n margin-right: 1.25em;\n margin-left: initial;\n}\n.md-typeset ul li p,\n.md-typeset ul li blockquote,\n.md-typeset ol li p,\n.md-typeset ol li blockquote {\n margin: 0.5em 0;\n}\n.md-typeset ul li:last-child,\n.md-typeset ol li:last-child {\n margin-bottom: 0;\n}\n.md-typeset ul li ul,\n.md-typeset ul li ol,\n.md-typeset ol li ul,\n.md-typeset ol li ol {\n margin: 0.5em 0 0.5em 0.625em;\n}\n[dir=rtl] .md-typeset ul li ul,\n[dir=rtl] .md-typeset ul li ol,\n[dir=rtl] .md-typeset ol li ul,\n[dir=rtl] .md-typeset ol li ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset dd {\n margin: 1em 0 1.5em 1.875em;\n}\n[dir=rtl] .md-typeset dd {\n margin-right: 1.875em;\n margin-left: initial;\n}\n.md-typeset img,\n.md-typeset svg {\n max-width: 100%;\n height: auto;\n}\n.md-typeset img[align=left],\n.md-typeset svg[align=left] {\n margin: 1em;\n margin-left: 0;\n}\n.md-typeset img[align=right],\n.md-typeset svg[align=right] {\n margin: 1em;\n margin-right: 0;\n}\n.md-typeset img[align]:only-child,\n.md-typeset svg[align]:only-child {\n margin-top: 0;\n}\n.md-typeset figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n}\n.md-typeset figure img {\n display: block;\n}\n.md-typeset figcaption {\n max-width: 24rem;\n margin: 1em auto 2em;\n font-style: italic;\n}\n.md-typeset iframe {\n max-width: 100%;\n}\n.md-typeset table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: 0.64rem;\n background-color: var(--md-default-bg-color);\n border: 0.05rem solid var(--md-typeset-table-color);\n border-radius: 0.1rem;\n touch-action: auto;\n}\n@media print {\n .md-typeset table:not([class]) {\n display: table;\n }\n}\n.md-typeset table:not([class]) + * {\n margin-top: 1.5em;\n}\n.md-typeset table:not([class]) th > *:first-child,\n.md-typeset table:not([class]) td > *:first-child {\n margin-top: 0;\n}\n.md-typeset table:not([class]) th > *:last-child,\n.md-typeset table:not([class]) td > *:last-child {\n margin-bottom: 0;\n}\n.md-typeset table:not([class]) th:not([align]),\n.md-typeset table:not([class]) td:not([align]) {\n text-align: left;\n}\n[dir=rtl] .md-typeset table:not([class]) th:not([align]),\n[dir=rtl] .md-typeset table:not([class]) td:not([align]) {\n text-align: right;\n}\n.md-typeset table:not([class]) th {\n min-width: 5rem;\n padding: 0.9375em 1.25em;\n font-weight: 700;\n vertical-align: top;\n}\n.md-typeset table:not([class]) th a {\n color: inherit;\n}\n.md-typeset table:not([class]) td {\n padding: 0.9375em 1.25em;\n vertical-align: top;\n border-top: 0.05rem solid var(--md-typeset-table-color);\n}\n.md-typeset table:not([class]) tbody tr {\n transition: background-color 125ms;\n}\n.md-typeset table:not([class]) tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 0.05rem 0 var(--md-default-bg-color) inset;\n}\n.md-typeset table:not([class]) a {\n word-break: normal;\n}\n.md-typeset table th[role=columnheader] {\n cursor: pointer;\n}\n.md-typeset table th[role=columnheader]::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: text-bottom;\n mask-image: var(--md-typeset-table-sort-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transition: background-color 125ms;\n content: \"\";\n}\n.md-typeset table th[role=columnheader]:hover::after {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset table th[role=columnheader][aria-sort=ascending]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--asc);\n}\n.md-typeset table th[role=columnheader][aria-sort=descending]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--desc);\n}\n.md-typeset__scrollwrap {\n margin: 1em -0.8rem;\n overflow-x: auto;\n touch-action: auto;\n}\n.md-typeset__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 0.8rem;\n}\n@media print {\n .md-typeset__table {\n display: block;\n }\n}\nhtml .md-typeset__table table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n}\n\nhtml {\n height: 100%;\n overflow-x: hidden;\n font-size: 125%;\n}\n@media screen and (min-width: 100em) {\n html {\n font-size: 137.5%;\n }\n}\n@media screen and (min-width: 125em) {\n html {\n font-size: 150%;\n }\n}\n\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n font-size: 0.5rem;\n background-color: var(--md-default-bg-color);\n}\n@media print {\n body {\n display: block;\n }\n}\n@media screen and (max-width: 59.9375em) {\n body[data-md-state=lock] {\n position: fixed;\n }\n}\n\n.md-grid {\n max-width: 61rem;\n margin-right: auto;\n margin-left: auto;\n}\n\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n}\n@media print {\n .md-container {\n display: block;\n }\n}\n\n.md-main {\n flex-grow: 1;\n}\n.md-main__inner {\n display: flex;\n height: 100%;\n margin-top: 1.5rem;\n}\n\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.md-toggle {\n display: none;\n}\n\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-option:checked + label:not([hidden]) {\n display: block;\n}\n.md-option.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n\n.md-skip {\n position: fixed;\n z-index: -1;\n margin: 0.5rem;\n padding: 0.3rem 0.5rem;\n color: var(--md-default-bg-color);\n font-size: 0.64rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n transform: translateY(0.4rem);\n opacity: 0;\n}\n.md-skip:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 175ms 75ms;\n}\n\n@page {\n margin: 25mm;\n}\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-announce {\n display: none;\n }\n}\n.md-announce__inner {\n margin: 0.6rem auto;\n padding: 0 0.8rem;\n color: var(--md-footer-fg-color);\n font-size: 0.7rem;\n}\n\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n.md-clipboard {\n position: absolute;\n top: 0.5em;\n right: 0.5em;\n z-index: 1;\n width: 1.5em;\n height: 1.5em;\n color: var(--md-default-fg-color--lightest);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.1rem;\n cursor: pointer;\n transition: color 250ms;\n}\n@media print {\n .md-clipboard {\n display: none;\n }\n}\n.md-clipboard:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n:hover > .md-clipboard {\n color: var(--md-default-fg-color--light);\n}\n.md-clipboard:focus, .md-clipboard:hover {\n color: var(--md-accent-fg-color);\n}\n.md-clipboard::after {\n display: block;\n width: 1.125em;\n height: 1.125em;\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-clipboard--inline {\n cursor: pointer;\n}\n.md-clipboard--inline code {\n transition: color 250ms, background-color 250ms;\n}\n.md-clipboard--inline:focus code, .md-clipboard--inline:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n.md-content {\n flex-grow: 1;\n overflow: hidden;\n scroll-padding-top: 51.2rem;\n}\n.md-content__inner {\n margin: 0 0.8rem 1.2rem;\n padding-top: 0.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-left: 1.2rem;\n }\n [dir=rtl] .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n margin-left: 0.8rem;\n }\n .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n }\n [dir=rtl] .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 0.8rem;\n margin-left: 1.2rem;\n }\n}\n.md-content__inner::before {\n display: block;\n height: 0.4rem;\n content: \"\";\n}\n.md-content__inner > :last-child {\n margin-bottom: 0;\n}\n.md-content__button {\n float: right;\n margin: 0.4rem 0;\n margin-left: 0.4rem;\n padding: 0;\n}\n@media print {\n .md-content__button {\n display: none;\n }\n}\n[dir=rtl] .md-content__button {\n float: left;\n margin-right: 0.4rem;\n margin-left: initial;\n}\n[dir=rtl] .md-content__button svg {\n transform: scaleX(-1);\n}\n.md-typeset .md-content__button {\n color: var(--md-default-fg-color--lighter);\n}\n.md-content__button svg {\n display: inline;\n vertical-align: top;\n}\n\n.md-dialog {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: fixed;\n right: 0.8rem;\n bottom: 0.8rem;\n left: initial;\n z-index: 3;\n min-width: 11.1rem;\n padding: 0.4rem 0.6rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n transform: translateY(100%);\n opacity: 0;\n transition: transform 0ms 400ms, opacity 400ms;\n pointer-events: none;\n}\n@media print {\n .md-dialog {\n display: none;\n }\n}\n[dir=rtl] .md-dialog {\n right: initial;\n left: 0.8rem;\n}\n.md-dialog[data-md-state=open] {\n transform: translateY(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1), opacity 400ms;\n pointer-events: initial;\n}\n.md-dialog__inner {\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n}\n\n.md-typeset .md-button {\n display: inline-block;\n padding: 0.625em 2em;\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: 0.1rem solid currentColor;\n border-radius: 0.1rem;\n cursor: pointer;\n transition: color 125ms, background-color 125ms, border-color 125ms;\n}\n.md-typeset .md-button--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n}\n.md-typeset .md-button:focus, .md-typeset .md-button:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .md-input {\n height: 1.8rem;\n padding: 0 0.6rem;\n font-size: 0.8rem;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transition: box-shadow 250ms;\n}\n.md-typeset .md-input:focus, .md-typeset .md-input:hover {\n box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.15), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.15);\n}\n.md-typeset .md-input--stretch {\n width: 100%;\n}\n\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 3;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0), 0 0.2rem 0.4rem rgba(0, 0, 0, 0);\n}\n@media print {\n .md-header {\n display: none;\n }\n}\n.md-header[data-md-state=shadow] {\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2);\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), box-shadow 250ms;\n}\n.md-header[data-md-state=hidden] {\n transform: translateY(-100%);\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), box-shadow 250ms;\n}\n.md-header__inner {\n display: flex;\n align-items: center;\n padding: 0 0.2rem;\n}\n.md-header__button {\n position: relative;\n z-index: 1;\n margin: 0.2rem;\n padding: 0.4rem;\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n}\n.md-header__button:hover {\n opacity: 0.7;\n}\n.md-header__button:not([hidden]) {\n display: inline-block;\n}\n.md-header__button:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-header__button.md-logo {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n@media screen and (max-width: 76.1875em) {\n .md-header__button.md-logo {\n display: none;\n }\n}\n.md-header__button.md-logo img,\n.md-header__button.md-logo svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n@media screen and (min-width: 60em) {\n .md-header__button[for=__search] {\n display: none;\n }\n}\n.no-js .md-header__button[for=__search] {\n display: none;\n}\n[dir=rtl] .md-header__button[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (min-width: 76.25em) {\n .md-header__button[for=__drawer] {\n display: none;\n }\n}\n.md-header__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-header__topic + .md-header__topic {\n z-index: -1;\n transform: translateX(1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__topic + .md-header__topic {\n transform: translateX(-1.25rem);\n}\n.md-header__title {\n flex-grow: 1;\n height: 2.4rem;\n margin-right: 0.4rem;\n margin-left: 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-header__title[data-md-state=active] .md-header__topic {\n z-index: -1;\n transform: translateX(-1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic {\n transform: translateX(1.25rem);\n}\n.md-header__title[data-md-state=active] .md-header__topic + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: initial;\n}\n.md-header__title > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.md-header__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition: max-width 0ms 250ms, opacity 250ms 250ms;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-header__option {\n max-width: 0;\n opacity: 0;\n transition: max-width 0ms, opacity 0ms;\n}\n.md-header__source {\n display: none;\n}\n@media screen and (min-width: 60em) {\n .md-header__source {\n display: block;\n width: 11.7rem;\n max-width: 11.7rem;\n margin-left: 1rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1rem;\n margin-left: initial;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-header__source {\n margin-left: 1.4rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1.4rem;\n }\n}\n\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-footer {\n display: none;\n }\n}\n.md-footer__inner {\n padding: 0.2rem;\n overflow: auto;\n}\n.md-footer__link {\n display: flex;\n padding-top: 1.4rem;\n padding-bottom: 0.4rem;\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n}\n@media screen and (min-width: 45em) {\n .md-footer__link {\n width: 50%;\n }\n}\n.md-footer__link:focus, .md-footer__link:hover {\n opacity: 0.7;\n}\n.md-footer__link--prev {\n float: left;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--prev {\n width: 25%;\n }\n .md-footer__link--prev .md-footer__title {\n display: none;\n }\n}\n[dir=rtl] .md-footer__link--prev {\n float: right;\n}\n[dir=rtl] .md-footer__link--prev svg {\n transform: scaleX(-1);\n}\n.md-footer__link--next {\n float: right;\n text-align: right;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--next {\n width: 75%;\n }\n}\n[dir=rtl] .md-footer__link--next {\n float: left;\n text-align: left;\n}\n[dir=rtl] .md-footer__link--next svg {\n transform: scaleX(-1);\n}\n.md-footer__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - 2.4rem);\n padding: 0 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-footer__button {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n.md-footer__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: -1rem;\n padding: 0 1rem;\n font-size: 0.64rem;\n opacity: 0.7;\n}\n\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n}\n.md-footer-meta__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: 0.2rem;\n}\nhtml .md-footer-meta.md-typeset a {\n color: var(--md-footer-fg-color--light);\n}\nhtml .md-footer-meta.md-typeset a:focus, html .md-footer-meta.md-typeset a:hover {\n color: var(--md-footer-fg-color);\n}\n\n.md-footer-copyright {\n width: 100%;\n margin: auto 0.6rem;\n padding: 0.4rem 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: 0.64rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-copyright {\n width: auto;\n }\n}\n.md-footer-copyright__highlight {\n color: var(--md-footer-fg-color--light);\n}\n\n.md-footer-social {\n margin: 0 0.4rem;\n padding: 0.2rem 0 0.6rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-social {\n padding: 0.6rem 0;\n }\n}\n.md-footer-social__link {\n display: inline-block;\n width: 1.6rem;\n height: 1.6rem;\n text-align: center;\n}\n.md-footer-social__link::before {\n line-height: 1.9;\n}\n.md-footer-social__link svg {\n max-height: 0.8rem;\n vertical-align: -25%;\n fill: currentColor;\n}\n\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n.md-nav {\n font-size: 0.7rem;\n line-height: 1.3;\n}\n.md-nav__title {\n display: block;\n padding: 0 0.6rem;\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n}\n.md-nav__title .md-nav__button {\n display: none;\n}\n.md-nav__title .md-nav__button img {\n width: auto;\n height: 100%;\n}\n.md-nav__title .md-nav__button.md-logo img,\n.md-nav__title .md-nav__button.md-logo svg {\n display: block;\n width: 2.4rem;\n height: 2.4rem;\n fill: currentColor;\n}\n.md-nav__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-nav__item {\n padding: 0 0.6rem;\n}\n.md-nav__item .md-nav__item {\n padding-right: 0;\n}\n[dir=rtl] .md-nav__item .md-nav__item {\n padding-right: 0.6rem;\n padding-left: 0;\n}\n.md-nav__link {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n}\n.md-nav__link[data-md-state=blur] {\n color: var(--md-default-fg-color--light);\n}\n.md-nav__item .md-nav__link--active {\n color: var(--md-typeset-a-color);\n}\n.md-nav__item .md-nav__link--index [href] {\n width: 100%;\n}\n.md-nav__link:focus, .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-nav__link.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-nav--primary .md-nav__link[for=__toc] {\n display: none;\n}\n.md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n}\n.md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: none;\n}\n.md-nav__link > * {\n display: flex;\n cursor: pointer;\n}\n.md-nav__source {\n display: none;\n}\n@media screen and (max-width: 76.1875em) {\n .md-nav--primary, .md-nav--primary .md-nav {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n .md-nav--primary .md-nav__title,\n.md-nav--primary .md-nav__item {\n font-size: 0.8rem;\n line-height: 1.5;\n }\n .md-nav--primary .md-nav__title {\n position: relative;\n height: 5.6rem;\n padding: 3rem 0.8rem 0.2rem;\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: 2.4rem;\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n position: absolute;\n top: 0.4rem;\n left: 0.4rem;\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon {\n right: 0.4rem;\n left: initial;\n }\n .md-nav--primary .md-nav__title .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0.05rem 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child {\n border-top: 0;\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n .md-nav--primary .md-nav__title .md-logo {\n position: absolute;\n top: 0.2rem;\n left: 0.2rem;\n display: block;\n margin: 0.2rem;\n padding: 0.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-logo {\n right: 0.2rem;\n left: initial;\n }\n .md-nav--primary .md-nav__list {\n flex: 1;\n }\n .md-nav--primary .md-nav__item {\n padding: 0;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link:focus, .md-nav--primary .md-nav__item--active > .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n }\n .md-nav--primary .md-nav__link {\n margin-top: 0;\n padding: 0.6rem 0.8rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon {\n flex-shrink: 0;\n width: 1.2rem;\n height: 1.2rem;\n margin-right: -0.2rem;\n font-size: 1.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon {\n margin-right: 0;\n margin-left: -0.2rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n [dir=rtl] .md-nav--primary .md-nav__icon::after {\n transform: scale(-1);\n }\n .md-nav--primary .md-nav--secondary .md-nav {\n position: static;\n background-color: transparent;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-left: 1.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-right: 1.4rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-left: 2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-right: 2rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 2.6rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 2.6rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 3.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 3.2rem;\n padding-left: initial;\n }\n .md-nav--secondary {\n background-color: transparent;\n }\n .md-nav__toggle ~ .md-nav {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), opacity 125ms 50ms;\n }\n [dir=rtl] .md-nav__toggle ~ .md-nav {\n transform: translateX(-100%);\n }\n .md-nav__toggle:checked ~ .md-nav {\n transform: translateX(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms 125ms;\n }\n .md-nav__toggle:checked ~ .md-nav > .md-nav__list {\n backface-visibility: hidden;\n }\n}\n@media screen and (max-width: 59.9375em) {\n .md-nav--primary .md-nav__link[for=__toc] {\n display: flex;\n }\n .md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--primary .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n .md-nav__source {\n display: block;\n padding: 0 0.2rem;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-nav--integrated .md-nav__link[for=__toc] {\n display: flex;\n }\n .md-nav--integrated .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--integrated .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n}\n@media screen and (min-width: 60em) {\n .md-nav--secondary .md-nav__title[for=__toc] {\n scroll-snap-align: start;\n }\n .md-nav--secondary .md-nav__title .md-nav__icon {\n display: none;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-nav {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n scroll-snap-align: start;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n display: none;\n }\n .md-nav__toggle ~ .md-nav {\n display: none;\n }\n .md-nav__toggle:checked ~ .md-nav, .md-nav__toggle:indeterminate ~ .md-nav {\n display: block;\n }\n .md-nav__item--nested > .md-nav > .md-nav__title {\n display: none;\n }\n .md-nav__item--section {\n display: block;\n margin: 1.25em 0;\n }\n .md-nav__item--section:last-child {\n margin-bottom: 0;\n }\n .md-nav__item--section > .md-nav__link {\n font-weight: 700;\n pointer-events: none;\n }\n .md-nav__item--section > .md-nav__link--index [href] {\n pointer-events: initial;\n }\n .md-nav__item--section > .md-nav__link .md-nav__icon {\n display: none;\n }\n .md-nav__item--section > .md-nav {\n display: block;\n }\n .md-nav__item--section > .md-nav > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n .md-nav__icon {\n float: right;\n width: 0.9rem;\n height: 0.9rem;\n transition: transform 250ms;\n }\n [dir=rtl] .md-nav__icon {\n float: left;\n transform: rotate(180deg);\n }\n .md-nav__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: -0.1rem;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link .md-nav__icon, .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link .md-nav__icon {\n transform: rotate(90deg);\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--nested,\n.md-nav--lifted > .md-nav__title {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active {\n display: block;\n padding: 0;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link {\n padding: 0 0.6rem;\n font-weight: 700;\n pointer-events: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link--index [href] {\n pointer-events: initial;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link .md-nav__icon {\n display: none;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] {\n display: block;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] > .md-nav__list > .md-nav__item {\n padding-right: 0.6rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: 0.05rem solid var(--md-primary-fg-color);\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav > .md-nav__title {\n display: none;\n }\n}\n\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n.md-search {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search {\n padding: 0.2rem 0;\n }\n}\n.no-js .md-search {\n display: none;\n}\n.md-search__overlay {\n z-index: 1;\n opacity: 0;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__overlay {\n position: absolute;\n top: -1rem;\n left: -2.2rem;\n width: 2rem;\n height: 2rem;\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: 1rem;\n transform-origin: center;\n transition: transform 300ms 100ms, opacity 200ms 200ms;\n pointer-events: none;\n }\n [dir=rtl] .md-search__overlay {\n right: -2.2rem;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n opacity: 1;\n transition: transform 400ms, opacity 100ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n cursor: pointer;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [dir=rtl] .md-search__overlay {\n right: 0;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n width: 100%;\n height: 200vh;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@media screen and (max-width: 29.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(45);\n }\n}\n@media screen and (min-width: 30em) and (max-width: 44.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(60);\n }\n}\n@media screen and (min-width: 45em) and (max-width: 59.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(75);\n }\n}\n.md-search__inner {\n backface-visibility: hidden;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__inner {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 2;\n width: 0;\n height: 0;\n overflow: hidden;\n transform: translateX(5%);\n opacity: 0;\n transition: width 0ms 300ms, height 0ms 300ms, transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms 150ms;\n }\n [dir=rtl] .md-search__inner {\n right: 0;\n left: initial;\n transform: translateX(-5%);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 100%;\n height: 100%;\n transform: translateX(0);\n opacity: 1;\n transition: width 0ms 0ms, height 0ms 0ms, transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms 150ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__inner {\n position: relative;\n float: right;\n width: 11.7rem;\n padding: 0.1rem 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n }\n [dir=rtl] .md-search__inner {\n float: left;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 34.4rem;\n }\n}\n.md-search__form {\n position: relative;\n z-index: 2;\n height: 2.4rem;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 0.6rem transparent;\n transition: color 250ms, background-color 250ms;\n}\n@media screen and (min-width: 60em) {\n .md-search__form {\n height: 1.8rem;\n background-color: rgba(0, 0, 0, 0.26);\n border-radius: 0.1rem;\n }\n .md-search__form:hover {\n background-color: rgba(255, 255, 255, 0.12);\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__form {\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem 0.1rem 0 0;\n box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.07);\n}\n.md-search__input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: 100%;\n padding: 0 2.2rem 0 3.6rem;\n font-size: 0.9rem;\n text-overflow: ellipsis;\n background: transparent;\n}\n[dir=rtl] .md-search__input {\n padding: 0 3.6rem 0 2.2rem;\n}\n.md-search__input::placeholder {\n transition: color 250ms;\n}\n.md-search__input ~ .md-search__icon, .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n}\n.md-search__input::-ms-clear {\n display: none;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__input {\n width: 100%;\n height: 2.4rem;\n font-size: 0.9rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__input {\n padding-left: 2.2rem;\n color: inherit;\n font-size: 0.8rem;\n }\n [dir=rtl] .md-search__input {\n padding-right: 2.2rem;\n }\n .md-search__input::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n .md-search__input + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input {\n text-overflow: clip;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle=search]:checked ~ .md-header .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n }\n}\n.md-search__icon {\n display: inline-block;\n width: 1.2rem;\n height: 1.2rem;\n cursor: pointer;\n transition: color 250ms, opacity 250ms;\n}\n.md-search__icon:hover {\n opacity: 0.7;\n}\n.md-search__icon[for=__search] {\n position: absolute;\n top: 0.3rem;\n left: 0.5rem;\n z-index: 2;\n}\n[dir=rtl] .md-search__icon[for=__search] {\n right: 0.5rem;\n left: initial;\n}\n[dir=rtl] .md-search__icon[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[for=__search] {\n top: 0.6rem;\n left: 0.8rem;\n }\n [dir=rtl] .md-search__icon[for=__search] {\n right: 0.8rem;\n left: initial;\n }\n .md-search__icon[for=__search] svg:first-child {\n display: none;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__icon[for=__search] {\n pointer-events: none;\n }\n .md-search__icon[for=__search] svg:last-child {\n display: none;\n }\n}\n.md-search__options {\n position: absolute;\n top: 0.3rem;\n right: 0.5rem;\n z-index: 2;\n pointer-events: none;\n}\n[dir=rtl] .md-search__options {\n right: initial;\n left: 0.5rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__options {\n top: 0.6rem;\n right: 0.8rem;\n }\n [dir=rtl] .md-search__options {\n right: initial;\n left: 0.8rem;\n }\n}\n.md-search__options > * {\n margin-left: 0.2rem;\n color: var(--md-default-fg-color--light);\n transform: scale(0.75);\n opacity: 0;\n transition: transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-search__options > *:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__options > * {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__options > *:hover {\n opacity: 0.7;\n}\n.md-search__suggest {\n position: absolute;\n top: 0;\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n padding: 0 2.2rem 0 3.6rem;\n color: var(--md-default-fg-color--lighter);\n font-size: 0.9rem;\n white-space: nowrap;\n opacity: 0;\n transition: opacity 50ms;\n}\n[dir=rtl] .md-search__suggest {\n padding: 0 3.6rem 0 2.2rem;\n}\n@media screen and (min-width: 60em) {\n .md-search__suggest {\n padding-left: 2.2rem;\n font-size: 0.8rem;\n }\n [dir=rtl] .md-search__suggest {\n padding-right: 2.2rem;\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__suggest {\n opacity: 1;\n transition: opacity 300ms 100ms;\n}\n.md-search__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 0.1rem 0.1rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__output {\n top: 2.4rem;\n bottom: 0;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__output {\n top: 1.9rem;\n opacity: 0;\n transition: opacity 400ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__output {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4);\n opacity: 1;\n }\n}\n.md-search__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n backface-visibility: hidden;\n touch-action: pan-y;\n}\n@media (max-resolution: 1dppx) {\n .md-search__scrollwrap {\n transform: translateZ(0);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search__scrollwrap {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-search__scrollwrap {\n width: 34.4rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__scrollwrap {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__scrollwrap {\n max-height: 75vh;\n }\n .md-search__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n .md-search__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n }\n}\n\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n}\n.md-search-result__meta {\n padding: 0 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.8rem;\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__meta {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__meta {\n padding-right: 2.2rem;\n padding-left: initial;\n }\n}\n.md-search-result__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-search-result__item {\n box-shadow: 0 -0.05rem 0 var(--md-default-fg-color--lightest);\n}\n.md-search-result__item:first-child {\n box-shadow: none;\n}\n.md-search-result__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n}\n.md-search-result__link:focus, .md-search-result__link:hover {\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__link:last-child p:last-child {\n margin-bottom: 0.6rem;\n}\n.md-search-result__more summary {\n display: block;\n padding: 0.75em 0.8rem;\n color: var(--md-typeset-a-color);\n font-size: 0.64rem;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__more summary {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__more summary {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__more summary:focus, .md-search-result__more summary:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__more summary::marker, .md-search-result__more summary::-webkit-details-marker {\n display: none;\n}\n.md-search-result__more summary ~ * > * {\n opacity: 0.65;\n}\n.md-search-result__article {\n position: relative;\n padding: 0 0.8rem;\n overflow: hidden;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__article {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__article {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__article--document .md-search-result__title {\n margin: 0.55rem 0;\n font-weight: 400;\n font-size: 0.8rem;\n line-height: 1.4;\n}\n.md-search-result__icon {\n position: absolute;\n left: 0;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.5rem;\n color: var(--md-default-fg-color--light);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search-result__icon {\n display: none;\n }\n}\n.md-search-result__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-search-result__icon {\n right: 0;\n left: initial;\n}\n[dir=rtl] .md-search-result__icon::after {\n transform: scaleX(-1);\n}\n.md-search-result__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: 0.64rem;\n line-height: 1.6;\n}\n.md-search-result__teaser {\n display: -webkit-box;\n max-height: 2rem;\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n@media screen and (max-width: 44.9375em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n.md-search-result__teaser mark {\n text-decoration: underline;\n background-color: transparent;\n}\n.md-search-result__terms {\n margin: 0.5em 0;\n font-size: 0.64rem;\n font-style: italic;\n}\n.md-search-result mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n}\n\n.md-select {\n position: relative;\n z-index: 1;\n}\n.md-select__inner {\n position: absolute;\n top: calc(100% - 0.2rem);\n left: 50%;\n max-height: 0;\n margin-top: 0.2rem;\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate3d(-50%, 0.3rem, 0);\n opacity: 0;\n transition: transform 250ms 375ms, opacity 250ms 250ms, max-height 0ms 500ms;\n}\n.md-select:focus-within .md-select__inner, .md-select:hover .md-select__inner {\n max-height: 10rem;\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms, max-height 0ms;\n}\n.md-select__inner::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: -0.2rem;\n margin-left: -0.2rem;\n border: 0.2rem solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n}\n.md-select__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: 0.8rem;\n list-style-type: none;\n border-radius: 0.1rem;\n}\n.md-select__item {\n line-height: 1.8rem;\n}\n.md-select__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n outline: none;\n cursor: pointer;\n transition: background-color 250ms, color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-select__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-select__link:focus, .md-select__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-select__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n.md-sidebar {\n position: sticky;\n top: 2.4rem;\n flex-shrink: 0;\n align-self: flex-start;\n width: 12.1rem;\n padding: 1.2rem 0;\n}\n@media print {\n .md-sidebar {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-sidebar--primary {\n position: fixed;\n top: 0;\n left: -12.1rem;\n z-index: 4;\n display: block;\n width: 12.1rem;\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 250ms;\n }\n [dir=rtl] .md-sidebar--primary {\n right: -12.1rem;\n left: initial;\n }\n [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4);\n transform: translateX(12.1rem);\n }\n [dir=rtl] [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n transform: translateX(-12.1rem);\n }\n .md-sidebar--primary .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar {\n height: 0;\n }\n .no-js .md-sidebar {\n height: auto;\n }\n}\n.md-sidebar--secondary {\n display: none;\n order: 2;\n}\n@media screen and (min-width: 60em) {\n .md-sidebar--secondary {\n height: 0;\n }\n .no-js .md-sidebar--secondary {\n height: auto;\n }\n .md-sidebar--secondary:not([hidden]) {\n display: block;\n }\n .md-sidebar--secondary .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n}\n.md-sidebar__scrollwrap {\n margin: 0 0.2rem;\n overflow-y: auto;\n backface-visibility: hidden;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-sidebar__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n\n@media screen and (max-width: 76.1875em) {\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 4;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n opacity: 0;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [data-md-toggle=drawer]:checked ~ .md-overlay {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@keyframes facts {\n 0% {\n height: 0;\n }\n 100% {\n height: 0.65rem;\n }\n}\n@keyframes fact {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n.md-source {\n display: block;\n font-size: 0.65rem;\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n backface-visibility: hidden;\n transition: opacity 250ms;\n}\n.md-source:hover {\n opacity: 0.7;\n}\n.md-source__icon {\n display: inline-block;\n width: 2rem;\n height: 2.4rem;\n vertical-align: middle;\n}\n.md-source__icon svg {\n margin-top: 0.6rem;\n margin-left: 0.6rem;\n}\n[dir=rtl] .md-source__icon svg {\n margin-right: 0.6rem;\n margin-left: initial;\n}\n.md-source__icon + .md-source__repository {\n margin-left: -2rem;\n padding-left: 2rem;\n}\n[dir=rtl] .md-source__icon + .md-source__repository {\n margin-right: -2rem;\n margin-left: initial;\n padding-right: 2rem;\n padding-left: initial;\n}\n.md-source__repository {\n display: inline-block;\n max-width: calc(100% - 1.2rem);\n margin-left: 0.6rem;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n}\n.md-source__facts {\n margin: 0.1rem 0 0;\n padding: 0;\n overflow: hidden;\n font-size: 0.55rem;\n list-style-type: none;\n opacity: 0.75;\n}\n[data-md-state=done] .md-source__facts {\n animation: facts 250ms ease-in;\n}\n.md-source__fact {\n display: inline-block;\n}\n[data-md-state=done] .md-source__fact {\n animation: fact 400ms ease-out;\n}\n.md-source__fact::before {\n display: inline-block;\n width: 0.6rem;\n height: 0.6rem;\n margin-right: 0.1rem;\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-source__fact::before {\n margin-right: initial;\n margin-left: 0.1rem;\n}\n.md-source__fact:nth-child(1n+2)::before {\n margin-left: 0.4rem;\n}\n[dir=rtl] .md-source__fact:nth-child(1n+2)::before {\n margin-right: 0.4rem;\n margin-left: 0.1rem;\n}\n.md-source__fact--version::before {\n mask-image: var(--md-source-version-icon);\n}\n.md-source__fact--stars::before {\n mask-image: var(--md-source-stars-icon);\n}\n.md-source__fact--forks::before {\n mask-image: var(--md-source-forks-icon);\n}\n.md-source__fact--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n}\n\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n}\n@media print {\n .md-tabs {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-tabs {\n display: none;\n }\n}\n.md-tabs[data-md-state=hidden] {\n pointer-events: none;\n}\n.md-tabs__list {\n margin: 0;\n margin-left: 0.2rem;\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n}\n[dir=rtl] .md-tabs__list {\n margin-right: 0.2rem;\n margin-left: initial;\n}\n.md-tabs__item {\n display: inline-block;\n height: 2.4rem;\n padding-right: 0.6rem;\n padding-left: 0.6rem;\n}\n.md-tabs__link {\n display: block;\n margin-top: 0.8rem;\n font-size: 0.7rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n backface-visibility: hidden;\n opacity: 0.7;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms;\n}\n.md-tabs__link--active, .md-tabs__link:focus, .md-tabs__link:hover {\n color: inherit;\n opacity: 1;\n}\n.md-tabs__item:nth-child(2) .md-tabs__link {\n transition-delay: 20ms;\n}\n.md-tabs__item:nth-child(3) .md-tabs__link {\n transition-delay: 40ms;\n}\n.md-tabs__item:nth-child(4) .md-tabs__link {\n transition-delay: 60ms;\n}\n.md-tabs__item:nth-child(5) .md-tabs__link {\n transition-delay: 80ms;\n}\n.md-tabs__item:nth-child(6) .md-tabs__link {\n transition-delay: 100ms;\n}\n.md-tabs__item:nth-child(7) .md-tabs__link {\n transition-delay: 120ms;\n}\n.md-tabs__item:nth-child(8) .md-tabs__link {\n transition-delay: 140ms;\n}\n.md-tabs__item:nth-child(9) .md-tabs__link {\n transition-delay: 160ms;\n}\n.md-tabs__item:nth-child(10) .md-tabs__link {\n transition-delay: 180ms;\n}\n.md-tabs__item:nth-child(11) .md-tabs__link {\n transition-delay: 200ms;\n}\n.md-tabs__item:nth-child(12) .md-tabs__link {\n transition-delay: 220ms;\n}\n.md-tabs__item:nth-child(13) .md-tabs__link {\n transition-delay: 240ms;\n}\n.md-tabs__item:nth-child(14) .md-tabs__link {\n transition-delay: 260ms;\n}\n.md-tabs__item:nth-child(15) .md-tabs__link {\n transition-delay: 280ms;\n}\n.md-tabs__item:nth-child(16) .md-tabs__link {\n transition-delay: 300ms;\n}\n.md-tabs[data-md-state=hidden] .md-tabs__link {\n transform: translateY(50%);\n opacity: 0;\n transition: transform 0ms 100ms, opacity 100ms;\n}\n\n.md-top {\n position: fixed;\n top: 3.2rem;\n z-index: 2;\n margin-left: 50%;\n padding: 0.4rem 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.7rem;\n background-color: var(--md-default-bg-color);\n border-radius: 1.6rem;\n outline: none;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate(-50%, 0);\n transition: color 125ms, background-color 125ms, transform 125ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms;\n}\n@media print {\n .md-top {\n display: none;\n }\n}\n[dir=rtl] .md-top {\n float: left;\n}\n.md-top[data-md-state=hidden] {\n transform: translate(-50%, 0.2rem);\n opacity: 0;\n transition-duration: 0ms;\n pointer-events: none;\n}\n.md-top:focus, .md-top:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n}\n.md-top svg {\n display: inline-block;\n vertical-align: -0.5em;\n}\n\n@keyframes hoverfix {\n 0% {\n pointer-events: none;\n }\n}\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n.md-version {\n flex-shrink: 0;\n height: 2.4rem;\n font-size: 0.8rem;\n}\n.md-version__current {\n position: relative;\n top: 0.05rem;\n margin-right: 0.4rem;\n margin-left: 1.4rem;\n color: inherit;\n outline: none;\n cursor: pointer;\n}\n[dir=rtl] .md-version__current {\n margin-right: 1.4rem;\n margin-left: 0.4rem;\n}\n.md-version__current::after {\n display: inline-block;\n width: 0.4rem;\n height: 0.6rem;\n margin-left: 0.4rem;\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n}\n[dir=rtl] .md-version__current::after {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-version__list {\n position: absolute;\n top: 0.15rem;\n z-index: 1;\n max-height: 0;\n margin: 0.2rem 0.8rem;\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n opacity: 0;\n transition: max-height 0ms 500ms, opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n}\n.md-version:focus-within .md-version__list, .md-version:hover .md-version__list {\n max-height: 10rem;\n opacity: 1;\n transition: max-height 0ms, opacity 250ms;\n}\n@media (pointer: coarse) {\n .md-version:hover .md-version__list {\n animation: hoverfix 250ms forwards;\n }\n .md-version:focus-within .md-version__list {\n animation: none;\n }\n}\n.md-version__item {\n line-height: 1.8rem;\n}\n.md-version__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-version__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-version__link:focus, .md-version__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-version__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n:root {\n --md-admonition-icon--note:\n svg-load(\"material/pencil.svg\");\n --md-admonition-icon--abstract:\n svg-load(\"material/clipboard-text.svg\");\n --md-admonition-icon--info:\n svg-load(\"material/information.svg\");\n --md-admonition-icon--tip:\n svg-load(\"material/fire.svg\");\n --md-admonition-icon--success:\n svg-load(\"material/check-bold.svg\");\n --md-admonition-icon--question:\n svg-load(\"material/help-circle.svg\");\n --md-admonition-icon--warning:\n svg-load(\"material/alert.svg\");\n --md-admonition-icon--failure:\n svg-load(\"material/close-thick.svg\");\n --md-admonition-icon--danger:\n svg-load(\"material/lightning-bolt.svg\");\n --md-admonition-icon--bug:\n svg-load(\"material/bug.svg\");\n --md-admonition-icon--example:\n svg-load(\"material/format-list-numbered.svg\");\n --md-admonition-icon--quote:\n svg-load(\"material/format-quote-close.svg\");\n}\n\n.md-typeset .admonition, .md-typeset details {\n margin: 1.5625em 0;\n padding: 0 0.6rem;\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: 0.64rem;\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: 0.2rem solid #448aff;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.05);\n}\n@media print {\n .md-typeset .admonition, .md-typeset details {\n box-shadow: none;\n }\n}\n[dir=rtl] .md-typeset .admonition, [dir=rtl] .md-typeset details {\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\n.md-typeset .admonition .admonition, .md-typeset details .admonition, .md-typeset .admonition details, .md-typeset details details {\n margin-top: 1em;\n margin-bottom: 1em;\n}\n.md-typeset .admonition .md-typeset__scrollwrap, .md-typeset details .md-typeset__scrollwrap {\n margin: 1em -0.6rem;\n}\n.md-typeset .admonition .md-typeset__table, .md-typeset details .md-typeset__table {\n padding: 0 0.6rem;\n}\n.md-typeset .admonition > .tabbed-set:only-child, .md-typeset details > .tabbed-set:only-child {\n margin-top: 0;\n}\nhtml .md-typeset .admonition > :last-child, html .md-typeset details > :last-child {\n margin-bottom: 0.6rem;\n}\n.md-typeset .admonition-title, .md-typeset summary {\n position: relative;\n margin: 0 -0.6rem 0 -0.8rem;\n padding: 0.4rem 0.6rem 0.4rem 2rem;\n font-weight: 700;\n background-color: rgba(68, 138, 255, 0.1);\n border-left: 0.2rem solid #448aff;\n}\n[dir=rtl] .md-typeset .admonition-title, [dir=rtl] .md-typeset summary {\n margin: 0 -0.8rem 0 -0.6rem;\n padding: 0.4rem 2rem 0.4rem 0.6rem;\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\nhtml .md-typeset .admonition-title:last-child, html .md-typeset summary:last-child {\n margin-bottom: 0;\n}\n.md-typeset .admonition-title::before, .md-typeset summary::before {\n position: absolute;\n left: 0.6rem;\n width: 1rem;\n height: 1rem;\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .admonition-title::before, [dir=rtl] .md-typeset summary::before {\n right: 0.6rem;\n left: initial;\n}\n.md-typeset .admonition-title + .tabbed-set:last-child, .md-typeset summary + .tabbed-set:last-child {\n margin-top: 0;\n}\n\n.md-typeset .admonition.note, .md-typeset details.note {\n border-color: #448aff;\n}\n\n.md-typeset .note > .admonition-title, .md-typeset .note > summary {\n background-color: rgba(68, 138, 255, 0.1);\n border-color: #448aff;\n}\n.md-typeset .note > .admonition-title::before, .md-typeset .note > summary::before {\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.abstract, .md-typeset details.abstract, .md-typeset .admonition.tldr, .md-typeset details.tldr, .md-typeset .admonition.summary, .md-typeset details.summary {\n border-color: #00b0ff;\n}\n\n.md-typeset .abstract > .admonition-title, .md-typeset .abstract > summary, .md-typeset .tldr > .admonition-title, .md-typeset .tldr > summary, .md-typeset .summary > .admonition-title, .md-typeset .summary > summary {\n background-color: rgba(0, 176, 255, 0.1);\n border-color: #00b0ff;\n}\n.md-typeset .abstract > .admonition-title::before, .md-typeset .abstract > summary::before, .md-typeset .tldr > .admonition-title::before, .md-typeset .tldr > summary::before, .md-typeset .summary > .admonition-title::before, .md-typeset .summary > summary::before {\n background-color: #00b0ff;\n mask-image: var(--md-admonition-icon--abstract);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.info, .md-typeset details.info, .md-typeset .admonition.todo, .md-typeset details.todo {\n border-color: #00b8d4;\n}\n\n.md-typeset .info > .admonition-title, .md-typeset .info > summary, .md-typeset .todo > .admonition-title, .md-typeset .todo > summary {\n background-color: rgba(0, 184, 212, 0.1);\n border-color: #00b8d4;\n}\n.md-typeset .info > .admonition-title::before, .md-typeset .info > summary::before, .md-typeset .todo > .admonition-title::before, .md-typeset .todo > summary::before {\n background-color: #00b8d4;\n mask-image: var(--md-admonition-icon--info);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.tip, .md-typeset details.tip, .md-typeset .admonition.important, .md-typeset details.important, .md-typeset .admonition.hint, .md-typeset details.hint {\n border-color: #00bfa5;\n}\n\n.md-typeset .tip > .admonition-title, .md-typeset .tip > summary, .md-typeset .important > .admonition-title, .md-typeset .important > summary, .md-typeset .hint > .admonition-title, .md-typeset .hint > summary {\n background-color: rgba(0, 191, 165, 0.1);\n border-color: #00bfa5;\n}\n.md-typeset .tip > .admonition-title::before, .md-typeset .tip > summary::before, .md-typeset .important > .admonition-title::before, .md-typeset .important > summary::before, .md-typeset .hint > .admonition-title::before, .md-typeset .hint > summary::before {\n background-color: #00bfa5;\n mask-image: var(--md-admonition-icon--tip);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.success, .md-typeset details.success, .md-typeset .admonition.done, .md-typeset details.done, .md-typeset .admonition.check, .md-typeset details.check {\n border-color: #00c853;\n}\n\n.md-typeset .success > .admonition-title, .md-typeset .success > summary, .md-typeset .done > .admonition-title, .md-typeset .done > summary, .md-typeset .check > .admonition-title, .md-typeset .check > summary {\n background-color: rgba(0, 200, 83, 0.1);\n border-color: #00c853;\n}\n.md-typeset .success > .admonition-title::before, .md-typeset .success > summary::before, .md-typeset .done > .admonition-title::before, .md-typeset .done > summary::before, .md-typeset .check > .admonition-title::before, .md-typeset .check > summary::before {\n background-color: #00c853;\n mask-image: var(--md-admonition-icon--success);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.question, .md-typeset details.question, .md-typeset .admonition.faq, .md-typeset details.faq, .md-typeset .admonition.help, .md-typeset details.help {\n border-color: #64dd17;\n}\n\n.md-typeset .question > .admonition-title, .md-typeset .question > summary, .md-typeset .faq > .admonition-title, .md-typeset .faq > summary, .md-typeset .help > .admonition-title, .md-typeset .help > summary {\n background-color: rgba(100, 221, 23, 0.1);\n border-color: #64dd17;\n}\n.md-typeset .question > .admonition-title::before, .md-typeset .question > summary::before, .md-typeset .faq > .admonition-title::before, .md-typeset .faq > summary::before, .md-typeset .help > .admonition-title::before, .md-typeset .help > summary::before {\n background-color: #64dd17;\n mask-image: var(--md-admonition-icon--question);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.warning, .md-typeset details.warning, .md-typeset .admonition.attention, .md-typeset details.attention, .md-typeset .admonition.caution, .md-typeset details.caution {\n border-color: #ff9100;\n}\n\n.md-typeset .warning > .admonition-title, .md-typeset .warning > summary, .md-typeset .attention > .admonition-title, .md-typeset .attention > summary, .md-typeset .caution > .admonition-title, .md-typeset .caution > summary {\n background-color: rgba(255, 145, 0, 0.1);\n border-color: #ff9100;\n}\n.md-typeset .warning > .admonition-title::before, .md-typeset .warning > summary::before, .md-typeset .attention > .admonition-title::before, .md-typeset .attention > summary::before, .md-typeset .caution > .admonition-title::before, .md-typeset .caution > summary::before {\n background-color: #ff9100;\n mask-image: var(--md-admonition-icon--warning);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.failure, .md-typeset details.failure, .md-typeset .admonition.missing, .md-typeset details.missing, .md-typeset .admonition.fail, .md-typeset details.fail {\n border-color: #ff5252;\n}\n\n.md-typeset .failure > .admonition-title, .md-typeset .failure > summary, .md-typeset .missing > .admonition-title, .md-typeset .missing > summary, .md-typeset .fail > .admonition-title, .md-typeset .fail > summary {\n background-color: rgba(255, 82, 82, 0.1);\n border-color: #ff5252;\n}\n.md-typeset .failure > .admonition-title::before, .md-typeset .failure > summary::before, .md-typeset .missing > .admonition-title::before, .md-typeset .missing > summary::before, .md-typeset .fail > .admonition-title::before, .md-typeset .fail > summary::before {\n background-color: #ff5252;\n mask-image: var(--md-admonition-icon--failure);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.danger, .md-typeset details.danger, .md-typeset .admonition.error, .md-typeset details.error {\n border-color: #ff1744;\n}\n\n.md-typeset .danger > .admonition-title, .md-typeset .danger > summary, .md-typeset .error > .admonition-title, .md-typeset .error > summary {\n background-color: rgba(255, 23, 68, 0.1);\n border-color: #ff1744;\n}\n.md-typeset .danger > .admonition-title::before, .md-typeset .danger > summary::before, .md-typeset .error > .admonition-title::before, .md-typeset .error > summary::before {\n background-color: #ff1744;\n mask-image: var(--md-admonition-icon--danger);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.bug, .md-typeset details.bug {\n border-color: #f50057;\n}\n\n.md-typeset .bug > .admonition-title, .md-typeset .bug > summary {\n background-color: rgba(245, 0, 87, 0.1);\n border-color: #f50057;\n}\n.md-typeset .bug > .admonition-title::before, .md-typeset .bug > summary::before {\n background-color: #f50057;\n mask-image: var(--md-admonition-icon--bug);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.example, .md-typeset details.example {\n border-color: #7c4dff;\n}\n\n.md-typeset .example > .admonition-title, .md-typeset .example > summary {\n background-color: rgba(124, 77, 255, 0.1);\n border-color: #7c4dff;\n}\n.md-typeset .example > .admonition-title::before, .md-typeset .example > summary::before {\n background-color: #7c4dff;\n mask-image: var(--md-admonition-icon--example);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.quote, .md-typeset details.quote, .md-typeset .admonition.cite, .md-typeset details.cite {\n border-color: #9e9e9e;\n}\n\n.md-typeset .quote > .admonition-title, .md-typeset .quote > summary, .md-typeset .cite > .admonition-title, .md-typeset .cite > summary {\n background-color: rgba(158, 158, 158, 0.1);\n border-color: #9e9e9e;\n}\n.md-typeset .quote > .admonition-title::before, .md-typeset .quote > summary::before, .md-typeset .cite > .admonition-title::before, .md-typeset .cite > summary::before {\n background-color: #9e9e9e;\n mask-image: var(--md-admonition-icon--quote);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n.md-typeset .footnote {\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n}\n.md-typeset .footnote > ol {\n margin-left: 0;\n}\n.md-typeset .footnote > ol > li {\n transition: color 125ms;\n}\n.md-typeset .footnote > ol > li:target {\n color: var(--md-default-fg-color);\n}\n.md-typeset .footnote > ol > li:hover .footnote-backref, .md-typeset .footnote > ol > li:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n}\n.md-typeset .footnote > ol > li > :first-child {\n margin-top: 0;\n}\n.md-typeset .footnote-ref {\n font-weight: 700;\n font-size: 0.75em;\n}\nhtml .md-typeset .footnote-ref {\n outline-offset: 0.1rem;\n}\n.md-typeset .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(0.25rem);\n opacity: 0;\n transition: color 250ms, transform 250ms 250ms, opacity 125ms 250ms;\n}\n@media print {\n .md-typeset .footnote-backref {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n}\n[dir=rtl] .md-typeset .footnote-backref {\n transform: translateX(-0.25rem);\n}\n.md-typeset .footnote-backref:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset .footnote-backref::before {\n display: inline-block;\n width: 0.8rem;\n height: 0.8rem;\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .footnote-backref::before svg {\n transform: scaleX(-1);\n}\n.md-typeset [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n}\n.md-typeset [id^=\"fnref:\"]:target > .footnote-ref {\n outline: auto;\n}\n.md-typeset [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n}\n\n.md-typeset .headerlink {\n display: inline-block;\n margin-left: 0.5rem;\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition: color 250ms, opacity 125ms;\n}\n@media print {\n .md-typeset .headerlink {\n display: none;\n }\n}\n[dir=rtl] .md-typeset .headerlink {\n margin-right: 0.5rem;\n margin-left: initial;\n}\n.md-typeset :hover > .headerlink,\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus {\n opacity: 1;\n transition: color 250ms, opacity 125ms;\n}\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus,\n.md-typeset .headerlink:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset :target {\n scroll-margin-top: 3.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset :target {\n scroll-margin-top: 6rem;\n }\n}\n.md-typeset h1:target,\n.md-typeset h2:target,\n.md-typeset h3:target {\n scroll-margin-top: initial;\n}\n.md-typeset h1:target::before,\n.md-typeset h2:target::before,\n.md-typeset h3:target::before {\n display: block;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h1:target,\n.md-header--lifted ~ .md-container .md-typeset h2:target,\n.md-header--lifted ~ .md-container .md-typeset h3:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h1:target::before,\n.md-header--lifted ~ .md-container .md-typeset h2:target::before,\n.md-header--lifted ~ .md-container .md-typeset h3:target::before {\n margin-top: -5.8rem;\n padding-top: 5.8rem;\n }\n}\n.md-typeset h4:target {\n scroll-margin-top: initial;\n}\n.md-typeset h4:target::before {\n display: block;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h4:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h4:target::before {\n margin-top: -5.85rem;\n padding-top: 5.85rem;\n }\n}\n.md-typeset h5:target,\n.md-typeset h6:target {\n scroll-margin-top: initial;\n}\n.md-typeset h5:target::before,\n.md-typeset h6:target::before {\n display: block;\n margin-top: -3.6rem;\n padding-top: 3.6rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h5:target,\n.md-header--lifted ~ .md-container .md-typeset h6:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h5:target::before,\n.md-header--lifted ~ .md-container .md-typeset h6:target::before {\n margin-top: -6rem;\n padding-top: 6rem;\n }\n}\n\n.md-typeset div.arithmatex {\n overflow: auto;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset div.arithmatex {\n margin: 0 -0.8rem;\n }\n}\n.md-typeset div.arithmatex > * {\n width: min-content;\n margin: 1em auto !important;\n padding: 0 0.8rem;\n touch-action: auto;\n}\n\n.md-typeset del.critic,\n.md-typeset ins.critic,\n.md-typeset .critic.comment {\n box-decoration-break: clone;\n}\n.md-typeset del.critic {\n background-color: var(--md-typeset-del-color);\n}\n.md-typeset ins.critic {\n background-color: var(--md-typeset-ins-color);\n}\n.md-typeset .critic.comment {\n color: var(--md-code-hl-comment-color);\n}\n.md-typeset .critic.comment::before {\n content: \"/* \";\n}\n.md-typeset .critic.comment::after {\n content: \" */\";\n}\n.md-typeset .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: 0.8rem;\n padding-left: 0.8rem;\n overflow: auto;\n box-shadow: none;\n}\n.md-typeset .critic.block > :first-child {\n margin-top: 0.5em;\n}\n.md-typeset .critic.block > :last-child {\n margin-bottom: 0.5em;\n}\n\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n.md-typeset details {\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n}\n.md-typeset details[open] > summary::after {\n transform: rotate(90deg);\n}\n.md-typeset details:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n}\n.md-typeset details:not([open]) > summary {\n border-radius: 0.1rem;\n}\n.md-typeset details::after {\n display: table;\n content: \"\";\n}\n.md-typeset summary {\n display: block;\n min-height: 1rem;\n padding: 0.4rem 1.8rem 0.4rem 2rem;\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n cursor: pointer;\n}\n[dir=rtl] .md-typeset summary {\n padding: 0.4rem 2.2rem 0.4rem 1.8rem;\n}\n.md-typeset summary.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-typeset summary:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset summary::after {\n position: absolute;\n top: 0.4rem;\n right: 0.4rem;\n width: 1rem;\n height: 1rem;\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n}\n[dir=rtl] .md-typeset summary::after {\n right: initial;\n left: 0.4rem;\n transform: rotate(180deg);\n}\n.md-typeset summary::marker, .md-typeset summary::-webkit-details-marker {\n display: none;\n}\n\n.md-typeset .emojione,\n.md-typeset .twemoji,\n.md-typeset .gemoji {\n display: inline-flex;\n height: 1.125em;\n vertical-align: text-top;\n}\n.md-typeset .emojione svg,\n.md-typeset .twemoji svg,\n.md-typeset .gemoji svg {\n width: 1.125em;\n max-height: 100%;\n fill: currentColor;\n}\n\n.highlight .o,\n.highlight .ow {\n color: var(--md-code-hl-operator-color);\n}\n.highlight .p {\n color: var(--md-code-hl-punctuation-color);\n}\n.highlight .cpf,\n.highlight .l,\n.highlight .s,\n.highlight .sb,\n.highlight .sc,\n.highlight .s2,\n.highlight .si,\n.highlight .s1,\n.highlight .ss {\n color: var(--md-code-hl-string-color);\n}\n.highlight .cp,\n.highlight .se,\n.highlight .sh,\n.highlight .sr,\n.highlight .sx {\n color: var(--md-code-hl-special-color);\n}\n.highlight .m,\n.highlight .mb,\n.highlight .mf,\n.highlight .mh,\n.highlight .mi,\n.highlight .il,\n.highlight .mo {\n color: var(--md-code-hl-number-color);\n}\n.highlight .k,\n.highlight .kd,\n.highlight .kn,\n.highlight .kp,\n.highlight .kr,\n.highlight .kt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .kc,\n.highlight .n {\n color: var(--md-code-hl-name-color);\n}\n.highlight .no,\n.highlight .nb,\n.highlight .bp {\n color: var(--md-code-hl-constant-color);\n}\n.highlight .nc,\n.highlight .ne,\n.highlight .nf,\n.highlight .nn {\n color: var(--md-code-hl-function-color);\n}\n.highlight .nd,\n.highlight .ni,\n.highlight .nl,\n.highlight .nt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .c,\n.highlight .cm,\n.highlight .c1,\n.highlight .ch,\n.highlight .cs,\n.highlight .sd {\n color: var(--md-code-hl-comment-color);\n}\n.highlight .na,\n.highlight .nv,\n.highlight .vc,\n.highlight .vg,\n.highlight .vi {\n color: var(--md-code-hl-variable-color);\n}\n.highlight .ge,\n.highlight .gr,\n.highlight .gh,\n.highlight .go,\n.highlight .gp,\n.highlight .gs,\n.highlight .gu,\n.highlight .gt {\n color: var(--md-code-hl-generic-color);\n}\n.highlight .gd,\n.highlight .gi {\n margin: 0 -0.125em;\n padding: 0 0.125em;\n border-radius: 0.1rem;\n}\n.highlight .gd {\n background-color: var(--md-typeset-del-color);\n}\n.highlight .gi {\n background-color: var(--md-typeset-ins-color);\n}\n.highlight .hll {\n display: block;\n margin: 0 -1.1764705882em;\n padding: 0 1.1764705882em;\n background-color: var(--md-code-hl-color);\n}\n.highlight [data-linenos]::before {\n position: sticky;\n left: -1.1764705882em;\n float: left;\n margin-right: 1.1764705882em;\n margin-left: -1.1764705882em;\n padding-left: 1.1764705882em;\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n}\n\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n}\n.highlighttable tbody,\n.highlighttable td {\n display: block;\n padding: 0;\n}\n.highlighttable tr {\n display: flex;\n}\n.highlighttable pre {\n margin: 0;\n}\n.highlighttable .linenos {\n padding: 0.7720588235em 1.1764705882em;\n padding-right: 0;\n font-size: 0.85em;\n background-color: var(--md-code-bg-color);\n user-select: none;\n}\n.highlighttable .linenodiv {\n padding-right: 0.5882352941em;\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n}\n.highlighttable .linenodiv pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n}\n.highlighttable .code {\n flex: 1;\n overflow: hidden;\n}\n\n.md-typeset .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: 0.1rem;\n}\n.md-typeset .highlighttable code {\n border-radius: 0;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > .highlight {\n margin: 1em -0.8rem;\n }\n .md-typeset > .highlight .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n .md-typeset > .highlight code {\n border-radius: 0;\n }\n .md-typeset > .highlighttable {\n margin: 1em -0.8rem;\n border-radius: 0;\n }\n .md-typeset > .highlighttable .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n}\n\n.md-typeset .keys kbd::before,\n.md-typeset .keys kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n}\n.md-typeset .keys span {\n padding: 0 0.2em;\n color: var(--md-default-fg-color--light);\n}\n.md-typeset .keys .key-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-left-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-right-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-left-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-right-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-left-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-right-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-left-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-right-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-left-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-right-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-left-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-right-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-left-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-right-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-left-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-right-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-arrow-down::before {\n padding-right: 0.4em;\n content: \"↓\";\n}\n.md-typeset .keys .key-arrow-left::before {\n padding-right: 0.4em;\n content: \"←\";\n}\n.md-typeset .keys .key-arrow-right::before {\n padding-right: 0.4em;\n content: \"→\";\n}\n.md-typeset .keys .key-arrow-up::before {\n padding-right: 0.4em;\n content: \"↑\";\n}\n.md-typeset .keys .key-backspace::before {\n padding-right: 0.4em;\n content: \"⌫\";\n}\n.md-typeset .keys .key-backtab::before {\n padding-right: 0.4em;\n content: \"⇤\";\n}\n.md-typeset .keys .key-caps-lock::before {\n padding-right: 0.4em;\n content: \"⇪\";\n}\n.md-typeset .keys .key-clear::before {\n padding-right: 0.4em;\n content: \"⌧\";\n}\n.md-typeset .keys .key-context-menu::before {\n padding-right: 0.4em;\n content: \"☰\";\n}\n.md-typeset .keys .key-delete::before {\n padding-right: 0.4em;\n content: \"⌦\";\n}\n.md-typeset .keys .key-eject::before {\n padding-right: 0.4em;\n content: \"⏏\";\n}\n.md-typeset .keys .key-end::before {\n padding-right: 0.4em;\n content: \"⤓\";\n}\n.md-typeset .keys .key-escape::before {\n padding-right: 0.4em;\n content: \"⎋\";\n}\n.md-typeset .keys .key-home::before {\n padding-right: 0.4em;\n content: \"⤒\";\n}\n.md-typeset .keys .key-insert::before {\n padding-right: 0.4em;\n content: \"⎀\";\n}\n.md-typeset .keys .key-page-down::before {\n padding-right: 0.4em;\n content: \"⇟\";\n}\n.md-typeset .keys .key-page-up::before {\n padding-right: 0.4em;\n content: \"⇞\";\n}\n.md-typeset .keys .key-print-screen::before {\n padding-right: 0.4em;\n content: \"⎙\";\n}\n.md-typeset .keys .key-tab::after {\n padding-left: 0.4em;\n content: \"⇥\";\n}\n.md-typeset .keys .key-num-enter::after {\n padding-left: 0.4em;\n content: \"⌤\";\n}\n.md-typeset .keys .key-enter::after {\n padding-left: 0.4em;\n content: \"⏎\";\n}\n\n.md-typeset .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 -0.05rem var(--md-default-fg-color--lightest);\n}\n@media print {\n .md-typeset .tabbed-content {\n display: block;\n order: initial;\n }\n}\n.md-typeset .tabbed-content > pre:only-child,\n.md-typeset .tabbed-content > .highlight:only-child pre,\n.md-typeset .tabbed-content > .highlighttable:only-child {\n margin: 0;\n}\n.md-typeset .tabbed-content > pre:only-child > code,\n.md-typeset .tabbed-content > .highlight:only-child pre > code,\n.md-typeset .tabbed-content > .highlighttable:only-child > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .tabbed-content > .tabbed-set {\n margin: 0;\n}\n.md-typeset .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: 0.1rem;\n}\n.md-typeset .tabbed-set > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-typeset .tabbed-set > input:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:checked + label + .tabbed-content {\n display: block;\n}\n.md-typeset .tabbed-set > input:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset .tabbed-set > label {\n z-index: 1;\n width: auto;\n padding: 0.9375em 1.25em 0.78125em;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.64rem;\n border-bottom: 0.1rem solid transparent;\n cursor: pointer;\n transition: color 250ms;\n}\n.md-typeset .tabbed-set > label:hover {\n color: var(--md-accent-fg-color);\n}\n\n@media screen {\n .md-typeset .tabbed-alternate input:nth-child(1):checked ~ .tabbed-labels > :nth-child(1), .md-typeset .tabbed-alternate input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), .md-typeset .tabbed-alternate input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), .md-typeset .tabbed-alternate input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), .md-typeset .tabbed-alternate input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), .md-typeset .tabbed-alternate input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), .md-typeset .tabbed-alternate input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), .md-typeset .tabbed-alternate input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), .md-typeset .tabbed-alternate input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9), .md-typeset .tabbed-alternate input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10) {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n}\n\n.md-typeset .tabbed-alternate input:nth-child(1).focus-visible ~ .tabbed-labels > :nth-child(1), .md-typeset .tabbed-alternate input:nth-child(2).focus-visible ~ .tabbed-labels > :nth-child(2), .md-typeset .tabbed-alternate input:nth-child(3).focus-visible ~ .tabbed-labels > :nth-child(3), .md-typeset .tabbed-alternate input:nth-child(4).focus-visible ~ .tabbed-labels > :nth-child(4), .md-typeset .tabbed-alternate input:nth-child(5).focus-visible ~ .tabbed-labels > :nth-child(5), .md-typeset .tabbed-alternate input:nth-child(6).focus-visible ~ .tabbed-labels > :nth-child(6), .md-typeset .tabbed-alternate input:nth-child(7).focus-visible ~ .tabbed-labels > :nth-child(7), .md-typeset .tabbed-alternate input:nth-child(8).focus-visible ~ .tabbed-labels > :nth-child(8), .md-typeset .tabbed-alternate input:nth-child(9).focus-visible ~ .tabbed-labels > :nth-child(9), .md-typeset .tabbed-alternate input:nth-child(10).focus-visible ~ .tabbed-labels > :nth-child(10) {\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n.md-typeset .tabbed-alternate input:nth-child(1):checked ~ .tabbed-content > :nth-child(1), .md-typeset .tabbed-alternate input:nth-child(2):checked ~ .tabbed-content > :nth-child(2), .md-typeset .tabbed-alternate input:nth-child(3):checked ~ .tabbed-content > :nth-child(3), .md-typeset .tabbed-alternate input:nth-child(4):checked ~ .tabbed-content > :nth-child(4), .md-typeset .tabbed-alternate input:nth-child(5):checked ~ .tabbed-content > :nth-child(5), .md-typeset .tabbed-alternate input:nth-child(6):checked ~ .tabbed-content > :nth-child(6), .md-typeset .tabbed-alternate input:nth-child(7):checked ~ .tabbed-content > :nth-child(7), .md-typeset .tabbed-alternate input:nth-child(8):checked ~ .tabbed-content > :nth-child(8), .md-typeset .tabbed-alternate input:nth-child(9):checked ~ .tabbed-content > :nth-child(9), .md-typeset .tabbed-alternate input:nth-child(10):checked ~ .tabbed-content > :nth-child(10) {\n display: block;\n}\n\n.md-typeset .tabbed-labels {\n display: flex;\n max-width: 100vw;\n overflow: auto;\n box-shadow: 0 -0.05rem var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: x proximity;\n -ms-overflow-style: none;\n scrollbar-width: none;\n}\n@media print {\n .md-typeset .tabbed-labels {\n display: contents;\n }\n}\n.md-typeset .tabbed-labels::-webkit-scrollbar {\n display: none;\n}\n.md-typeset .tabbed-labels > label {\n z-index: 1;\n width: auto;\n padding: 0.9375em 1.25em 0.78125em;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.64rem;\n white-space: nowrap;\n border-bottom: 0.1rem solid transparent;\n scroll-snap-align: start;\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n cursor: pointer;\n transition: background-color 250ms, color 250ms;\n}\n@media print {\n .md-typeset .tabbed-labels > label:nth-child(1) {\n order: 1;\n }\n .md-typeset .tabbed-labels > label:nth-child(2) {\n order: 2;\n }\n .md-typeset .tabbed-labels > label:nth-child(3) {\n order: 3;\n }\n .md-typeset .tabbed-labels > label:nth-child(4) {\n order: 4;\n }\n .md-typeset .tabbed-labels > label:nth-child(5) {\n order: 5;\n }\n .md-typeset .tabbed-labels > label:nth-child(6) {\n order: 6;\n }\n .md-typeset .tabbed-labels > label:nth-child(7) {\n order: 7;\n }\n .md-typeset .tabbed-labels > label:nth-child(8) {\n order: 8;\n }\n .md-typeset .tabbed-labels > label:nth-child(9) {\n order: 9;\n }\n .md-typeset .tabbed-labels > label:nth-child(10) {\n order: 10;\n }\n}\n.md-typeset .tabbed-labels > label:hover {\n color: var(--md-accent-fg-color);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > .tabbed-alternate .tabbed-labels {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n scroll-padding: 0 0.8rem;\n }\n}\n.md-typeset .tabbed-alternate {\n flex-direction: column;\n}\n.md-typeset .tabbed-alternate .tabbed-content {\n display: initial;\n order: initial;\n width: 100%;\n box-shadow: initial;\n}\n@media print {\n .md-typeset .tabbed-alternate .tabbed-content {\n display: contents;\n }\n}\n.md-typeset .tabbed-alternate .tabbed-block {\n display: none;\n}\n@media print {\n .md-typeset .tabbed-alternate .tabbed-block {\n display: block;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(1) {\n order: 1;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(2) {\n order: 2;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(3) {\n order: 3;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(4) {\n order: 4;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(5) {\n order: 5;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(6) {\n order: 6;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(7) {\n order: 7;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(8) {\n order: 8;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(9) {\n order: 9;\n }\n .md-typeset .tabbed-alternate .tabbed-block:nth-child(10) {\n order: 10;\n }\n}\n.md-typeset .tabbed-alternate .tabbed-block > pre:only-child,\n.md-typeset .tabbed-alternate .tabbed-block > .highlight:only-child pre,\n.md-typeset .tabbed-alternate .tabbed-block > .highlighttable:only-child {\n margin: 0;\n}\n.md-typeset .tabbed-alternate .tabbed-block > pre:only-child > code,\n.md-typeset .tabbed-alternate .tabbed-block > .highlight:only-child pre > code,\n.md-typeset .tabbed-alternate .tabbed-block > .highlighttable:only-child > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .tabbed-alternate .tabbed-block > .tabbed-set {\n margin: 0;\n}\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n.md-typeset .task-list-item {\n position: relative;\n list-style-type: none;\n}\n.md-typeset .task-list-item [type=checkbox] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n}\n[dir=rtl] .md-typeset .task-list-item [type=checkbox] {\n right: -2em;\n left: initial;\n}\n.md-typeset .task-list-control [type=checkbox] {\n z-index: -1;\n opacity: 0;\n}\n.md-typeset .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: -1.5em;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .task-list-indicator::before {\n right: -1.5em;\n left: initial;\n}\n.md-typeset [type=checkbox]:checked + .task-list-indicator::before {\n background-color: #00e676;\n mask-image: var(--md-tasklist-icon--checked);\n}\n\n@media screen and (min-width: 45em) {\n .md-typeset .inline {\n float: left;\n width: 11.7rem;\n margin-top: 0;\n margin-right: 0.8rem;\n margin-bottom: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n .md-typeset .inline.end {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline.end {\n float: left;\n margin-right: 0.8rem;\n margin-left: 0;\n }\n}\n\n/*# sourceMappingURL=main.css.map */","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Enforce correct box model and prevent adjustments of font size after\n// orientation changes in IE and iOS\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n// All elements shall inherit the document default\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n// Remove margin in all browsers\nbody {\n margin: 0;\n}\n\n// Reset tap outlines on iOS and Android\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\n// Reset link styles\na {\n color: inherit;\n text-decoration: none;\n}\n\n// Normalize horizontal separator styles\nhr {\n display: block;\n box-sizing: content-box;\n height: px2rem(1px);\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\n// Normalize font-size in all browsers\nsmall {\n font-size: 80%;\n}\n\n// Prevent subscript and superscript from affecting line-height\nsub,\nsup {\n line-height: 1em;\n}\n\n// Remove border on image\nimg {\n border-style: none;\n}\n\n// Reset table styles\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\n// Reset table cell styles\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\n// Reset button styles\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\n// Reset input styles\ninput {\n border: 0;\n outline: none;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Color definitions\n:root {\n\n // Default color shades\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n\n // Primary color shades\n --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-400)}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Accent color shades\n --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Light theme (default)\n > * {\n\n // Code color shades\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n\n // Typeset color shades\n --md-typeset-color: var(--md-default-fg-color);\n\n // Typeset `a` color shades\n --md-typeset-a-color: var(--md-primary-fg-color);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n\n // Typeset `del` and `ins` color shades\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n\n // Typeset `table` color shades\n --md-typeset-table-color: hsla(0, 0%, 0%, 0.12);\n\n // Admonition color shades\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n\n // Footer color shades\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon\n.md-icon {\n\n // SVG defaults\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: font definitions\n// ----------------------------------------------------------------------------\n\n// Enable font-smoothing in Webkit and FF\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Define default fonts\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family:\n var(--md-text-font-family, _),\n -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\n// Define monospaced fonts\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family:\n var(--md-code-font-family, _),\n SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: typesetted content\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-typeset-table-sort-icon: svg-load(\"material/sort.svg\");\n --md-typeset-table-sort-icon--asc: svg-load(\"material/sort-ascending.svg\");\n --md-typeset-table-sort-icon--desc: svg-load(\"material/sort-descending.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Content that is typeset - if possible, all margins, paddings and font sizes\n// should be set in ems, so nested blocks (e.g. admonitions) render correctly.\n.md-typeset {\n font-size: px2rem(16px);\n line-height: 1.6;\n color-adjust: exact;\n\n // [print]: We'll use a smaller `font-size` for printing, so code examples\n // don't break too early, and `16px` looks too big anyway.\n @media print {\n font-size: px2rem(13.6px);\n }\n\n // Default spacing\n ul,\n ol,\n dl,\n figure,\n blockquote,\n pre {\n margin: 1em 0;\n }\n\n // Headline on level 1\n h1 {\n margin: 0 0 px2em(40px, 32px);\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: px2em(32px);\n line-height: 1.3;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 2\n h2 {\n margin: px2em(40px, 25px) 0 px2em(16px, 25px);\n font-weight: 300;\n font-size: px2em(25px);\n line-height: 1.4;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3\n h3 {\n margin: px2em(32px, 20px) 0 px2em(16px, 20px);\n font-weight: 400;\n font-size: px2em(20px);\n line-height: 1.5;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3 following level 2\n h2 + h3 {\n margin-top: px2em(16px, 20px);\n }\n\n // Headline on level 4\n h4 {\n margin: px2em(16px) 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5-6\n h5,\n h6 {\n margin: px2em(16px, 12.8px) 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2em(12.8px);\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5\n h5 {\n text-transform: uppercase;\n }\n\n // Horizontal separator\n hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Text link\n a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n\n // Also enable color transition on pseudo elements\n &,\n &::before {\n transition: color 125ms;\n }\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n }\n\n // Code block\n code,\n pre,\n kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n\n // [print]: Wrap text and hide scollbars\n @media print {\n white-space: pre-wrap;\n }\n }\n\n // Inline code block\n code {\n padding: 0 px2em(4px, 13.6px);\n font-size: px2em(13.6px);\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n box-decoration-break: clone;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Code block in headline\n h1 code,\n h2 code,\n h3 code,\n h4 code,\n h5 code,\n h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n }\n\n // Ensure link color in code blocks\n a code {\n color: currentColor;\n }\n\n // Unformatted content\n pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n\n // Code block\n > code {\n display: block;\n margin: 0;\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Code block on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Unformatted text\n > pre {\n margin: 1em px2rem(-16px);\n\n // Code block\n code {\n border-radius: 0;\n }\n }\n }\n\n // Keyboard key\n kbd {\n display: inline-block;\n padding: 0 px2em(8px, 12px);\n color: var(--md-default-fg-color);\n font-size: px2em(12px);\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(2px) 0 px2rem(1px) var(--md-typeset-kbd-border-color),\n 0 px2rem(2px) 0 var(--md-typeset-kbd-border-color),\n 0 px2rem(-2px) px2rem(4px) var(--md-typeset-kbd-accent-color) inset;\n }\n\n // Text highlighting marker\n mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n }\n\n // Abbreviation\n abbr {\n text-decoration: none;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--light);\n cursor: help;\n\n // Show tooltip for touch devices\n @media (hover: none) {\n position: relative;\n\n // Tooltip\n &[title]:focus::after,\n &[title]:hover::after {\n @include z-depth(2);\n\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: px2rem(4px) px2rem(6px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n content: attr(title);\n }\n }\n }\n\n // Small text\n small {\n opacity: 0.75;\n }\n\n // Superscript and subscript\n sup,\n sub {\n margin-left: px2em(1px, 12.8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(1px, 12.8px);\n margin-left: initial;\n }\n }\n\n // Blockquotes, possibly nested\n blockquote {\n padding-left: px2rem(12px);\n color: var(--md-default-fg-color--light);\n border-left: px2rem(4px) solid var(--md-default-fg-color--lighter);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: initial;\n border-right: px2rem(4px) solid var(--md-default-fg-color--lighter);\n border-left: initial;\n }\n }\n\n // Unordered list\n ul {\n list-style-type: disc;\n }\n\n // Unordered and ordered list\n ul,\n ol {\n display: flow-root;\n margin-left: px2em(10px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n\n // Nested ordered list\n ol {\n list-style-type: lower-alpha;\n\n // Triply nested ordered list\n ol {\n list-style-type: lower-roman;\n }\n }\n\n // List element\n li {\n margin-bottom: 0.5em;\n margin-left: px2em(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(20px);\n margin-left: initial;\n }\n\n // Adjust spacing\n p,\n blockquote {\n margin: 0.5em 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Nested list\n ul,\n ol {\n margin: 0.5em 0 0.5em px2em(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n }\n }\n }\n\n // Definition list\n dd {\n margin: 1em 0 1.5em px2em(30px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(30px);\n margin-left: initial;\n }\n }\n\n // Image or icon\n img,\n svg {\n max-width: 100%;\n height: auto;\n\n // Adjust spacing when left-aligned\n &[align=\"left\"] {\n margin: 1em;\n margin-left: 0;\n }\n\n // Adjust spacing when right-aligned\n &[align=\"right\"] {\n margin: 1em;\n margin-right: 0;\n }\n\n // Adjust spacing when sole children\n &[align]:only-child {\n margin-top: 0;\n }\n }\n\n // Figure\n figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n\n // Figure images\n img {\n display: block;\n }\n }\n\n // Figure caption\n figcaption {\n max-width: px2rem(480px);\n margin: 1em auto 2em;\n font-style: italic;\n }\n\n // Limit width to container\n iframe {\n max-width: 100%;\n }\n\n // Data table\n table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: px2rem(12.8px);\n background-color: var(--md-default-bg-color);\n border: px2rem(1px) solid var(--md-typeset-table-color);\n border-radius: px2rem(2px);\n touch-action: auto;\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: table;\n }\n\n // Due to margin collapse because of the necessary inline-block hack, we\n // cannot increase the bottom margin on the table, so we just increase the\n // top margin on the following element\n + * {\n margin-top: 1.5em;\n }\n\n // Elements in table heading and cell\n th > *,\n td > * {\n\n // Adjust spacing on first child\n &:first-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Table heading and cell\n th:not([align]),\n td:not([align]) {\n text-align: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n text-align: right;\n }\n }\n\n // Table heading\n th {\n min-width: px2rem(100px);\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n font-weight: 700;\n vertical-align: top;\n\n // Links in table headings\n a {\n color: inherit;\n }\n }\n\n // Table cell\n td {\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n vertical-align: top;\n border-top: px2rem(1px) solid var(--md-typeset-table-color);\n }\n\n // Table body row\n tbody tr {\n transition: background-color 125ms;\n\n // Table row on hover\n &:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 px2rem(1px) 0 var(--md-default-bg-color) inset;\n }\n }\n\n // Text link in table\n a {\n word-break: normal;\n }\n }\n\n // Sortable table\n table th[role=\"columnheader\"] {\n cursor: pointer;\n\n // Sort icon\n &::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: text-bottom;\n mask-image: var(--md-typeset-table-sort-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transition: background-color 125ms;\n content: \"\";\n }\n\n // Show sort icon on hover\n &:hover::after {\n background-color: var(--md-default-fg-color--lighter);\n }\n\n // Sort ascending icon\n &[aria-sort=\"ascending\"]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--asc);\n }\n\n // Sort descending icon\n &[aria-sort=\"descending\"]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--desc);\n }\n }\n\n // Data table scroll wrapper\n &__scrollwrap {\n margin: 1em px2rem(-16px);\n overflow-x: auto;\n touch-action: auto;\n }\n\n // Data table wrapper\n &__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 px2rem(16px);\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: block;\n }\n\n // Data table\n html & table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","//\n// Name: Material Shadows\n// Description: Mixins for Material Design Shadows.\n// Version: 3.0.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-shadows\n//\n// twitter: @mrmlnc\n//\n// ------------------------------------\n\n\n// Mixins\n// ------------------------------------\n\n@mixin z-depth-transition() {\n transition: box-shadow .28s cubic-bezier(.4, 0, .2, 1);\n}\n\n@mixin z-depth-focus() {\n box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);\n}\n\n@mixin z-depth-2dp() {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),\n 0 1px 5px 0 rgba(0, 0, 0, .12),\n 0 3px 1px -2px rgba(0, 0, 0, .2);\n}\n\n@mixin z-depth-3dp() {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .14),\n 0 1px 8px 0 rgba(0, 0, 0, .12),\n 0 3px 3px -2px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-4dp() {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14),\n 0 1px 10px 0 rgba(0, 0, 0, .12),\n 0 2px 4px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-6dp() {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .14),\n 0 1px 18px 0 rgba(0, 0, 0, .12),\n 0 3px 5px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-8dp() {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14),\n 0 3px 14px 2px rgba(0, 0, 0, .12),\n 0 5px 5px -3px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-16dp() {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14),\n 0 6px 30px 5px rgba(0, 0, 0, .12),\n 0 8px 10px -5px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-24dp() {\n box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14),\n 0 24px 38px 3px rgba(0, 0, 0, .12),\n 0 11px 15px -7px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth($dp: 2) {\n @if $dp == 2 {\n @include z-depth-2dp();\n } @else if $dp == 3 {\n @include z-depth-3dp();\n } @else if $dp == 4 {\n @include z-depth-4dp();\n } @else if $dp == 6 {\n @include z-depth-6dp();\n } @else if $dp == 8 {\n @include z-depth-8dp();\n } @else if $dp == 16 {\n @include z-depth-16dp();\n } @else if $dp == 24 {\n @include z-depth-24dp();\n }\n}\n\n\n// Class generator\n// ------------------------------------\n\n@mixin z-depth-classes($transition: false, $focus: false) {\n @if $transition == true {\n &-transition {\n @include z-depth-transition();\n }\n }\n\n @if $focus == true {\n &-focus {\n @include z-depth-focus();\n }\n }\n\n // The available values for the shadow depth\n @each $depth in 2, 3, 4, 6, 8, 16, 24 {\n &-#{$depth}dp {\n @include z-depth($depth);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: base grid and containers\n// ----------------------------------------------------------------------------\n\n// Stretch container to viewport and set base `font-size`\nhtml {\n height: 100%;\n overflow-x: hidden;\n // Hack: normally, we would set the base `font-size` to `62.5%`, so we can\n // base all calculations on `10px`, but Chromium and Chrome define a minimal\n // `font-size` of `12px` if the system language is set to Chinese. For this\n // reason we just double the `font-size` and set it to `20px`.\n //\n // See https://github.com/squidfunk/mkdocs-material/issues/911\n font-size: 125%;\n\n // [screen medium +]: Set base `font-size` to `11px`\n @include break-from-device(screen medium) {\n font-size: 137.5%;\n }\n\n // [screen large +]: Set base `font-size` to `12px`\n @include break-from-device(screen large) {\n font-size: 150%;\n }\n}\n\n// Stretch body to container - flexbox is used, so the footer will always be\n// aligned to the bottom of the viewport\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n // Hack: reset `font-size` to `10px`, so the spacing for all inline elements\n // is correct again. Otherwise the spacing would be based on `20px`.\n font-size: px2rem(10px);\n background-color: var(--md-default-bg-color);\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n\n // Body in locked state\n &[data-md-state=\"lock\"] {\n\n // [tablet portrait -]: Omit scroll bubbling\n @include break-to-device(tablet portrait) {\n position: fixed;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Grid container - this class is applied to wrapper elements within the\n// header, content area and footer, and makes sure that their width is limited\n// to `1220px`, and they are rendered centered if the screen is larger.\n.md-grid {\n max-width: px2rem(1220px);\n margin-right: auto;\n margin-left: auto;\n}\n\n// Main container\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n}\n\n// Main area - stretch to remaining space of container\n.md-main {\n flex-grow: 1;\n\n // Main area wrapper\n &__inner {\n display: flex;\n height: 100%;\n margin-top: px2rem(24px + 6px);\n }\n}\n\n// Add ellipsis in case of overflowing text\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: navigational elements\n// ----------------------------------------------------------------------------\n\n// Toggle - this class is applied to checkbox elements, which are used to\n// implement the CSS-only drawer and navigation, as well as the search\n.md-toggle {\n display: none;\n}\n\n// Option - this class is applied to radio elements, which are used to\n// implement the color palette toggle\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Option label for checked radio button\n &:checked + label:not([hidden]) {\n display: block;\n }\n\n // Show outline for keyboard devices\n &.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n}\n\n// Skip link\n.md-skip {\n position: fixed;\n // Hack: if we don't set the negative `z-index`, the skip link will force the\n // creation of new layers when code blocks are near the header on scrolling\n z-index: -1;\n margin: px2rem(10px);\n padding: px2rem(6px) px2rem(10px);\n color: var(--md-default-bg-color);\n font-size: px2rem(12.8px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n transform: translateY(px2rem(8px));\n opacity: 0;\n\n // Show skip link on focus\n &:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 175ms 75ms;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: print styles\n// ----------------------------------------------------------------------------\n\n// Add margins to page\n@page {\n margin: 25mm;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Announcement bar\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide announcement bar\n @media print {\n display: none;\n }\n\n // Announcement wrapper\n &__inner {\n margin: px2rem(12px) auto;\n padding: 0 px2rem(16px);\n color: var(--md-footer-fg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Button to copy to clipboard\n.md-clipboard {\n position: absolute;\n top: px2em(8px);\n right: px2em(8px);\n z-index: 1;\n width: px2em(24px);\n height: px2em(24px);\n color: var(--md-default-fg-color--lightest);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(2px);\n cursor: pointer;\n transition: color 250ms;\n\n // [print]: Hide button\n @media print {\n display: none;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Darken color on code block hover\n :hover > & {\n color: var(--md-default-fg-color--light);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Button icon - the width and height are defined in `em`, so the size is\n // automatically adjusted for nested code blocks (e.g. in admonitions)\n &::after {\n display: block;\n width: px2em(18px);\n height: px2em(18px);\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Inline button\n &--inline {\n cursor: pointer;\n\n // Code block\n code {\n transition:\n color 250ms,\n background-color 250ms;\n }\n\n // Code block on focus/hover\n &:focus code,\n &:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Content area\n.md-content {\n flex-grow: 1;\n // Hack: we must use `overflow: hidden`, so the content area is capped by\n // the dimensions of its parent. Otherwise, long code blocks might lead to\n // a wider content area which will break everything. This, however, induces\n // margin collapse, which will break scroll margins. Adding a large enough\n // scroll padding seems to do the trick, at least in Chrome and Firefox.\n overflow: hidden;\n scroll-padding-top: px2rem(1024px);\n\n // Content wrapper\n &__inner {\n margin: 0 px2rem(16px) px2rem(24px);\n padding-top: px2rem(12px);\n\n // [screen +]: Adjust spacing between content area and sidebars\n @include break-from-device(screen) {\n\n // Sidebar with navigation is visible\n .md-sidebar--primary:not([hidden]) ~ .md-content > & {\n margin-left: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(24px);\n margin-left: px2rem(16px);\n }\n }\n\n // Sidebar with table of contents is visible\n .md-sidebar--secondary:not([hidden]) ~ .md-content > & {\n margin-right: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(16px);\n margin-left: px2rem(24px);\n }\n }\n }\n\n // Hack: add pseudo element for spacing, as the overflow of the content\n // container may not be hidden due to an imminent offset error on targets\n &::before {\n display: block;\n height: px2rem(8px);\n content: \"\";\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0;\n }\n }\n\n // Button inside of the content area - these buttons are meant for actions on\n // a document-level, i.e. linking to related source code files, printing etc.\n &__button {\n float: right;\n margin: px2rem(8px) 0;\n margin-left: px2rem(8px);\n padding: 0;\n\n // [print]: Hide buttons\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(8px);\n margin-left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Adjust default link color for icons\n .md-typeset & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Align with body copy located next to icon\n svg {\n display: inline;\n vertical-align: top;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Dialog\n.md-dialog {\n @include z-depth(2);\n\n position: fixed;\n right: px2rem(16px);\n bottom: px2rem(16px);\n left: initial;\n z-index: 3;\n min-width: px2rem(222px);\n padding: px2rem(8px) px2rem(12px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(100%);\n opacity: 0;\n transition:\n transform 0ms 400ms,\n opacity 400ms;\n pointer-events: none;\n\n // [print]: Hide dialog\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n\n // Dialog in open state\n &[data-md-state=\"open\"] {\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),\n opacity 400ms;\n pointer-events: initial;\n }\n\n // Dialog wrapper\n &__inner {\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Form button\n .md-button {\n display: inline-block;\n padding: px2em(10px) px2em(32px);\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: px2rem(2px) solid currentColor;\n border-radius: px2rem(2px);\n cursor: pointer;\n transition:\n color 125ms,\n background-color 125ms,\n border-color 125ms;\n\n // Primary button\n &--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n }\n\n // Form input\n .md-input {\n height: px2rem(36px);\n padding: 0 px2rem(12px);\n font-size: px2rem(16px);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transition: box-shadow 250ms;\n\n // Input on focus/hover\n &:focus,\n &:hover {\n box-shadow:\n 0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);\n }\n\n // Stretch to full width\n &--stretch {\n width: 100%;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Header - by default, the header will be sticky and stay always on top of the\n// viewport. If this behavior is not desired, just set `position: static`.\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 3;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n // Hack: reduce jitter by adding a transparent box shadow of the same size\n // so the size of the layer doesn't change during animation\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0);\n\n // [print]: Hide header\n @media print {\n display: none;\n }\n\n // Header in shadow state, i.e. shadow is visible\n &[data-md-state=\"shadow\"] {\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0.1),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0.2);\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n box-shadow 250ms;\n }\n\n // Header in hidden state, i.e. moved out of sight\n &[data-md-state=\"hidden\"] {\n transform: translateY(-100%);\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n box-shadow 250ms;\n }\n\n // Header wrapper\n &__inner {\n display: flex;\n align-items: center;\n padding: 0 px2rem(4px);\n }\n\n // Header button\n &__button {\n position: relative;\n z-index: 1;\n margin: px2rem(4px);\n padding: px2rem(8px);\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n\n // Button on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Header button is visible\n &:not([hidden]) {\n display: inline-block;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // [tablet -]: Hide button\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n }\n\n // Button for search\n &[for=\"__search\"] {\n\n // [tablet landscape +]: Hide button\n @include break-from-device(tablet landscape) {\n display: none;\n }\n\n // [no-js]: Hide button\n .no-js & {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Button for drawer\n &[for=\"__drawer\"] {\n\n // [screen +]: Hide button\n @include break-from-device(screen) {\n display: none;\n }\n }\n }\n\n // Header topic\n &__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Second header topic - title of the current page\n & + & {\n z-index: -1;\n transform: translateX(px2rem(25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-25px));\n }\n }\n }\n\n // Header title\n &__title {\n flex-grow: 1;\n height: px2rem(48px);\n margin-right: px2rem(8px);\n margin-left: px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n\n // Header title in active state, i.e. page title is visible\n &[data-md-state=\"active\"] .md-header__topic {\n z-index: -1;\n transform: translateX(px2rem(-25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(25px));\n }\n\n // Second header topic - title of the current page\n + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: initial;\n }\n }\n\n // Add ellipsis in case of overflowing text\n > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n }\n }\n\n // Header option\n &__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition:\n max-width 0ms 250ms,\n opacity 250ms 250ms;\n\n // Hide toggle when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-width: 0;\n opacity: 0;\n transition:\n max-width 0ms,\n opacity 0ms;\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n\n // [tablet landscape +]: Show repository information\n @include break-from-device(tablet landscape) {\n display: block;\n width: px2rem(234px);\n max-width: px2rem(234px);\n margin-left: px2rem(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(20px);\n margin-left: initial;\n }\n }\n\n // [screen +]: Adjust spacing of search bar\n @include break-from-device(screen) {\n margin-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Footer\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide footer\n @media print {\n display: none;\n }\n\n // Footer wrapper\n &__inner {\n padding: px2rem(4px);\n overflow: auto;\n }\n\n // Footer link to previous and next page\n &__link {\n display: flex;\n padding-top: px2rem(28px);\n padding-bottom: px2rem(8px);\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n\n // [tablet +]: Adjust width to 50/50\n @include break-from-device(tablet) {\n width: 50%;\n }\n\n // Footer link on focus/hover\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Footer link to previous page\n &--prev {\n float: left;\n\n // [mobile -]: Adjust width to 25/75 and hide title\n @include break-to-device(mobile) {\n width: 25%;\n\n // Hide footer title\n .md-footer__title {\n display: none;\n }\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Footer link to next page\n &--next {\n float: right;\n text-align: right;\n\n // [mobile -]: Adjust width to 25/75\n @include break-to-device(mobile) {\n width: 75%;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n text-align: left;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footer title\n &__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - #{px2rem(48px)});\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n }\n\n // Footer link button\n &__button {\n margin: px2rem(4px);\n padding: px2rem(8px);\n }\n\n // Footer link direction (i.e. prev and next)\n &__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: px2rem(-20px);\n padding: 0 px2rem(20px);\n font-size: px2rem(12.8px);\n opacity: 0.7;\n }\n}\n\n// Footer metadata\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n\n // Footer metadata wrapper\n &__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: px2rem(4px);\n }\n\n // Lighten color for non-hovered text links\n html &.md-typeset a {\n color: var(--md-footer-fg-color--light);\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-footer-fg-color);\n }\n }\n}\n\n// Footer copyright and theme information\n.md-footer-copyright {\n width: 100%;\n margin: auto px2rem(12px);\n padding: px2rem(8px) 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: px2rem(12.8px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n width: auto;\n }\n\n // Footer copyright highlight - this is the upper part of the copyright and\n // theme information, which will include a darker color than the theme link\n &__highlight {\n color: var(--md-footer-fg-color--light);\n }\n}\n\n// Footer social links\n.md-footer-social {\n margin: 0 px2rem(8px);\n padding: px2rem(4px) 0 px2rem(12px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n padding: px2rem(12px) 0;\n }\n\n // Footer social link\n &__link {\n display: inline-block;\n width: px2rem(32px);\n height: px2rem(32px);\n text-align: center;\n\n // Adjust line-height to match height for correct alignment\n &::before {\n line-height: 1.9;\n }\n\n // Fill icon with current color\n svg {\n max-height: px2rem(16px);\n vertical-align: -25%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Navigation\n.md-nav {\n font-size: px2rem(14px);\n line-height: 1.3;\n\n // Navigation title\n &__title {\n display: block;\n padding: 0 px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n\n // Navigaton button\n .md-nav__button {\n display: none;\n\n // Stretch images based on height, as it's the smaller dimension\n img {\n width: auto;\n height: 100%;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(48px);\n height: px2rem(48px);\n fill: currentColor;\n }\n }\n }\n }\n\n // Navigation list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Navigation item\n &__item {\n padding: 0 px2rem(12px);\n\n // Navigation item on level 2\n & & {\n padding-right: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: 0;\n }\n }\n }\n\n // Navigation link\n &__link {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n\n // Navigation link in blurred state\n &[data-md-state=\"blur\"] {\n color: var(--md-default-fg-color--light);\n }\n\n // Active link\n .md-nav__item &--active {\n color: var(--md-typeset-a-color);\n }\n\n // Stretch section index link to full width\n .md-nav__item &--index [href] {\n width: 100%;\n }\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n\n // Navigation link for table of contents\n .md-nav--primary &[for=\"__toc\"] {\n display: none;\n\n // Table of contents icon\n .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n }\n\n // Hide table of contents\n ~ .md-nav {\n display: none;\n }\n }\n\n // Navigation link children (for section indexes)\n > * {\n display: flex;\n cursor: pointer;\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n\n // Primary and nested navigation\n &--primary,\n &--primary & {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n\n // Primary navigation\n &--primary {\n\n // Navigation title and item\n .md-nav__title,\n .md-nav__item {\n font-size: px2rem(16px);\n line-height: 1.5;\n }\n\n // Navigation title\n .md-nav__title {\n position: relative;\n height: px2rem(112px);\n padding: px2rem(60px) px2rem(16px) px2rem(4px);\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: px2rem(48px);\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: px2rem(8px);\n left: px2rem(8px);\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(4px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(8px);\n left: initial;\n }\n\n // Navigation icon in link to previous level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n\n // Navigation list\n ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow:\n 0 px2rem(1px) 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Omit border on first child\n > :first-child {\n border-top: 0;\n }\n }\n\n // Top-level navigation title\n &[for=\"__drawer\"] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n\n // Button with logo, pointing to `config.site_url`\n .md-logo {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(4px);\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(4px);\n left: initial;\n }\n }\n }\n\n // Navigation list\n .md-nav__list {\n flex: 1;\n }\n\n // Navigation item\n .md-nav__item {\n padding: 0;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n\n // Navigation link in active navigation\n &--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Navigation link\n .md-nav__link {\n margin-top: 0;\n padding: px2rem(12px) px2rem(16px);\n\n // Navigation icon\n .md-nav__icon {\n flex-shrink: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin-right: px2rem(-4px);\n font-size: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: 0;\n margin-left: px2rem(-4px);\n }\n\n // Navigation icon in link to next level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n }\n\n // Flip icon vertically\n .md-nav__icon {\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] &::after {\n transform: scale(-1);\n }\n }\n\n // Table of contents contained in primary navigation\n .md-nav--secondary {\n\n // Navigation on level 2-6\n .md-nav {\n position: static;\n background-color: transparent;\n\n // Navigation link on level 3\n .md-nav__link {\n padding-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(28px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 4\n .md-nav .md-nav__link {\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 5\n .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(52px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(52px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 6\n .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(64px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(64px);\n padding-left: initial;\n }\n }\n }\n }\n }\n\n // Table of contents\n &--secondary {\n background-color: transparent;\n }\n\n // Toggle for nested navigation\n &__toggle ~ & {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n opacity 125ms 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(-100%);\n }\n }\n\n // Show nested navigation when toggle is active\n &__toggle:checked ~ & {\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms 125ms;\n\n // Navigation list\n > .md-nav__list {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n }\n }\n }\n\n // [tablet portrait -]: Layered navigation with table of contents\n @include break-to-device(tablet portrait) {\n\n // Show link to table of contents\n &--primary &__link[for=\"__toc\"] {\n display: flex;\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n\n // Repository information container\n &__source {\n display: block;\n padding: 0 px2rem(4px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n }\n\n // [tablet landscape]: Layered navigation with table of contents\n @include break-at-device(tablet landscape) {\n\n // Show link to integrated table of contents\n &--integrated &__link[for=\"__toc\"] {\n display: flex;\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n }\n\n // [tablet landscape +]: Tree-like table of contents\n @include break-from-device(tablet landscape) {\n\n // Navigation title\n &--secondary &__title {\n\n // Adjust snapping behavior\n &[for=\"__toc\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n\n // [screen +]: Tree-like navigation\n @include break-from-device(screen) {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n\n // Navigation title\n &--primary &__title {\n\n // Adjust snapping behavior\n &[for=\"__drawer\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Hide toggle for nested navigation\n &__toggle ~ & {\n display: none;\n }\n\n // Show nested navigation when toggle is active or indeterminate\n &__toggle:checked ~ &,\n &__toggle:indeterminate ~ & {\n display: block;\n }\n\n // Hide navigation title in nested navigation\n &__item--nested > & > &__title {\n display: none;\n }\n\n // Navigation section\n &__item--section {\n display: block;\n margin: 1.25em 0;\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Show navigation link as title\n > .md-nav__link {\n font-weight: 700;\n pointer-events: none;\n\n // Make navigation link clickable\n &--index [href] {\n pointer-events: initial;\n }\n\n // Hide naviation icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Navigation\n > .md-nav {\n display: block;\n\n // Adjust spacing on next level item\n > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n }\n }\n\n // Navigation icon\n &__icon {\n float: right;\n width: px2rem(18px);\n height: px2rem(18px);\n transition: transform 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n transform: rotate(180deg);\n }\n\n // Navigation icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: px2rem(-2px);\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Navigation icon - rotate icon when toggle is active or indeterminate\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link &,\n .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link & {\n transform: rotate(90deg);\n }\n }\n\n // Modifier for when navigation tabs are rendered\n &--lifted {\n\n // Hide nested level 0 navigation items and site title\n > .md-nav__list > .md-nav__item--nested,\n > .md-nav__title {\n display: none;\n }\n\n // Hide level 0 navigation items\n > .md-nav__list > .md-nav__item {\n display: none;\n\n // Active parent navigation item\n &--active {\n display: block;\n padding: 0;\n\n // Show navigation link as title\n > .md-nav__link {\n padding: 0 px2rem(12px);\n font-weight: 700;\n pointer-events: none;\n\n // Make navigation link clickable\n &--index [href] {\n pointer-events: initial;\n }\n\n // Hide naviation icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n }\n\n // Hack: Always show active navigation tab on breakpoint screen, despite\n // of checkbox being checked or not. Fixes #1655.\n .md-nav[data-md-level=\"1\"] {\n display: block;\n\n // Adjust spacing for level 1 navigation items\n > .md-nav__list > .md-nav__item {\n padding-right: px2rem(12px);\n }\n }\n }\n\n // Modifier for when table of contents is rendered in primary navigation\n &--integrated &__link[for=\"__toc\"] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: px2rem(1px) solid var(--md-primary-fg-color);\n\n // Hide navigation title\n > .md-nav__title {\n display: none;\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Search\n.md-search {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding: px2rem(4px) 0;\n }\n\n // [no-js]: Hide search\n .no-js & {\n display: none;\n }\n\n // Search overlay\n &__overlay {\n z-index: 1;\n opacity: 0;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: absolute;\n top: px2rem(-20px);\n left: px2rem(-44px);\n width: px2rem(40px);\n height: px2rem(40px);\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(20px);\n transform-origin: center;\n transition:\n transform 300ms 100ms,\n opacity 200ms 200ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-44px);\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition:\n transform 400ms,\n opacity 100ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n cursor: pointer;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n // Hack: when the header is translated upon scrolling, a new layer is\n // induced, which means that the height will now refer to the height of\n // the header, albeit positioning is fixed. This should be mitigated\n // in all cases when setting the height to 2x the viewport.\n height: 200vh;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [mobile portrait -]: Scale up 45 times\n @include break-to-device(mobile portrait) {\n transform: scale(45);\n }\n\n // [mobile landscape]: Scale up 60 times\n @include break-at-device(mobile landscape) {\n transform: scale(60);\n }\n\n // [tablet portrait]: Scale up 75 times\n @include break-at-device(tablet portrait) {\n transform: scale(75);\n }\n }\n }\n\n // Search wrapper\n &__inner {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 2;\n width: 0;\n height: 0;\n overflow: hidden;\n transform: translateX(5%);\n opacity: 0;\n transition:\n width 0ms 300ms,\n height 0ms 300ms,\n transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 150ms 150ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n transform: translateX(-5%);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n height: 100%;\n transform: translateX(0);\n opacity: 1;\n transition:\n width 0ms 0ms,\n height 0ms 0ms,\n transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms 150ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: relative;\n float: right;\n width: px2rem(234px);\n padding: px2rem(2px) 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [tablet landscape]: Omit overlaying header title\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Match width of content area\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n }\n }\n\n // Search form\n &__form {\n position: relative;\n z-index: 2;\n height: px2rem(48px);\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 px2rem(12px) transparent;\n transition:\n color 250ms,\n background-color 250ms;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n height: px2rem(36px);\n background-color: hsla(0, 0%, 0%, 0.26);\n border-radius: px2rem(2px);\n\n // Search form on hover\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.12);\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px) px2rem(2px) 0 0;\n box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);\n }\n }\n\n // Search input\n &__input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: 100%;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n font-size: px2rem(18px);\n text-overflow: ellipsis;\n background: transparent;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n transition: color 250ms;\n }\n\n // Search icon and placeholder\n ~ .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n\n // Remove the \"x\" rendered by Internet Explorer\n &::-ms-clear {\n display: none;\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n width: 100%;\n height: px2rem(48px);\n font-size: px2rem(18px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n color: inherit;\n font-size: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n\n // Search icon\n + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n text-overflow: clip;\n\n // Search icon and placeholder\n + .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n }\n }\n }\n\n // Search icon\n &__icon {\n display: inline-block;\n width: px2rem(24px);\n height: px2rem(24px);\n cursor: pointer;\n transition:\n color 250ms,\n opacity 250ms;\n\n // Search icon on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Search focus button\n &[for=\"__search\"] {\n position: absolute;\n top: px2rem(6px);\n left: px2rem(10px);\n z-index: 2;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(10px);\n left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n\n // Hide the magnifying glass\n svg:first-child {\n display: none;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n pointer-events: none;\n\n // Hide the back arrow\n svg:last-child {\n display: none;\n }\n }\n }\n }\n\n // Search options\n &__options {\n position: absolute;\n top: px2rem(6px);\n right: px2rem(10px);\n z-index: 2;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(10px);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n right: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n }\n\n // Search option buttons\n > * {\n margin-left: px2rem(4px);\n color: var(--md-default-fg-color--light);\n transform: scale(0.75);\n opacity: 0;\n transition:\n transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Show reset button when search is active and input non-empty\n [data-md-toggle=\"search\"]:checked ~ .md-header\n .md-search__input:valid ~ & {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n\n // Search focus icon\n &:hover {\n opacity: 0.7;\n }\n }\n }\n }\n\n // Search suggestions\n &__suggest {\n position: absolute;\n top: 0;\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n color: var(--md-default-fg-color--lighter);\n font-size: px2rem(18px);\n white-space: nowrap;\n opacity: 0;\n transition: opacity 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n font-size: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n }\n\n // Show suggestions when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition: opacity 300ms 100ms;\n }\n }\n\n // Search output\n &__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 px2rem(2px) px2rem(2px);\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(48px);\n bottom: 0;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n top: px2rem(38px);\n opacity: 0;\n transition: opacity 400ms;\n\n // Show output when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n @include z-depth(6);\n\n opacity: 1;\n }\n }\n }\n\n // Search scroll wrapper\n &__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 88+ has weird overscroll behavior. Overall, scroll snapping\n // seems to be something that is not ready for prime time on some browsers.\n // scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Mitigiate excessive repaints on non-retina devices\n @media (max-resolution: 1dppx) {\n transform: translateZ(0);\n }\n\n // [tablet landscape]: Set fixed width to omit unnecessary reflow\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Set fixed width to omit unnecessary reflow\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n\n // [tablet landscape +]: Limit height to viewport\n @include break-from-device(tablet landscape) {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Show scroll wrapper when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-height: 75vh;\n }\n\n // Search scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n}\n\n// Search result\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n\n // Search result metadata\n &__meta {\n padding: 0 px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: px2rem(36px);\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: initial;\n }\n }\n }\n\n // Search result list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Search result item\n &__item {\n box-shadow: 0 px2rem(-1px) 0 var(--md-default-fg-color--lightest);\n\n // Omit border on first child\n &:first-child {\n box-shadow: none;\n }\n }\n\n // Search result link\n &__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n\n // Search result link on focus/hover\n &:focus,\n &:hover {\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Adjust spacing on last child of last link\n &:last-child p:last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Search result more link\n &__more summary {\n display: block;\n padding: px2em(12px) px2rem(16px);\n color: var(--md-typeset-a-color);\n font-size: px2rem(12.8px);\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result more link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n\n // Adjust transparency of less relevant results\n ~ * > * {\n opacity: 0.65;\n }\n }\n\n // Search result article\n &__article {\n position: relative;\n padding: 0 px2rem(16px);\n overflow: hidden;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result article document\n &--document {\n\n // Search result title\n .md-search-result__title {\n margin: px2rem(11px) 0;\n font-weight: 400;\n font-size: px2rem(16px);\n line-height: 1.4;\n }\n }\n }\n\n // Search result icon\n &__icon {\n position: absolute;\n left: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(10px);\n color: var(--md-default-fg-color--light);\n\n // [tablet portrait -]: Hide icon\n @include break-to-device(tablet portrait) {\n display: none;\n }\n\n // Search result icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n\n // Flip icon vertically\n &::after {\n transform: scaleX(-1);\n }\n }\n }\n\n // Search result title\n &__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: px2rem(12.8px);\n line-height: 1.6;\n }\n\n // Search result teaser\n &__teaser {\n display: -webkit-box;\n max-height: px2rem(40px);\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n\n // [mobile -]: Adjust number of lines\n @include break-to-device(mobile) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // [tablet landscape]: Adjust number of lines\n @include break-at-device(tablet landscape) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // Search term highlighting\n mark {\n text-decoration: underline;\n background-color: transparent;\n }\n }\n\n // Search result terms\n &__terms {\n margin: 0.5em 0;\n font-size: px2rem(12.8px);\n font-style: italic;\n }\n\n // Search term highlighting\n mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Selection\n.md-select {\n position: relative;\n z-index: 1;\n\n // Selection bubble\n &__inner {\n position: absolute;\n top: calc(100% - #{px2rem(4px)});\n left: 50%;\n max-height: 0;\n margin-top: px2rem(4px);\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate3d(-50%, px2rem(6px), 0);\n opacity: 0;\n transition:\n transform 250ms 375ms,\n opacity 250ms 250ms,\n max-height 0ms 500ms;\n\n // Selection bubble on parent focus/hover\n .md-select:focus-within &,\n .md-select:hover & {\n max-height: px2rem(200px);\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms,\n max-height 0ms;\n }\n\n // Selection bubble handle\n &::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: px2rem(-4px);\n margin-left: px2rem(-4px);\n border: px2rem(4px) solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n }\n }\n\n // Selection list\n &__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: px2rem(16px);\n list-style-type: none;\n border-radius: px2rem(2px);\n }\n\n // Selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n outline: none;\n cursor: pointer;\n transition:\n background-color 250ms,\n color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Sidebar\n.md-sidebar {\n position: sticky;\n top: px2rem(48px);\n flex-shrink: 0;\n align-self: flex-start;\n width: px2rem(242px);\n padding: px2rem(24px) 0;\n\n // [print]: Hide sidebar\n @media print {\n display: none;\n }\n\n // [tablet -]: Show navigation as drawer\n @include break-to-device(tablet) {\n\n // Primary sidebar with navigation\n &--primary {\n position: fixed;\n top: 0;\n left: px2rem(-242px);\n z-index: 4;\n display: block;\n width: px2rem(242px);\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-242px);\n left: initial;\n }\n\n // Show sidebar when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ .md-container & {\n @include z-depth(8);\n\n transform: translateX(px2rem(242px));\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-242px));\n }\n }\n\n // Stretch scroll wrapper for primary sidebar\n .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n }\n }\n\n // [screen +]: Show navigation as sidebar\n @include break-from-device(screen) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n }\n\n // Secondary sidebar with table of contents\n &--secondary {\n display: none;\n order: 2;\n\n // [tablet landscape +]: Show table of contents as sidebar\n @include break-from-device(tablet landscape) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n\n // Sidebar is visible\n &:not([hidden]) {\n display: block;\n }\n\n // Ensure smooth scrolling on iOS\n .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n }\n }\n\n // Sidebar scroll wrapper\n &__scrollwrap {\n margin: 0 px2rem(4px);\n overflow-y: auto;\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 81+ exhibits a strange bug, where it scrolls the container\n // to the bottom if `scroll-snap-type` is set on the initial render. For\n // this reason, we disable scroll snapping until this is resolved (#1667).\n // scroll-snap-type: y mandatory;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Sidebar scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n}\n\n// [tablet -]: Show overlay on active drawer\n@include break-to-device(tablet) {\n\n // Sidebar overlay\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 4;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n opacity: 0;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Show overlay when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Show repository facts\n@keyframes facts {\n 0% {\n height: 0;\n }\n\n 100% {\n height: px2rem(13px);\n }\n}\n\n// Show repository fact\n@keyframes fact {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Repository information\n.md-source {\n display: block;\n font-size: px2rem(13px);\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n transition: opacity 250ms;\n\n // Repository information on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Repository icon\n &__icon {\n display: inline-block;\n width: px2rem(40px);\n height: px2rem(48px);\n vertical-align: middle;\n\n // Align with margin only (as opposed to normal button alignment)\n svg {\n margin-top: px2rem(12px);\n margin-left: px2rem(12px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(12px);\n margin-left: initial;\n }\n }\n\n // Adjust spacing if icon is present\n + .md-source__repository {\n margin-left: px2rem(-40px);\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(-40px);\n margin-left: initial;\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n }\n\n // Repository name\n &__repository {\n display: inline-block;\n max-width: calc(100% - #{px2rem(24px)});\n margin-left: px2rem(12px);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n }\n\n // Repository facts\n &__facts {\n margin: px2rem(2px) 0 0;\n padding: 0;\n overflow: hidden;\n font-size: px2rem(11px);\n list-style-type: none;\n opacity: 0.75;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: facts 250ms ease-in;\n }\n }\n\n // Repository fact\n &__fact {\n display: inline-block;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: fact 400ms ease-out;\n }\n\n // Repository fact icon\n &::before {\n display: inline-block;\n width: px2rem(12px);\n height: px2rem(12px);\n margin-right: px2rem(2px);\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: initial;\n margin-left: px2rem(2px);\n }\n }\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-left: px2rem(8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(8px);\n margin-left: px2rem(2px);\n }\n }\n\n // Repository fact: version\n &--version::before {\n mask-image: var(--md-source-version-icon);\n }\n\n // Repository fact: stars\n &--stars::before {\n mask-image: var(--md-source-stars-icon);\n }\n\n // Repository fact: forks\n &--forks::before {\n mask-image: var(--md-source-forks-icon);\n }\n\n // Repository fact: repositories\n &--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Navigation tabs\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n\n // [print]: Hide tabs\n @media print {\n display: none;\n }\n\n // [tablet -]: Hide tabs\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Tabs in hidden state, i.e. when scrolling down\n &[data-md-state=\"hidden\"] {\n pointer-events: none;\n }\n\n // Navigation tabs list\n &__list {\n margin: 0;\n margin-left: px2rem(4px);\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(4px);\n margin-left: initial;\n }\n }\n\n // Navigation tabs item\n &__item {\n display: inline-block;\n height: px2rem(48px);\n padding-right: px2rem(12px);\n padding-left: px2rem(12px);\n }\n\n // Navigation tabs link - could be defined as block elements and aligned via\n // line height, but this would imply more repaints when scrolling\n &__link {\n display: block;\n margin-top: px2rem(16px);\n font-size: px2rem(14px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n // Hack: save a repaint when tabs are appearing on scrolling up\n backface-visibility: hidden;\n opacity: 0.7;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms;\n\n // Active link and link on focus/hover\n &--active,\n &:focus,\n &:hover {\n color: inherit;\n opacity: 1;\n }\n\n // Delay transitions by a small amount\n @for $i from 2 through 16 {\n .md-tabs__item:nth-child(#{$i}) & {\n transition-delay: 20ms * ($i - 1);\n }\n }\n\n // Hide tabs upon scrolling - disable transition to minimizes repaints\n // while scrolling down, while scrolling up seems to be okay\n .md-tabs[data-md-state=\"hidden\"] & {\n transform: translateY(50%);\n opacity: 0;\n transition:\n transform 0ms 100ms,\n opacity 100ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Back-to-top button\n.md-top {\n position: fixed;\n top: px2rem(48px + 16px);\n z-index: 2;\n margin-left: 50%;\n padding: px2rem(8px) px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(14px);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(32px);\n outline: none;\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate(-50%, 0);\n transition:\n color 125ms,\n background-color 125ms,\n transform 125ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms;\n\n // [print]: Hide back-to-top button\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n\n // Back-to-top button in hidden state\n &[data-md-state=\"hidden\"] {\n transform: translate(-50%, px2rem(4px));\n opacity: 0;\n transition-duration: 0ms;\n pointer-events: none;\n }\n\n // Back-to-top button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n }\n\n // Inline icon\n svg {\n display: inline-block;\n vertical-align: -0.5em;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// See https://github.com/squidfunk/mkdocs-material/issues/2429\n@keyframes hoverfix {\n 0% {\n pointer-events: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Version selection\n.md-version {\n flex-shrink: 0;\n height: px2rem(48px);\n font-size: px2rem(16px);\n\n // Current selection\n &__current {\n position: relative;\n // Hack: in general, we would use `vertical-align` to align the version at\n // the bottom with the title, but since the list uses absolute positioning,\n // this won't work consistently. Furthermore, we would need to use inline\n // positioning to align the links, which looks jagged.\n top: px2rem(1px);\n margin-right: px2rem(8px);\n margin-left: px2rem(28px);\n color: inherit;\n outline: none;\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n margin-left: px2rem(8px);\n }\n\n // Version selection icon\n &::after {\n display: inline-block;\n width: px2rem(8px);\n height: px2rem(12px);\n margin-left: px2rem(8px);\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n }\n\n // Version selection list\n &__list {\n position: absolute;\n top: px2rem(3px);\n z-index: 1;\n max-height: 0;\n margin: px2rem(4px) px2rem(16px);\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n opacity: 0;\n transition:\n max-height 0ms 500ms,\n opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n\n // Version selection list on parent focus/hover\n .md-version:focus-within &,\n .md-version:hover & {\n max-height: px2rem(200px);\n opacity: 1;\n transition:\n max-height 0ms,\n opacity 250ms;\n }\n\n // Fix hover on touch devices\n @media (pointer: coarse) {\n\n // Switch off on hover\n .md-version:hover & {\n animation: hoverfix 250ms forwards;\n }\n\n // Enable on focus\n .md-version:focus-within & {\n animation: none;\n }\n }\n }\n\n // Version selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Version selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n/// Admonition flavours\n$admonitions: (\n note: pencil $clr-blue-a200,\n abstract summary tldr: clipboard-text $clr-light-blue-a400,\n info todo: information $clr-cyan-a700,\n tip hint important: fire $clr-teal-a700,\n success check done: check-bold $clr-green-a700,\n question help faq: help-circle $clr-light-green-a700,\n warning caution attention: alert $clr-orange-a400,\n failure fail missing: close-thick $clr-red-a200,\n danger error: lightning-bolt $clr-red-a400,\n bug: bug $clr-pink-a400,\n example: format-list-numbered $clr-deep-purple-a200,\n quote cite: format-quote-close $clr-grey\n) !default;\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n @each $names, $props in $admonitions {\n --md-admonition-icon--#{nth($names, 1)}:\n svg-load(\"material/#{nth($props, 1)}.svg\");\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Admonition\n .admonition {\n margin: px2em(20px, 12.8px) 0;\n padding: 0 px2rem(12px);\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: px2rem(12.8px);\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: px2rem(4px) solid $clr-blue-a200;\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.05);\n\n // [print]: Omit shadow as it may lead to rendering errors\n @media print {\n box-shadow: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust vertical spacing for nested admonitions\n .admonition {\n margin-top: 1em;\n margin-bottom: 1em;\n }\n\n // Adjust spacing for contained table wrappers\n .md-typeset__scrollwrap {\n margin: 1em px2rem(-12px);\n }\n\n // Adjust spacing for contained tables\n .md-typeset__table {\n padding: 0 px2rem(12px);\n }\n\n // Adjust spacing for single-child tabbed block container\n > .tabbed-set:only-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n html & > :last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Admonition title\n .admonition-title {\n position: relative;\n margin: 0 px2rem(-12px) 0 px2rem(-16px);\n padding: px2rem(8px) px2rem(12px) px2rem(8px) px2rem(40px);\n font-weight: 700;\n background-color: transparentize($clr-blue-a200, 0.9);\n border-left: px2rem(4px) solid $clr-blue-a200;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin: 0 px2rem(-16px) 0 px2rem(-12px);\n padding: px2rem(8px) px2rem(40px) px2rem(8px) px2rem(12px);\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust spacing for title-only admonitions\n html &:last-child {\n margin-bottom: 0;\n }\n\n // Admonition icon\n &::before {\n position: absolute;\n left: px2rem(12px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: $clr-blue-a200;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(12px);\n left: initial;\n }\n }\n\n // Adjust spacing on last tabbed block container child - if the tabbed\n // block container is the sole child, it looks better to omit the margin\n + .tabbed-set:last-child {\n margin-top: 0;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: flavours\n// ----------------------------------------------------------------------------\n\n@each $names, $props in $admonitions {\n $name: nth($names, 1);\n $tint: nth($props, 2);\n\n // Admonition flavour\n .md-typeset .admonition.#{$name} {\n border-color: $tint;\n }\n\n // Admonition flavour title\n .md-typeset .#{$name} > .admonition-title {\n background-color: transparentize($tint, 0.9);\n border-color: $tint;\n\n // Admonition icon\n &::before {\n background-color: $tint;\n mask-image: var(--md-admonition-icon--#{$name});\n mask-repeat: no-repeat;\n mask-size: contain;\n }\n }\n\n // Define synonyms for flavours\n @if length($names) > 1 {\n @for $n from 2 through length($names) {\n .#{nth($names, $n)} {\n @extend .#{$name};\n }\n }\n }\n}\n","// ==========================================================================\n//\n// Name: UI Color Palette\n// Description: The color palette of material design.\n// Version: 2.3.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-color\n//\n// twitter: @mrmlnc\n//\n// ==========================================================================\n\n\n//\n// List of base colors\n//\n\n// $clr-red\n// $clr-pink\n// $clr-purple\n// $clr-deep-purple\n// $clr-indigo\n// $clr-blue\n// $clr-light-blue\n// $clr-cyan\n// $clr-teal\n// $clr-green\n// $clr-light-green\n// $clr-lime\n// $clr-yellow\n// $clr-amber\n// $clr-orange\n// $clr-deep-orange\n// $clr-brown\n// $clr-grey\n// $clr-blue-grey\n// $clr-black\n// $clr-white\n\n\n//\n// Red\n//\n\n$clr-red-list: (\n \"base\": #f44336,\n \"50\": #ffebee,\n \"100\": #ffcdd2,\n \"200\": #ef9a9a,\n \"300\": #e57373,\n \"400\": #ef5350,\n \"500\": #f44336,\n \"600\": #e53935,\n \"700\": #d32f2f,\n \"800\": #c62828,\n \"900\": #b71c1c,\n \"a100\": #ff8a80,\n \"a200\": #ff5252,\n \"a400\": #ff1744,\n \"a700\": #d50000\n);\n\n$clr-red: map-get($clr-red-list, \"base\");\n\n$clr-red-50: map-get($clr-red-list, \"50\");\n$clr-red-100: map-get($clr-red-list, \"100\");\n$clr-red-200: map-get($clr-red-list, \"200\");\n$clr-red-300: map-get($clr-red-list, \"300\");\n$clr-red-400: map-get($clr-red-list, \"400\");\n$clr-red-500: map-get($clr-red-list, \"500\");\n$clr-red-600: map-get($clr-red-list, \"600\");\n$clr-red-700: map-get($clr-red-list, \"700\");\n$clr-red-800: map-get($clr-red-list, \"800\");\n$clr-red-900: map-get($clr-red-list, \"900\");\n$clr-red-a100: map-get($clr-red-list, \"a100\");\n$clr-red-a200: map-get($clr-red-list, \"a200\");\n$clr-red-a400: map-get($clr-red-list, \"a400\");\n$clr-red-a700: map-get($clr-red-list, \"a700\");\n\n\n//\n// Pink\n//\n\n$clr-pink-list: (\n \"base\": #e91e63,\n \"50\": #fce4ec,\n \"100\": #f8bbd0,\n \"200\": #f48fb1,\n \"300\": #f06292,\n \"400\": #ec407a,\n \"500\": #e91e63,\n \"600\": #d81b60,\n \"700\": #c2185b,\n \"800\": #ad1457,\n \"900\": #880e4f,\n \"a100\": #ff80ab,\n \"a200\": #ff4081,\n \"a400\": #f50057,\n \"a700\": #c51162\n);\n\n$clr-pink: map-get($clr-pink-list, \"base\");\n\n$clr-pink-50: map-get($clr-pink-list, \"50\");\n$clr-pink-100: map-get($clr-pink-list, \"100\");\n$clr-pink-200: map-get($clr-pink-list, \"200\");\n$clr-pink-300: map-get($clr-pink-list, \"300\");\n$clr-pink-400: map-get($clr-pink-list, \"400\");\n$clr-pink-500: map-get($clr-pink-list, \"500\");\n$clr-pink-600: map-get($clr-pink-list, \"600\");\n$clr-pink-700: map-get($clr-pink-list, \"700\");\n$clr-pink-800: map-get($clr-pink-list, \"800\");\n$clr-pink-900: map-get($clr-pink-list, \"900\");\n$clr-pink-a100: map-get($clr-pink-list, \"a100\");\n$clr-pink-a200: map-get($clr-pink-list, \"a200\");\n$clr-pink-a400: map-get($clr-pink-list, \"a400\");\n$clr-pink-a700: map-get($clr-pink-list, \"a700\");\n\n\n//\n// Purple\n//\n\n$clr-purple-list: (\n \"base\": #9c27b0,\n \"50\": #f3e5f5,\n \"100\": #e1bee7,\n \"200\": #ce93d8,\n \"300\": #ba68c8,\n \"400\": #ab47bc,\n \"500\": #9c27b0,\n \"600\": #8e24aa,\n \"700\": #7b1fa2,\n \"800\": #6a1b9a,\n \"900\": #4a148c,\n \"a100\": #ea80fc,\n \"a200\": #e040fb,\n \"a400\": #d500f9,\n \"a700\": #aa00ff\n);\n\n$clr-purple: map-get($clr-purple-list, \"base\");\n\n$clr-purple-50: map-get($clr-purple-list, \"50\");\n$clr-purple-100: map-get($clr-purple-list, \"100\");\n$clr-purple-200: map-get($clr-purple-list, \"200\");\n$clr-purple-300: map-get($clr-purple-list, \"300\");\n$clr-purple-400: map-get($clr-purple-list, \"400\");\n$clr-purple-500: map-get($clr-purple-list, \"500\");\n$clr-purple-600: map-get($clr-purple-list, \"600\");\n$clr-purple-700: map-get($clr-purple-list, \"700\");\n$clr-purple-800: map-get($clr-purple-list, \"800\");\n$clr-purple-900: map-get($clr-purple-list, \"900\");\n$clr-purple-a100: map-get($clr-purple-list, \"a100\");\n$clr-purple-a200: map-get($clr-purple-list, \"a200\");\n$clr-purple-a400: map-get($clr-purple-list, \"a400\");\n$clr-purple-a700: map-get($clr-purple-list, \"a700\");\n\n\n//\n// Deep purple\n//\n\n$clr-deep-purple-list: (\n \"base\": #673ab7,\n \"50\": #ede7f6,\n \"100\": #d1c4e9,\n \"200\": #b39ddb,\n \"300\": #9575cd,\n \"400\": #7e57c2,\n \"500\": #673ab7,\n \"600\": #5e35b1,\n \"700\": #512da8,\n \"800\": #4527a0,\n \"900\": #311b92,\n \"a100\": #b388ff,\n \"a200\": #7c4dff,\n \"a400\": #651fff,\n \"a700\": #6200ea\n);\n\n$clr-deep-purple: map-get($clr-deep-purple-list, \"base\");\n\n$clr-deep-purple-50: map-get($clr-deep-purple-list, \"50\");\n$clr-deep-purple-100: map-get($clr-deep-purple-list, \"100\");\n$clr-deep-purple-200: map-get($clr-deep-purple-list, \"200\");\n$clr-deep-purple-300: map-get($clr-deep-purple-list, \"300\");\n$clr-deep-purple-400: map-get($clr-deep-purple-list, \"400\");\n$clr-deep-purple-500: map-get($clr-deep-purple-list, \"500\");\n$clr-deep-purple-600: map-get($clr-deep-purple-list, \"600\");\n$clr-deep-purple-700: map-get($clr-deep-purple-list, \"700\");\n$clr-deep-purple-800: map-get($clr-deep-purple-list, \"800\");\n$clr-deep-purple-900: map-get($clr-deep-purple-list, \"900\");\n$clr-deep-purple-a100: map-get($clr-deep-purple-list, \"a100\");\n$clr-deep-purple-a200: map-get($clr-deep-purple-list, \"a200\");\n$clr-deep-purple-a400: map-get($clr-deep-purple-list, \"a400\");\n$clr-deep-purple-a700: map-get($clr-deep-purple-list, \"a700\");\n\n\n//\n// Indigo\n//\n\n$clr-indigo-list: (\n \"base\": #3f51b5,\n \"50\": #e8eaf6,\n \"100\": #c5cae9,\n \"200\": #9fa8da,\n \"300\": #7986cb,\n \"400\": #5c6bc0,\n \"500\": #3f51b5,\n \"600\": #3949ab,\n \"700\": #303f9f,\n \"800\": #283593,\n \"900\": #1a237e,\n \"a100\": #8c9eff,\n \"a200\": #536dfe,\n \"a400\": #3d5afe,\n \"a700\": #304ffe\n);\n\n$clr-indigo: map-get($clr-indigo-list, \"base\");\n\n$clr-indigo-50: map-get($clr-indigo-list, \"50\");\n$clr-indigo-100: map-get($clr-indigo-list, \"100\");\n$clr-indigo-200: map-get($clr-indigo-list, \"200\");\n$clr-indigo-300: map-get($clr-indigo-list, \"300\");\n$clr-indigo-400: map-get($clr-indigo-list, \"400\");\n$clr-indigo-500: map-get($clr-indigo-list, \"500\");\n$clr-indigo-600: map-get($clr-indigo-list, \"600\");\n$clr-indigo-700: map-get($clr-indigo-list, \"700\");\n$clr-indigo-800: map-get($clr-indigo-list, \"800\");\n$clr-indigo-900: map-get($clr-indigo-list, \"900\");\n$clr-indigo-a100: map-get($clr-indigo-list, \"a100\");\n$clr-indigo-a200: map-get($clr-indigo-list, \"a200\");\n$clr-indigo-a400: map-get($clr-indigo-list, \"a400\");\n$clr-indigo-a700: map-get($clr-indigo-list, \"a700\");\n\n\n//\n// Blue\n//\n\n$clr-blue-list: (\n \"base\": #2196f3,\n \"50\": #e3f2fd,\n \"100\": #bbdefb,\n \"200\": #90caf9,\n \"300\": #64b5f6,\n \"400\": #42a5f5,\n \"500\": #2196f3,\n \"600\": #1e88e5,\n \"700\": #1976d2,\n \"800\": #1565c0,\n \"900\": #0d47a1,\n \"a100\": #82b1ff,\n \"a200\": #448aff,\n \"a400\": #2979ff,\n \"a700\": #2962ff\n);\n\n$clr-blue: map-get($clr-blue-list, \"base\");\n\n$clr-blue-50: map-get($clr-blue-list, \"50\");\n$clr-blue-100: map-get($clr-blue-list, \"100\");\n$clr-blue-200: map-get($clr-blue-list, \"200\");\n$clr-blue-300: map-get($clr-blue-list, \"300\");\n$clr-blue-400: map-get($clr-blue-list, \"400\");\n$clr-blue-500: map-get($clr-blue-list, \"500\");\n$clr-blue-600: map-get($clr-blue-list, \"600\");\n$clr-blue-700: map-get($clr-blue-list, \"700\");\n$clr-blue-800: map-get($clr-blue-list, \"800\");\n$clr-blue-900: map-get($clr-blue-list, \"900\");\n$clr-blue-a100: map-get($clr-blue-list, \"a100\");\n$clr-blue-a200: map-get($clr-blue-list, \"a200\");\n$clr-blue-a400: map-get($clr-blue-list, \"a400\");\n$clr-blue-a700: map-get($clr-blue-list, \"a700\");\n\n\n//\n// Light Blue\n//\n\n$clr-light-blue-list: (\n \"base\": #03a9f4,\n \"50\": #e1f5fe,\n \"100\": #b3e5fc,\n \"200\": #81d4fa,\n \"300\": #4fc3f7,\n \"400\": #29b6f6,\n \"500\": #03a9f4,\n \"600\": #039be5,\n \"700\": #0288d1,\n \"800\": #0277bd,\n \"900\": #01579b,\n \"a100\": #80d8ff,\n \"a200\": #40c4ff,\n \"a400\": #00b0ff,\n \"a700\": #0091ea\n);\n\n$clr-light-blue: map-get($clr-light-blue-list, \"base\");\n\n$clr-light-blue-50: map-get($clr-light-blue-list, \"50\");\n$clr-light-blue-100: map-get($clr-light-blue-list, \"100\");\n$clr-light-blue-200: map-get($clr-light-blue-list, \"200\");\n$clr-light-blue-300: map-get($clr-light-blue-list, \"300\");\n$clr-light-blue-400: map-get($clr-light-blue-list, \"400\");\n$clr-light-blue-500: map-get($clr-light-blue-list, \"500\");\n$clr-light-blue-600: map-get($clr-light-blue-list, \"600\");\n$clr-light-blue-700: map-get($clr-light-blue-list, \"700\");\n$clr-light-blue-800: map-get($clr-light-blue-list, \"800\");\n$clr-light-blue-900: map-get($clr-light-blue-list, \"900\");\n$clr-light-blue-a100: map-get($clr-light-blue-list, \"a100\");\n$clr-light-blue-a200: map-get($clr-light-blue-list, \"a200\");\n$clr-light-blue-a400: map-get($clr-light-blue-list, \"a400\");\n$clr-light-blue-a700: map-get($clr-light-blue-list, \"a700\");\n\n\n//\n// Cyan\n//\n\n$clr-cyan-list: (\n \"base\": #00bcd4,\n \"50\": #e0f7fa,\n \"100\": #b2ebf2,\n \"200\": #80deea,\n \"300\": #4dd0e1,\n \"400\": #26c6da,\n \"500\": #00bcd4,\n \"600\": #00acc1,\n \"700\": #0097a7,\n \"800\": #00838f,\n \"900\": #006064,\n \"a100\": #84ffff,\n \"a200\": #18ffff,\n \"a400\": #00e5ff,\n \"a700\": #00b8d4\n);\n\n$clr-cyan: map-get($clr-cyan-list, \"base\");\n\n$clr-cyan-50: map-get($clr-cyan-list, \"50\");\n$clr-cyan-100: map-get($clr-cyan-list, \"100\");\n$clr-cyan-200: map-get($clr-cyan-list, \"200\");\n$clr-cyan-300: map-get($clr-cyan-list, \"300\");\n$clr-cyan-400: map-get($clr-cyan-list, \"400\");\n$clr-cyan-500: map-get($clr-cyan-list, \"500\");\n$clr-cyan-600: map-get($clr-cyan-list, \"600\");\n$clr-cyan-700: map-get($clr-cyan-list, \"700\");\n$clr-cyan-800: map-get($clr-cyan-list, \"800\");\n$clr-cyan-900: map-get($clr-cyan-list, \"900\");\n$clr-cyan-a100: map-get($clr-cyan-list, \"a100\");\n$clr-cyan-a200: map-get($clr-cyan-list, \"a200\");\n$clr-cyan-a400: map-get($clr-cyan-list, \"a400\");\n$clr-cyan-a700: map-get($clr-cyan-list, \"a700\");\n\n\n//\n// Teal\n//\n\n$clr-teal-list: (\n \"base\": #009688,\n \"50\": #e0f2f1,\n \"100\": #b2dfdb,\n \"200\": #80cbc4,\n \"300\": #4db6ac,\n \"400\": #26a69a,\n \"500\": #009688,\n \"600\": #00897b,\n \"700\": #00796b,\n \"800\": #00695c,\n \"900\": #004d40,\n \"a100\": #a7ffeb,\n \"a200\": #64ffda,\n \"a400\": #1de9b6,\n \"a700\": #00bfa5\n);\n\n$clr-teal: map-get($clr-teal-list, \"base\");\n\n$clr-teal-50: map-get($clr-teal-list, \"50\");\n$clr-teal-100: map-get($clr-teal-list, \"100\");\n$clr-teal-200: map-get($clr-teal-list, \"200\");\n$clr-teal-300: map-get($clr-teal-list, \"300\");\n$clr-teal-400: map-get($clr-teal-list, \"400\");\n$clr-teal-500: map-get($clr-teal-list, \"500\");\n$clr-teal-600: map-get($clr-teal-list, \"600\");\n$clr-teal-700: map-get($clr-teal-list, \"700\");\n$clr-teal-800: map-get($clr-teal-list, \"800\");\n$clr-teal-900: map-get($clr-teal-list, \"900\");\n$clr-teal-a100: map-get($clr-teal-list, \"a100\");\n$clr-teal-a200: map-get($clr-teal-list, \"a200\");\n$clr-teal-a400: map-get($clr-teal-list, \"a400\");\n$clr-teal-a700: map-get($clr-teal-list, \"a700\");\n\n\n//\n// Green\n//\n\n$clr-green-list: (\n \"base\": #4caf50,\n \"50\": #e8f5e9,\n \"100\": #c8e6c9,\n \"200\": #a5d6a7,\n \"300\": #81c784,\n \"400\": #66bb6a,\n \"500\": #4caf50,\n \"600\": #43a047,\n \"700\": #388e3c,\n \"800\": #2e7d32,\n \"900\": #1b5e20,\n \"a100\": #b9f6ca,\n \"a200\": #69f0ae,\n \"a400\": #00e676,\n \"a700\": #00c853\n);\n\n$clr-green: map-get($clr-green-list, \"base\");\n\n$clr-green-50: map-get($clr-green-list, \"50\");\n$clr-green-100: map-get($clr-green-list, \"100\");\n$clr-green-200: map-get($clr-green-list, \"200\");\n$clr-green-300: map-get($clr-green-list, \"300\");\n$clr-green-400: map-get($clr-green-list, \"400\");\n$clr-green-500: map-get($clr-green-list, \"500\");\n$clr-green-600: map-get($clr-green-list, \"600\");\n$clr-green-700: map-get($clr-green-list, \"700\");\n$clr-green-800: map-get($clr-green-list, \"800\");\n$clr-green-900: map-get($clr-green-list, \"900\");\n$clr-green-a100: map-get($clr-green-list, \"a100\");\n$clr-green-a200: map-get($clr-green-list, \"a200\");\n$clr-green-a400: map-get($clr-green-list, \"a400\");\n$clr-green-a700: map-get($clr-green-list, \"a700\");\n\n\n//\n// Light green\n//\n\n$clr-light-green-list: (\n \"base\": #8bc34a,\n \"50\": #f1f8e9,\n \"100\": #dcedc8,\n \"200\": #c5e1a5,\n \"300\": #aed581,\n \"400\": #9ccc65,\n \"500\": #8bc34a,\n \"600\": #7cb342,\n \"700\": #689f38,\n \"800\": #558b2f,\n \"900\": #33691e,\n \"a100\": #ccff90,\n \"a200\": #b2ff59,\n \"a400\": #76ff03,\n \"a700\": #64dd17\n);\n\n$clr-light-green: map-get($clr-light-green-list, \"base\");\n\n$clr-light-green-50: map-get($clr-light-green-list, \"50\");\n$clr-light-green-100: map-get($clr-light-green-list, \"100\");\n$clr-light-green-200: map-get($clr-light-green-list, \"200\");\n$clr-light-green-300: map-get($clr-light-green-list, \"300\");\n$clr-light-green-400: map-get($clr-light-green-list, \"400\");\n$clr-light-green-500: map-get($clr-light-green-list, \"500\");\n$clr-light-green-600: map-get($clr-light-green-list, \"600\");\n$clr-light-green-700: map-get($clr-light-green-list, \"700\");\n$clr-light-green-800: map-get($clr-light-green-list, \"800\");\n$clr-light-green-900: map-get($clr-light-green-list, \"900\");\n$clr-light-green-a100: map-get($clr-light-green-list, \"a100\");\n$clr-light-green-a200: map-get($clr-light-green-list, \"a200\");\n$clr-light-green-a400: map-get($clr-light-green-list, \"a400\");\n$clr-light-green-a700: map-get($clr-light-green-list, \"a700\");\n\n\n//\n// Lime\n//\n\n$clr-lime-list: (\n \"base\": #cddc39,\n \"50\": #f9fbe7,\n \"100\": #f0f4c3,\n \"200\": #e6ee9c,\n \"300\": #dce775,\n \"400\": #d4e157,\n \"500\": #cddc39,\n \"600\": #c0ca33,\n \"700\": #afb42b,\n \"800\": #9e9d24,\n \"900\": #827717,\n \"a100\": #f4ff81,\n \"a200\": #eeff41,\n \"a400\": #c6ff00,\n \"a700\": #aeea00\n);\n\n$clr-lime: map-get($clr-lime-list, \"base\");\n\n$clr-lime-50: map-get($clr-lime-list, \"50\");\n$clr-lime-100: map-get($clr-lime-list, \"100\");\n$clr-lime-200: map-get($clr-lime-list, \"200\");\n$clr-lime-300: map-get($clr-lime-list, \"300\");\n$clr-lime-400: map-get($clr-lime-list, \"400\");\n$clr-lime-500: map-get($clr-lime-list, \"500\");\n$clr-lime-600: map-get($clr-lime-list, \"600\");\n$clr-lime-700: map-get($clr-lime-list, \"700\");\n$clr-lime-800: map-get($clr-lime-list, \"800\");\n$clr-lime-900: map-get($clr-lime-list, \"900\");\n$clr-lime-a100: map-get($clr-lime-list, \"a100\");\n$clr-lime-a200: map-get($clr-lime-list, \"a200\");\n$clr-lime-a400: map-get($clr-lime-list, \"a400\");\n$clr-lime-a700: map-get($clr-lime-list, \"a700\");\n\n\n//\n// Yellow\n//\n\n$clr-yellow-list: (\n \"base\": #ffeb3b,\n \"50\": #fffde7,\n \"100\": #fff9c4,\n \"200\": #fff59d,\n \"300\": #fff176,\n \"400\": #ffee58,\n \"500\": #ffeb3b,\n \"600\": #fdd835,\n \"700\": #fbc02d,\n \"800\": #f9a825,\n \"900\": #f57f17,\n \"a100\": #ffff8d,\n \"a200\": #ffff00,\n \"a400\": #ffea00,\n \"a700\": #ffd600\n);\n\n$clr-yellow: map-get($clr-yellow-list, \"base\");\n\n$clr-yellow-50: map-get($clr-yellow-list, \"50\");\n$clr-yellow-100: map-get($clr-yellow-list, \"100\");\n$clr-yellow-200: map-get($clr-yellow-list, \"200\");\n$clr-yellow-300: map-get($clr-yellow-list, \"300\");\n$clr-yellow-400: map-get($clr-yellow-list, \"400\");\n$clr-yellow-500: map-get($clr-yellow-list, \"500\");\n$clr-yellow-600: map-get($clr-yellow-list, \"600\");\n$clr-yellow-700: map-get($clr-yellow-list, \"700\");\n$clr-yellow-800: map-get($clr-yellow-list, \"800\");\n$clr-yellow-900: map-get($clr-yellow-list, \"900\");\n$clr-yellow-a100: map-get($clr-yellow-list, \"a100\");\n$clr-yellow-a200: map-get($clr-yellow-list, \"a200\");\n$clr-yellow-a400: map-get($clr-yellow-list, \"a400\");\n$clr-yellow-a700: map-get($clr-yellow-list, \"a700\");\n\n\n//\n// amber\n//\n\n$clr-amber-list: (\n \"base\": #ffc107,\n \"50\": #fff8e1,\n \"100\": #ffecb3,\n \"200\": #ffe082,\n \"300\": #ffd54f,\n \"400\": #ffca28,\n \"500\": #ffc107,\n \"600\": #ffb300,\n \"700\": #ffa000,\n \"800\": #ff8f00,\n \"900\": #ff6f00,\n \"a100\": #ffe57f,\n \"a200\": #ffd740,\n \"a400\": #ffc400,\n \"a700\": #ffab00\n);\n\n$clr-amber: map-get($clr-amber-list, \"base\");\n\n$clr-amber-50: map-get($clr-amber-list, \"50\");\n$clr-amber-100: map-get($clr-amber-list, \"100\");\n$clr-amber-200: map-get($clr-amber-list, \"200\");\n$clr-amber-300: map-get($clr-amber-list, \"300\");\n$clr-amber-400: map-get($clr-amber-list, \"400\");\n$clr-amber-500: map-get($clr-amber-list, \"500\");\n$clr-amber-600: map-get($clr-amber-list, \"600\");\n$clr-amber-700: map-get($clr-amber-list, \"700\");\n$clr-amber-800: map-get($clr-amber-list, \"800\");\n$clr-amber-900: map-get($clr-amber-list, \"900\");\n$clr-amber-a100: map-get($clr-amber-list, \"a100\");\n$clr-amber-a200: map-get($clr-amber-list, \"a200\");\n$clr-amber-a400: map-get($clr-amber-list, \"a400\");\n$clr-amber-a700: map-get($clr-amber-list, \"a700\");\n\n\n//\n// Orange\n//\n\n$clr-orange-list: (\n \"base\": #ff9800,\n \"50\": #fff3e0,\n \"100\": #ffe0b2,\n \"200\": #ffcc80,\n \"300\": #ffb74d,\n \"400\": #ffa726,\n \"500\": #ff9800,\n \"600\": #fb8c00,\n \"700\": #f57c00,\n \"800\": #ef6c00,\n \"900\": #e65100,\n \"a100\": #ffd180,\n \"a200\": #ffab40,\n \"a400\": #ff9100,\n \"a700\": #ff6d00\n);\n\n$clr-orange: map-get($clr-orange-list, \"base\");\n\n$clr-orange-50: map-get($clr-orange-list, \"50\");\n$clr-orange-100: map-get($clr-orange-list, \"100\");\n$clr-orange-200: map-get($clr-orange-list, \"200\");\n$clr-orange-300: map-get($clr-orange-list, \"300\");\n$clr-orange-400: map-get($clr-orange-list, \"400\");\n$clr-orange-500: map-get($clr-orange-list, \"500\");\n$clr-orange-600: map-get($clr-orange-list, \"600\");\n$clr-orange-700: map-get($clr-orange-list, \"700\");\n$clr-orange-800: map-get($clr-orange-list, \"800\");\n$clr-orange-900: map-get($clr-orange-list, \"900\");\n$clr-orange-a100: map-get($clr-orange-list, \"a100\");\n$clr-orange-a200: map-get($clr-orange-list, \"a200\");\n$clr-orange-a400: map-get($clr-orange-list, \"a400\");\n$clr-orange-a700: map-get($clr-orange-list, \"a700\");\n\n\n//\n// Deep orange\n//\n\n$clr-deep-orange-list: (\n \"base\": #ff5722,\n \"50\": #fbe9e7,\n \"100\": #ffccbc,\n \"200\": #ffab91,\n \"300\": #ff8a65,\n \"400\": #ff7043,\n \"500\": #ff5722,\n \"600\": #f4511e,\n \"700\": #e64a19,\n \"800\": #d84315,\n \"900\": #bf360c,\n \"a100\": #ff9e80,\n \"a200\": #ff6e40,\n \"a400\": #ff3d00,\n \"a700\": #dd2c00\n);\n\n$clr-deep-orange: map-get($clr-deep-orange-list, \"base\");\n\n$clr-deep-orange-50: map-get($clr-deep-orange-list, \"50\");\n$clr-deep-orange-100: map-get($clr-deep-orange-list, \"100\");\n$clr-deep-orange-200: map-get($clr-deep-orange-list, \"200\");\n$clr-deep-orange-300: map-get($clr-deep-orange-list, \"300\");\n$clr-deep-orange-400: map-get($clr-deep-orange-list, \"400\");\n$clr-deep-orange-500: map-get($clr-deep-orange-list, \"500\");\n$clr-deep-orange-600: map-get($clr-deep-orange-list, \"600\");\n$clr-deep-orange-700: map-get($clr-deep-orange-list, \"700\");\n$clr-deep-orange-800: map-get($clr-deep-orange-list, \"800\");\n$clr-deep-orange-900: map-get($clr-deep-orange-list, \"900\");\n$clr-deep-orange-a100: map-get($clr-deep-orange-list, \"a100\");\n$clr-deep-orange-a200: map-get($clr-deep-orange-list, \"a200\");\n$clr-deep-orange-a400: map-get($clr-deep-orange-list, \"a400\");\n$clr-deep-orange-a700: map-get($clr-deep-orange-list, \"a700\");\n\n\n//\n// Brown\n//\n\n$clr-brown-list: (\n \"base\": #795548,\n \"50\": #efebe9,\n \"100\": #d7ccc8,\n \"200\": #bcaaa4,\n \"300\": #a1887f,\n \"400\": #8d6e63,\n \"500\": #795548,\n \"600\": #6d4c41,\n \"700\": #5d4037,\n \"800\": #4e342e,\n \"900\": #3e2723,\n);\n\n$clr-brown: map-get($clr-brown-list, \"base\");\n\n$clr-brown-50: map-get($clr-brown-list, \"50\");\n$clr-brown-100: map-get($clr-brown-list, \"100\");\n$clr-brown-200: map-get($clr-brown-list, \"200\");\n$clr-brown-300: map-get($clr-brown-list, \"300\");\n$clr-brown-400: map-get($clr-brown-list, \"400\");\n$clr-brown-500: map-get($clr-brown-list, \"500\");\n$clr-brown-600: map-get($clr-brown-list, \"600\");\n$clr-brown-700: map-get($clr-brown-list, \"700\");\n$clr-brown-800: map-get($clr-brown-list, \"800\");\n$clr-brown-900: map-get($clr-brown-list, \"900\");\n\n\n//\n// Grey\n//\n\n$clr-grey-list: (\n \"base\": #9e9e9e,\n \"50\": #fafafa,\n \"100\": #f5f5f5,\n \"200\": #eeeeee,\n \"300\": #e0e0e0,\n \"400\": #bdbdbd,\n \"500\": #9e9e9e,\n \"600\": #757575,\n \"700\": #616161,\n \"800\": #424242,\n \"900\": #212121,\n);\n\n$clr-grey: map-get($clr-grey-list, \"base\");\n\n$clr-grey-50: map-get($clr-grey-list, \"50\");\n$clr-grey-100: map-get($clr-grey-list, \"100\");\n$clr-grey-200: map-get($clr-grey-list, \"200\");\n$clr-grey-300: map-get($clr-grey-list, \"300\");\n$clr-grey-400: map-get($clr-grey-list, \"400\");\n$clr-grey-500: map-get($clr-grey-list, \"500\");\n$clr-grey-600: map-get($clr-grey-list, \"600\");\n$clr-grey-700: map-get($clr-grey-list, \"700\");\n$clr-grey-800: map-get($clr-grey-list, \"800\");\n$clr-grey-900: map-get($clr-grey-list, \"900\");\n\n\n//\n// Blue grey\n//\n\n$clr-blue-grey-list: (\n \"base\": #607d8b,\n \"50\": #eceff1,\n \"100\": #cfd8dc,\n \"200\": #b0bec5,\n \"300\": #90a4ae,\n \"400\": #78909c,\n \"500\": #607d8b,\n \"600\": #546e7a,\n \"700\": #455a64,\n \"800\": #37474f,\n \"900\": #263238,\n);\n\n$clr-blue-grey: map-get($clr-blue-grey-list, \"base\");\n\n$clr-blue-grey-50: map-get($clr-blue-grey-list, \"50\");\n$clr-blue-grey-100: map-get($clr-blue-grey-list, \"100\");\n$clr-blue-grey-200: map-get($clr-blue-grey-list, \"200\");\n$clr-blue-grey-300: map-get($clr-blue-grey-list, \"300\");\n$clr-blue-grey-400: map-get($clr-blue-grey-list, \"400\");\n$clr-blue-grey-500: map-get($clr-blue-grey-list, \"500\");\n$clr-blue-grey-600: map-get($clr-blue-grey-list, \"600\");\n$clr-blue-grey-700: map-get($clr-blue-grey-list, \"700\");\n$clr-blue-grey-800: map-get($clr-blue-grey-list, \"800\");\n$clr-blue-grey-900: map-get($clr-blue-grey-list, \"900\");\n\n\n//\n// Black\n//\n\n$clr-black-list: (\n \"base\": #000\n);\n\n$clr-black: map-get($clr-black-list, \"base\");\n\n\n//\n// White\n//\n\n$clr-white-list: (\n \"base\": #fff\n);\n\n$clr-white: map-get($clr-white-list, \"base\");\n\n\n//\n// List for all Colors for looping\n//\n\n$clr-list-all: (\n \"red\": $clr-red-list,\n \"pink\": $clr-pink-list,\n \"purple\": $clr-purple-list,\n \"deep-purple\": $clr-deep-purple-list,\n \"indigo\": $clr-indigo-list,\n \"blue\": $clr-blue-list,\n \"light-blue\": $clr-light-blue-list,\n \"cyan\": $clr-cyan-list,\n \"teal\": $clr-teal-list,\n \"green\": $clr-green-list,\n \"light-green\": $clr-light-green-list,\n \"lime\": $clr-lime-list,\n \"yellow\": $clr-yellow-list,\n \"amber\": $clr-amber-list,\n \"orange\": $clr-orange-list,\n \"deep-orange\": $clr-deep-orange-list,\n \"brown\": $clr-brown-list,\n \"grey\": $clr-grey-list,\n \"blue-grey\": $clr-blue-grey-list,\n \"black\": $clr-black-list,\n \"white\": $clr-white-list\n);\n\n\n//\n// Typography\n//\n\n$clr-ui-display-4: $clr-grey-600;\n$clr-ui-display-3: $clr-grey-600;\n$clr-ui-display-2: $clr-grey-600;\n$clr-ui-display-1: $clr-grey-600;\n$clr-ui-headline: $clr-grey-900;\n$clr-ui-title: $clr-grey-900;\n$clr-ui-subhead-1: $clr-grey-900;\n$clr-ui-body-2: $clr-grey-900;\n$clr-ui-body-1: $clr-grey-900;\n$clr-ui-caption: $clr-grey-600;\n$clr-ui-menu: $clr-grey-900;\n$clr-ui-button: $clr-grey-900;\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Footnote container\n .footnote {\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n\n // Footnote list - omit left indentation\n > ol {\n margin-left: 0;\n\n // Footnote item - footnote items can contain lists, so we need to scope\n // the spacing adjustments to the top-level footnote item.\n > li {\n transition: color 125ms;\n\n // Darken color on target\n &:target {\n color: var(--md-default-fg-color);\n }\n\n // Show backreferences on footnote hover\n &:hover .footnote-backref,\n &:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0;\n }\n }\n }\n }\n\n // Footnote reference\n .footnote-ref {\n font-weight: 700;\n font-size: px2em(12px, 16px);\n\n // Hack: increase specificity to override default\n html & {\n outline-offset: px2rem(2px);\n }\n }\n\n // Footnote backreference\n .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n // Hack: omit Unicode arrow for replacement with icon\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(px2rem(5px));\n opacity: 0;\n transition:\n color 250ms,\n transform 250ms 250ms,\n opacity 125ms 250ms;\n\n // [print]: Show footnote backreferences\n @media print {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-5px));\n }\n\n // Adjust color on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Footnote backreference icon\n &::before {\n display: inline-block;\n width: px2rem(16px);\n height: px2rem(16px);\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footnote reference wrapper\n [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n\n // Show outline for all devices\n > .footnote-ref {\n outline: auto;\n }\n }\n\n // Footnote wrapper\n [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Headerlink\n .headerlink {\n display: inline-block;\n margin-left: px2rem(10px);\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition:\n color 250ms,\n opacity 125ms;\n\n // [print]: Hide headerlinks\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(10px);\n margin-left: initial;\n }\n }\n\n // Show headerlinks on parent hover\n :hover > .headerlink,\n :target > .headerlink,\n .headerlink:focus {\n opacity: 1;\n transition:\n color 250ms,\n opacity 125ms;\n }\n\n // Adjust color on parent target or focus/hover\n :target > .headerlink,\n .headerlink:focus,\n .headerlink:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Adjust scroll offset for all elements with `id` attributes - general scroll\n // margin offset for anything that can be targeted. Browser support is pretty\n // decent by now, but Edge <79 and Safari (iOS and macOS) still don't support\n // it properly, so we settle with a cross-browser anchor correction solution.\n :target {\n scroll-margin-top: px2rem(48px + 24px);\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: px2rem(96px + 24px);\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 1-3\n h1:target,\n h2:target,\n h3:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px - 4px);\n padding-top: px2rem(96px + 24px - 4px);\n }\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 4\n h4:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px - 3px);\n padding-top: px2rem(96px + 24px - 3px);\n }\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 5-6\n h5:target,\n h6:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px);\n padding-top: px2rem(48px + 24px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px);\n padding-top: px2rem(96px + 24px);\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Arithmatex container\n div.arithmatex {\n overflow: auto;\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n margin: 0 px2rem(-16px);\n }\n\n // Arithmatex content\n > * {\n width: min-content;\n // stylelint-disable-next-line declaration-no-important\n margin: 1em auto !important;\n padding: 0 px2rem(16px);\n touch-action: auto;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Deletion, addition or comment\n del.critic,\n ins.critic,\n .critic.comment {\n box-decoration-break: clone;\n }\n\n // Deletion\n del.critic {\n background-color: var(--md-typeset-del-color);\n }\n\n // Addition\n ins.critic {\n background-color: var(--md-typeset-ins-color);\n }\n\n // Comment\n .critic.comment {\n color: var(--md-code-hl-comment-color);\n\n // Comment opening mark\n &::before {\n content: \"/* \";\n }\n\n // Comment closing mark\n &::after {\n content: \" */\";\n }\n }\n\n // Critic block\n .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: px2rem(16px);\n padding-left: px2rem(16px);\n overflow: auto;\n box-shadow: none;\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0.5em;\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0.5em;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Details\n details {\n @extend .admonition;\n\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n\n // Details title icon - rotate icon on transition to open state\n &[open] > summary::after {\n transform: rotate(90deg);\n }\n\n // Adjust spacing for details in closed state\n &:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n\n // Hack: we cannot set `overflow: hidden` on the `details` element (which\n // is why we set it to `overflow: visible`, as the outline would not be\n // visible when focusing. Therefore, we must set the border radius on the\n // summary explicitly.\n > summary {\n border-radius: px2rem(2px);\n }\n }\n\n // Hack: omit margin collapse\n &::after {\n display: table;\n content: \"\";\n }\n }\n\n // Details title\n summary {\n @extend .admonition-title;\n\n display: block;\n min-height: px2rem(20px);\n padding: px2rem(8px) px2rem(36px) px2rem(8px) px2rem(40px);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Details marker\n &::after {\n position: absolute;\n top: px2rem(8px);\n right: px2rem(8px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(8px);\n transform: rotate(180deg);\n }\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Emoji and icon container\n .emojione,\n .twemoji,\n .gemoji {\n display: inline-flex;\n height: px2em(18px);\n vertical-align: text-top;\n\n // Icon - inlined via mkdocs-material-extensions\n svg {\n width: px2em(18px);\n max-height: 100%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: syntax highlighting\n// ----------------------------------------------------------------------------\n\n// Code block\n.highlight {\n .o, // Operator\n .ow { // Operator, word\n color: var(--md-code-hl-operator-color);\n }\n\n .p { // Punctuation\n color: var(--md-code-hl-punctuation-color);\n }\n\n .cpf, // Comment, preprocessor file\n .l, // Literal\n .s, // Literal, string\n .sb, // Literal, string backticks\n .sc, // Literal, string char\n .s2, // Literal, string double\n .si, // Literal, string interpol\n .s1, // Literal, string single\n .ss { // Literal, string symbol\n color: var(--md-code-hl-string-color);\n }\n\n .cp, // Comment, pre-processor\n .se, // Literal, string escape\n .sh, // Literal, string heredoc\n .sr, // Literal, string regex\n .sx { // Literal, string other\n color: var(--md-code-hl-special-color);\n }\n\n .m, // Number\n .mb, // Number, binary\n .mf, // Number, float\n .mh, // Number, hex\n .mi, // Number, integer\n .il, // Number, integer long\n .mo { // Number, octal\n color: var(--md-code-hl-number-color);\n }\n\n .k, // Keyword,\n .kd, // Keyword, declaration\n .kn, // Keyword, namespace\n .kp, // Keyword, pseudo\n .kr, // Keyword, reserved\n .kt { // Keyword, type\n color: var(--md-code-hl-keyword-color);\n }\n\n .kc, // Keyword, constant\n .n { // Name\n color: var(--md-code-hl-name-color);\n }\n\n .no, // Name, constant\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n .nf, // Name, function\n .nn { // Name, namespace\n color: var(--md-code-hl-function-color);\n }\n\n .nd, // Name, decorator\n .ni, // Name, entity\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-keyword-color);\n }\n\n .c, // Comment\n .cm, // Comment, multiline\n .c1, // Comment, single\n .ch, // Comment, shebang\n .cs, // Comment, special\n .sd { // Literal, string doc\n color: var(--md-code-hl-comment-color);\n }\n\n .na, // Name, attribute\n .nv, // Variable,\n .vc, // Variable, class\n .vg, // Variable, global\n .vi { // Variable, instance\n color: var(--md-code-hl-variable-color);\n }\n\n .ge, // Generic, emph\n .gr, // Generic, error\n .gh, // Generic, heading\n .go, // Generic, output\n .gp, // Generic, prompt\n .gs, // Generic, strong\n .gu, // Generic, subheading\n .gt { // Generic, traceback\n color: var(--md-code-hl-generic-color);\n }\n\n .gd, // Diff, delete\n .gi { // Diff, insert\n margin: 0 px2em(-2px);\n padding: 0 px2em(2px);\n border-radius: px2rem(2px);\n }\n\n .gd { // Diff, delete\n background-color: var(--md-typeset-del-color);\n }\n\n .gi { // Diff, insert\n background-color: var(--md-typeset-ins-color);\n }\n\n // Highlighted line\n .hll {\n display: block;\n margin: 0 px2em(-16px, 13.6px);\n padding: 0 px2em(16px, 13.6px);\n background-color: var(--md-code-hl-color);\n }\n\n // Code block line numbers (inline)\n [data-linenos]::before {\n position: sticky;\n left: px2em(-16px, 13.6px);\n float: left;\n margin-right: px2em(16px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Code block with line numbers\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n\n // Set table elements to block layout, because otherwise the whole flexbox\n // hacking won't work correctly\n tbody,\n td {\n display: block;\n padding: 0;\n }\n\n // We need to use flexbox layout, because otherwise it's not possible to\n // make the code container scroll while keeping the line numbers static\n tr {\n display: flex;\n }\n\n // The pre tags are nested inside a table, so we need to omit the margin\n // because it collapses below all the overflows\n pre {\n margin: 0;\n }\n\n // Code block line numbers - disable user selection, so code can be easily\n // copied without accidentally also copying the line numbers\n .linenos {\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n padding-right: 0;\n font-size: px2em(13.6px);\n background-color: var(--md-code-bg-color);\n user-select: none;\n }\n\n // Code block line numbers container\n .linenodiv {\n padding-right: px2em(8px, 13.6px);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n\n // Adjust colors and alignment\n pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n }\n }\n\n // Code block container - stretch to remaining space\n .code {\n flex: 1;\n overflow: hidden;\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Code block with line numbers\n .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: px2rem(2px);\n\n // Omit rounded borders on contained code block\n code {\n border-radius: 0;\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Top-level code block\n > .highlight {\n margin: 1em px2rem(-16px);\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n\n // Omit rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // Top-level code block with line numbers\n > .highlighttable {\n margin: 1em px2rem(-16px);\n border-radius: 0;\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: legacy implementation (deprecated, removed in v8)\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tabbed content\n .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest);\n\n // [print]: Show all tabs (even hidden ones) when printing\n @media print {\n display: block;\n order: initial;\n }\n\n // Code block is the only child of a tab - remove margin and mirror\n // previous (now deprecated) SuperFences code block grouping behavior\n > pre:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Omit rounded borders\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Adjust spacing for nested tab\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tabbed container\n .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: px2rem(2px);\n\n // Tab radio button - the Tabbed extension will generate radio buttons with\n // labels, so tabs can be triggered without the necessity for JavaScript.\n // This is pretty cool, as it has great accessibility out-of-the box, so\n // we just hide the radio button and toggle the label color for indication.\n > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Tab label for checked radio button\n &:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n\n // Show tabbed block content\n + .tabbed-content {\n display: block;\n }\n }\n\n // Tab label on focus\n &:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n border-bottom: px2rem(2px) solid transparent;\n cursor: pointer;\n transition: color 250ms;\n\n // Tab label on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Placeholders: improve colocation for better compression\n// ----------------------------------------------------------------------------\n\n// Tab label placeholder\n%tabbed-label {\n\n // [screen]: Show active state\n @media screen {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n}\n\n// Tab label on keyboard focus placeholder\n%tabbed-label-focus-visible {\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n// Tab content placeholder\n%tabbed-content {\n display: block;\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset { // stylelint-disable-line\n\n // Tabbed labels\n .tabbed-labels {\n display: flex;\n max-width: 100vw;\n overflow: auto;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: x proximity;\n -ms-overflow-style: none; // IE, Edge\n scrollbar-width: none; // Firefox\n\n // [print]: Move one layer up for ordering\n @media print {\n display: contents;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n display: none; // Chrome, Safari\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n white-space: nowrap;\n border-bottom: px2rem(2px) solid transparent;\n scroll-snap-align: start;\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n transition:\n background-color 250ms,\n color 250ms;\n\n // [print]: Intersperse labels with containers\n @media print {\n\n // Ensure correct order of labels\n @for $i from 1 through 10 {\n &:nth-child(#{$i}) {\n order: $i;\n }\n }\n }\n\n // Tab label on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Top-level tabbed labels\n > .tabbed-alternate .tabbed-labels {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n scroll-padding: 0 px2rem(16px);\n }\n }\n\n // Tabbed container\n .tabbed-alternate {\n flex-direction: column;\n\n // Tabbed content\n .tabbed-content {\n display: initial;\n order: initial;\n width: 100%;\n box-shadow: initial;\n\n // [print]: Move one layer up for ordering\n @media print {\n display: contents;\n }\n }\n\n // Tabbed block\n .tabbed-block {\n display: none;\n\n // [print]: Intersperse labels with containers\n @media print {\n display: block;\n\n // Ensure correct order of containers\n @for $i from 1 through 10 {\n &:nth-child(#{$i}) {\n order: $i;\n }\n }\n }\n\n // Code block is the only child of a tab - remove margin and mirror\n // previous (now deprecated) SuperFences code block grouping behavior\n > pre:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Omit rounded borders\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Adjust spacing for nested tabbed container\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tab label states\n @for $i from 10 through 1 {\n input:nth-child(#{$i}) {\n\n // Tab is active\n &:checked {\n\n // Tab label\n ~ .tabbed-labels > :nth-child(#{$i}) {\n @extend %tabbed-label;\n }\n\n // Tab content\n ~ .tabbed-content > :nth-child(#{$i}) {\n @extend %tabbed-content;\n }\n }\n\n // Tab label on keyboard focus\n &.focus-visible ~ .tabbed-labels > :nth-child(#{$i}) {\n @extend %tabbed-label-focus-visible;\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tasklist item\n .task-list-item {\n position: relative;\n list-style-type: none;\n\n // Make checkbox items align with normal list items, but position\n // everything in ems for correct layout at smaller font sizes\n [type=\"checkbox\"] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: -2em;\n left: initial;\n }\n }\n }\n\n // Hide native checkbox, when custom classes are enabled\n .task-list-control [type=\"checkbox\"] {\n z-index: -1;\n opacity: 0;\n }\n\n // Tasklist indicator in unchecked state\n .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: px2em(-24px);\n width: px2em(20px);\n height: px2em(20px);\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2em(-24px);\n left: initial;\n }\n }\n\n // Tasklist indicator in checked state\n [type=\"checkbox\"]:checked + .task-list-indicator::before {\n background-color: $clr-green-a400;\n mask-image: var(--md-tasklist-icon--checked);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // [tablet +]: Allow for rendering content as sidebars\n @include break-from-device(tablet) {\n\n // Modifier to float block elements\n .inline {\n float: left;\n width: px2rem(234px);\n margin-top: 0;\n margin-right: px2rem(16px);\n margin-bottom: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n }\n\n // Modifier to move to end (ltr: right, rtl: left)\n &.end {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(16px);\n margin-left: 0;\n }\n }\n }\n }\n}\n"]} \ No newline at end of file diff --git a/assets/stylesheets/main.8b42a75e.min.css b/assets/stylesheets/main.8b42a75e.min.css deleted file mode 100644 index 06cbf39a6..000000000 --- a/assets/stylesheets/main.8b42a75e.min.css +++ /dev/null @@ -1,2 +0,0 @@ -@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:content-box;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:transparent;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-default-fg-color:rgba(0,0,0,0.87);--md-default-fg-color--light:rgba(0,0,0,0.54);--md-default-fg-color--lighter:rgba(0,0,0,0.32);--md-default-fg-color--lightest:rgba(0,0,0,0.07);--md-default-bg-color:#fff;--md-default-bg-color--light:hsla(0,0%,100%,0.7);--md-default-bg-color--lighter:hsla(0,0%,100%,0.3);--md-default-bg-color--lightest:hsla(0,0%,100%,0.12);--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:hsla(0,0%,100%,0.7);--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:rgba(82,108,254,0.1);--md-accent-bg-color:#fff;--md-accent-bg-color--light:hsla(0,0%,100%,0.7)}:root>*{--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-hl-color:rgba(255,255,0,0.5);--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-mark-color:rgba(255,255,0,0.5);--md-typeset-del-color:hsla(6,90%,60%,0.15);--md-typeset-ins-color:rgba(11,213,112,0.15);--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-table-color:rgba(0,0,0,0.12);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-fg-color:#fff;--md-footer-fg-color--light:hsla(0,0%,100%,0.7);--md-footer-fg-color--lighter:hsla(0,0%,100%,0.3);--md-footer-bg-color:rgba(0,0,0,0.87);--md-footer-bg-color--dark:rgba(0,0,0,0.32)}.md-icon svg{fill:currentColor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{font-feature-settings:"kern","liga";font-family:var(--md-text-font-family,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}body,code,input,kbd,pre{color:var(--md-typeset-color)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family,_),SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin:1em 0}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{background-color:transparent;box-shadow:none;margin:initial;padding:initial}.md-typeset a code{color:currentColor}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:44.9375em){.md-typeset>pre{margin:1em -.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help;text-decoration:none}@media (hover:none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus:after,.md-typeset abbr[title]:hover:after{background-color:var(--md-default-fg-color);border-radius:.1rem;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);color:var(--md-default-bg-color);content:attr(title);display:inline-block;font-size:.7rem;left:0;margin-top:2em;max-width:80%;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;padding:.2rem .3rem;position:absolute;width:auto}}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-left:0;margin-right:.078125em}.md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter);color:var(--md-default-fg-color--light);padding-left:.6rem}[dir=rtl] .md-typeset blockquote{border-left:initial;border-right:.2rem solid var(--md-default-fg-color--lighter);padding-left:0;padding-right:.6rem}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{display:flow-root;margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-left:0;margin-right:.625em}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-left:0;margin-right:1.25em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-left:0;margin-right:.625em}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-left:0;margin-right:1.875em}.md-typeset img,.md-typeset svg{height:auto;max-width:100%}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:0 auto;max-width:100%;text-align:center;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block}.md-typeset figcaption{font-style:italic;margin:1em auto 2em;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;margin-left:.5em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.9375em){body[data-md-state=lock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}.md-announce{background-color:var(--md-footer-bg-color);overflow:auto}@media print{.md-announce{display:none}}.md-announce__inner{color:var(--md-footer-fg-color);font-size:.7rem;margin:.6rem auto;padding:0 .8rem}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;position:absolute;right:.5em;top:.5em;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentColor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-content{flex-grow:1;overflow:hidden;scroll-padding-top:51.2rem}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){.md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:.8rem;margin-right:1.2rem}.md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem;margin-right:.8rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0 .4rem .4rem;padding:0}@media print{.md-content__button{display:none}}[dir=rtl] .md-content__button{float:left;margin-left:0;margin-right:.4rem}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);left:auto;min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;right:.8rem;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:3}@media print{.md-dialog{display:none}}[dir=rtl] .md-dialog{left:.8rem;right:auto}.md-dialog[data-md-state=open]{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-typeset .md-input{border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 .025rem .05rem rgba(0,0,0,.1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{box-shadow:0 .4rem 1rem rgba(0,0,0,.15),0 .025rem .05rem rgba(0,0,0,.15)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem transparent,0 .2rem .4rem transparent;color:var(--md-primary-bg-color);left:0;position:-webkit-sticky;position:sticky;right:0;top:0;z-index:3}@media print{.md-header{display:none}}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header[data-md-state=hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentColor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.1875em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentColor;display:block;height:1.2rem;width:1.2rem}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem;margin-left:1rem;margin-right:.4rem}.md-header__title[data-md-state=active] .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic{transform:translateX(1.25rem)}.md-header__title[data-md-state=active] .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__source{display:none}@media screen and (min-width:60em){.md-header__source{display:block;margin-left:1rem;max-width:11.7rem;width:11.7rem}[dir=rtl] .md-header__source{margin-left:0;margin-right:1rem}}@media screen and (min-width:76.25em){.md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{overflow:auto;padding:.2rem}.md-footer__link{display:flex;outline-color:var(--md-accent-fg-color);padding-bottom:.4rem;padding-top:1.4rem;transition:opacity .25s}@media screen and (min-width:45em){.md-footer__link{width:50%}}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}.md-footer__link--prev{float:left}@media screen and (max-width:44.9375em){.md-footer__link--prev{width:25%}.md-footer__link--prev .md-footer__title{display:none}}[dir=rtl] .md-footer__link--prev{float:right}[dir=rtl] .md-footer__link--prev svg{transform:scaleX(-1)}.md-footer__link--next{float:right;text-align:right}@media screen and (max-width:44.9375em){.md-footer__link--next{width:75%}}[dir=rtl] .md-footer__link--next{float:left;text-align:left}[dir=rtl] .md-footer__link--next svg{transform:scaleX(-1)}.md-footer__title{flex-grow:1;font-size:.9rem;line-height:2.4rem;max-width:calc(100% - 2.4rem);padding:0 1rem;position:relative}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;left:0;margin-top:-1rem;opacity:.7;padding:0 1rem;position:absolute;right:0}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-footer-social__link:before{line-height:1.9}.md-footer-social__link svg{fill:currentColor;max-height:.8rem;vertical-align:-25%}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentColor;display:block;height:2.4rem;width:2.4rem}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__item{padding:0 .6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-left:0;padding-right:.6rem}.md-nav__link{cursor:pointer;display:block;margin-top:.625em;overflow:hidden;scroll-snap-align:start;text-overflow:ellipsis;transition:color 125ms}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentColor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__source{display:none}@media screen and (max-width:76.1875em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;font-weight:400;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;left:.4rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{left:auto;right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentColor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;top:.2rem}[dir=rtl] .md-nav--primary .md-nav__title .md-logo{left:auto;right:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest);padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-left:2.4rem;padding-right:.8rem}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem;position:relative}.md-nav--primary .md-nav__link .md-nav__icon{color:inherit;font-size:1.2rem;height:1.2rem;margin-top:-.6rem;position:absolute;right:.6rem;top:50%;width:1.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{left:.6rem;right:auto}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentColor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{background-color:transparent;position:static}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:0;padding-right:1.4rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:2rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:2.6rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:0;padding-right:3.2rem}.md-nav--secondary{background-color:transparent}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.9375em){.md-nav--primary .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link[for=__toc]{padding-left:2.4rem;padding-right:.8rem}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-nav--integrated .md-nav__link[for=__toc]{display:block;padding-right:2.4rem;scroll-snap-align:none}[dir=rtl] .md-nav--integrated .md-nav__link[for=__toc]{padding-left:2.4rem;padding-right:.8rem}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width:76.25em){.md-nav{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav,.md-nav__toggle:indeterminate~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{display:none}.md-nav__item--section>.md-nav{display:block}.md-nav__item--section>.md-nav>.md-nav__title{display:block;padding:0;pointer-events:none;scroll-snap-align:start}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{float:right;height:.9rem;transition:transform .25s;width:.9rem}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon:after{background-color:currentColor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:-.1rem;width:100%}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon,.md-nav__item--nested .md-nav__toggle:indeterminate~.md-nav__link .md-nav__icon{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item--nested,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav>.md-nav__title{display:block;padding:0 .6rem;pointer-events:none;scroll-snap-align:start}.md-nav--lifted .md-nav[data-md-level="1"]{display:block}.md-nav--lifted .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-right:.6rem}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{border-left:.05rem solid var(--md-primary-fg-color);display:block;margin-bottom:1.25em}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav>.md-nav__title{display:none}}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.9375em){.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;left:-2.2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[dir=rtl] .md-search__overlay{left:auto;right:-2.2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){.md-search__overlay{background-color:rgba(0,0,0,.54);cursor:pointer;height:0;left:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[dir=rtl] .md-search__overlay{left:auto;right:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.9375em){.md-search__inner{height:0;left:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{left:auto;right:0;transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){.md-search__inner{float:right;padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width:60em) and (max-width:76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem transparent;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:rgba(0,0,0,.26);border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:hsla(0,0%,100%,.12)}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem rgba(0,0,0,.07);color:var(--md-default-fg-color)}.md-search__input{background:transparent;font-size:.9rem;height:100%;padding:0 2.2rem 0 3.6rem;position:relative;text-overflow:ellipsis;width:100%;z-index:2}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s;transition:color .25s}.md-search__input::-moz-placeholder{-moz-transition:color .25s;transition:color .25s}.md-search__input::-ms-input-placeholder{-ms-transition:color .25s;transition:color .25s}.md-search__input::placeholder{transition:color .25s}.md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.9375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){.md-search__input{color:inherit;font-size:.8rem;padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input::-webkit-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{left:.5rem;position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search]{left:auto;right:.5rem}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.9375em){.md-search__icon[for=__search]{left:.8rem;top:.6rem}[dir=rtl] .md-search__icon[for=__search]{left:auto;right:.8rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__options{pointer-events:none;position:absolute;right:.5rem;top:.3rem;z-index:2}[dir=rtl] .md-search__options{left:.5rem;right:auto}@media screen and (max-width:59.9375em){.md-search__options{right:.8rem;top:.6rem}[dir=rtl] .md-search__options{left:.8rem;right:auto}}.md-search__options>*{color:var(--md-default-fg-color--light);margin-left:.2rem;opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>*{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>:hover{opacity:.7}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;padding:0 2.2rem 0 3.6rem;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}[dir=rtl] .md-search__suggest{padding:0 3.6rem 0 2.2rem}@media screen and (min-width:60em){.md-search__suggest{font-size:.8rem;padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}.md-search__output{border-radius:0 0 .1rem .1rem;overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.9375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-left:0;padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0}.md-search-result__item{box-shadow:0 -.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{color:var(--md-typeset-a-color);cursor:pointer;display:block;font-size:.64rem;outline:none;padding:.75em .8rem;scroll-snap-align:start;transition:color .25s,background-color .25s}@media screen and (min-width:60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-left:.8rem;padding-right:2.2rem}}.md-search-result__more summary:focus,.md-search-result__more summary:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more summary::-webkit-details-marker,.md-search-result__more summary::marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-left:.8rem;padding-right:2.2rem}}.md-search-result__article--document .md-search-result__title{font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;left:0;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.9375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentColor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon{left:auto;right:0}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result__title{font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result__teaser{-webkit-box-orient:vertical;-webkit-line-clamp:2;color:var(--md-default-fg-color--light);display:-webkit-box;font-size:.64rem;line-height:1.6;margin:.5em 0;max-height:2rem;overflow:hidden;text-overflow:ellipsis}@media screen and (max-width:44.9375em){.md-search-result__teaser{-webkit-line-clamp:3;max-height:3rem}}@media screen and (min-width:60em) and (max-width:76.1875em){.md-search-result__teaser{-webkit-line-clamp:3;max-height:3rem}}.md-search-result__teaser mark{background-color:transparent;text-decoration:underline}.md-search-result__terms{font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:transparent;color:var(--md-accent-fg-color)}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid transparent;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid transparent;border-right:.2rem solid transparent;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}.md-select__link{cursor:pointer;display:block;outline:none;padding-left:.6rem;padding-right:1.2rem;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:-webkit-sticky;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.1875em){.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;left:-12.1rem;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:4}[dir=rtl] .md-sidebar--primary{left:auto;right:-12.1rem}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;-ms-scroll-snap-type:none;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) transparent;scrollbar-width:thin}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width:76.1875em){.md-overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:4}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@-webkit-keyframes facts{0%{height:0}to{height:.65rem}}@keyframes facts{0%{height:0}to{height:.65rem}}@-webkit-keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}.md-source__icon svg{margin-left:.6rem;margin-top:.6rem}[dir=rtl] .md-source__icon svg{margin-left:0;margin-right:.6rem}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-left:0;margin-right:-2rem;padding-left:0;padding-right:2rem}.md-source__repository{display:inline-block;margin-left:.6rem;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{font-size:.55rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0}[data-md-state=done] .md-source__facts{-webkit-animation:facts .25s ease-in;animation:facts .25s ease-in}.md-source__fact{display:inline-block}[data-md-state=done] .md-source__fact{-webkit-animation:fact .4s ease-out;animation:fact .4s ease-out}.md-source__fact:before{background-color:currentColor;content:"";display:inline-block;height:.6rem;margin-right:.1rem;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2):before{margin-left:.4rem}[dir=rtl] .md-source__fact{margin-left:.1rem;margin-right:0}[dir=rtl] .md-source__fact:nth-child(1n+2):before{margin-left:0;margin-right:.4rem}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);overflow:auto;width:100%}@media print{.md-tabs{display:none}}@media screen and (max-width:76.1875em){.md-tabs{display:none}}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs__list{contain:content;list-style:none;margin:0 0 0 .2rem;padding:0;white-space:nowrap}[dir=rtl] .md-tabs__list{margin-left:0;margin-right:.2rem}.md-tabs__item{display:inline-block;height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link--active,.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color--light);font-size:.7rem;margin-left:50%;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{float:left}.md-top[data-md-state=hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@-webkit-keyframes hoverfix{0%{pointer-events:none}}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}.md-version__current{color:inherit;cursor:pointer;margin-left:1.4rem;margin-right:.4rem;outline:none;position:relative;top:.05rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current:after{background-color:currentColor;content:"";display:inline-block;height:.6rem;margin-left:.4rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;width:.4rem}[dir=rtl] .md-version__current:after{margin-left:0;margin-right:.4rem}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.1),0 0 .05rem rgba(0,0,0,.25);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:1}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (pointer:coarse){.md-version:hover .md-version__list{-webkit-animation:hoverfix .25s forwards;animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{-webkit-animation:none;animation:none}}.md-version__item{line-height:1.8rem}.md-version__link{cursor:pointer;display:block;outline:none;padding-left:.6rem;padding-right:1.2rem;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 .025rem .05rem rgba(0,0,0,.05);color:var(--md-admonition-fg-color);font-size:.64rem;margin:1.5625em 0;overflow:hidden;padding:0 .6rem;page-break-inside:avoid}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-left:none;border-right:.2rem solid #448aff}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition-title,.md-typeset summary{background-color:rgba(68,138,255,.1);border-left:.2rem solid #448aff;font-weight:700;margin:0 -.6rem 0 -.8rem;padding:.4rem .6rem .4rem 2rem;position:relative}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-left:none;border-right:.2rem solid #448aff;margin:0 -.8rem 0 -.6rem;padding:.4rem 2rem .4rem .6rem}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;left:.6rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;width:1rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{left:auto;right:.6rem}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1);border-color:#448aff}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary{background-color:rgba(0,176,255,.1);border-color:#00b0ff}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before,.md-typeset .summary>.admonition-title:before,.md-typeset .summary>summary:before,.md-typeset .tldr>.admonition-title:before,.md-typeset .tldr>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1);border-color:#00b8d4}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before,.md-typeset .todo>.admonition-title:before,.md-typeset .todo>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .hint>.admonition-title,.md-typeset .hint>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:rgba(0,191,165,.1);border-color:#00bfa5}.md-typeset .hint>.admonition-title:before,.md-typeset .hint>summary:before,.md-typeset .important>.admonition-title:before,.md-typeset .important>summary:before,.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-color:#00c853}.md-typeset .check>.admonition-title,.md-typeset .check>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:rgba(0,200,83,.1);border-color:#00c853}.md-typeset .check>.admonition-title:before,.md-typeset .check>summary:before,.md-typeset .done>.admonition-title:before,.md-typeset .done>summary:before,.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-color:#64dd17}.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary,.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:rgba(100,221,23,.1);border-color:#64dd17}.md-typeset .faq>.admonition-title:before,.md-typeset .faq>summary:before,.md-typeset .help>.admonition-title:before,.md-typeset .help>summary:before,.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-color:#ff9100}.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary,.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:rgba(255,145,0,.1);border-color:#ff9100}.md-typeset .attention>.admonition-title:before,.md-typeset .attention>summary:before,.md-typeset .caution>.admonition-title:before,.md-typeset .caution>summary:before,.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-color:#ff5252}.md-typeset .fail>.admonition-title,.md-typeset .fail>summary,.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary{background-color:rgba(255,82,82,.1);border-color:#ff5252}.md-typeset .fail>.admonition-title:before,.md-typeset .fail>summary:before,.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before,.md-typeset .missing>.admonition-title:before,.md-typeset .missing>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1);border-color:#ff1744}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before,.md-typeset .error>.admonition-title:before,.md-typeset .error>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1);border-color:#f50057}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(124,77,255,.1);border-color:#7c4dff}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .cite>.admonition-title,.md-typeset .cite>summary,.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:hsla(0,0%,62%,.1);border-color:#9e9e9e}.md-typeset .cite>.admonition-title:before,.md-typeset .cite>summary:before,.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote>ol{margin-left:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentColor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}.md-typeset [id^="fnref:"]:target{margin-top:-3.4rem;padding-top:3.4rem;scroll-margin-top:0}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset [id^="fn:"]:target{margin-top:-3.45rem;padding-top:3.45rem;scroll-margin-top:0}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;margin-left:.5rem;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}[dir=rtl] .md-typeset .headerlink{margin-left:0;margin-right:.5rem}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{scroll-margin-top:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{scroll-margin-top:0}.md-typeset h1:target:before,.md-typeset h2:target:before,.md-typeset h3:target:before{content:"";display:block;margin-top:-3.4rem;padding-top:3.4rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h1:target,.md-header--lifted~.md-container .md-typeset h2:target,.md-header--lifted~.md-container .md-typeset h3:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h1:target:before,.md-header--lifted~.md-container .md-typeset h2:target:before,.md-header--lifted~.md-container .md-typeset h3:target:before{margin-top:-5.8rem;padding-top:5.8rem}}.md-typeset h4:target{scroll-margin-top:0}.md-typeset h4:target:before{content:"";display:block;margin-top:-3.45rem;padding-top:3.45rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h4:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h4:target:before{margin-top:-5.85rem;padding-top:5.85rem}}.md-typeset h5:target,.md-typeset h6:target{scroll-margin-top:0}.md-typeset h5:target:before,.md-typeset h6:target:before{content:"";display:block;margin-top:-3.6rem;padding-top:3.6rem}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset h5:target,.md-header--lifted~.md-container .md-typeset h6:target{scroll-margin-top:0}.md-header--lifted~.md-container .md-typeset h5:target:before,.md-header--lifted~.md-container .md-typeset h6:target:before{margin-top:-6rem;padding-top:6rem}}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.9375em){.md-typeset div.arithmatex{margin:0 -.8rem}}.md-typeset div.arithmatex>*{margin:1em auto!important;padding:0 .8rem;touch-action:auto;width:-webkit-min-content;width:-moz-min-content;width:min-content}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details:after{content:"";display:table}.md-typeset summary{border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer;display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2rem}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset summary:after{background-color:currentColor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;right:.4rem;top:.4rem;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{left:.4rem;right:auto;transform:rotate(180deg)}.md-typeset summary::-webkit-details-marker,.md-typeset summary::marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{display:inline-flex;height:1.125em;vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentColor;max-height:100%;width:1.125em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color);display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:-webkit-sticky;position:sticky;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable{display:flow-root;overflow:hidden}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;padding-right:.5882352941em}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .code{flex:1;overflow:hidden}.md-typeset .highlighttable{border-radius:.1rem;direction:ltr;margin:1em 0}.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width:44.9375em){.md-typeset>.highlight{margin:1em -.8rem}.md-typeset>.highlight .hll{margin:0 -.8rem;padding:0 .8rem}.md-typeset>.highlight code{border-radius:0}.md-typeset>.highlighttable{border-radius:0;margin:1em -.8rem}.md-typeset>.highlighttable .hll{margin:0 -.8rem;padding:0 .8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-left-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-left-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-right-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-right-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-left-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-right-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}.md-typeset .tabbed-content{box-shadow:0 -.05rem var(--md-default-fg-color--lightest);display:none;order:99;width:100%}@media print{.md-typeset .tabbed-content{display:block;order:0}}.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child,.md-typeset .tabbed-content>pre:only-child{margin:0}.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code,.md-typeset .tabbed-content>pre:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:checked+label{border-color:var(--md-accent-fg-color);color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-typeset .tabbed-set>input:not(.focus-visible)+label{-webkit-tap-highlight-color:transparent;outline:none}.md-typeset .tabbed-set>label{border-bottom:.1rem solid transparent;color:var(--md-default-fg-color--light);cursor:pointer;font-size:.64rem;font-weight:700;padding:.9375em 1.25em .78125em;transition:color .25s;width:auto;z-index:1}.md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}.md-typeset .task-list-item [type=checkbox]{left:-2em;position:absolute;top:.45em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{left:auto;right:-2em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;left:-1.5em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}[dir=rtl] .md-typeset .task-list-indicator:before{left:auto;right:-1.5em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media screen and (min-width:45em){.md-typeset .inline{float:left;margin-bottom:.8rem;margin-right:.8rem;margin-top:0;width:11.7rem}[dir=rtl] .md-typeset .inline{float:right;margin-left:.8rem;margin-right:0}.md-typeset .inline.end{float:right;margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{float:left;margin-left:0;margin-right:.8rem}} -/*# sourceMappingURL=main.8b42a75e.min.css.map */ \ No newline at end of file diff --git a/assets/stylesheets/main.8b42a75e.min.css.map b/assets/stylesheets/main.8b42a75e.min.css.map deleted file mode 100644 index 57c495859..000000000 --- a/assets/stylesheets/main.8b42a75e.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","src/assets/stylesheets/main.scss","src/assets/stylesheets/main/_reset.scss","src/assets/stylesheets/main/_colors.scss","src/assets/stylesheets/main/_icons.scss","src/assets/stylesheets/main/_typeset.scss","src/assets/stylesheets/utilities/_break.scss","node_modules/material-shadows/material-shadows.scss","src/assets/stylesheets/main/layout/_base.scss","src/assets/stylesheets/main/layout/_announce.scss","src/assets/stylesheets/main/layout/_clipboard.scss","src/assets/stylesheets/main/layout/_content.scss","src/assets/stylesheets/main/layout/_dialog.scss","src/assets/stylesheets/main/layout/_form.scss","src/assets/stylesheets/main/layout/_header.scss","src/assets/stylesheets/main/layout/_footer.scss","src/assets/stylesheets/main/layout/_nav.scss","src/assets/stylesheets/main/layout/_search.scss","src/assets/stylesheets/main/layout/_select.scss","src/assets/stylesheets/main/layout/_sidebar.scss","src/assets/stylesheets/main/layout/_source.scss","src/assets/stylesheets/main/layout/_tabs.scss","src/assets/stylesheets/main/layout/_top.scss","src/assets/stylesheets/main/layout/_version.scss","src/assets/stylesheets/main/extensions/markdown/_admonition.scss","node_modules/material-design-color/material-color.scss","src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","src/assets/stylesheets/main/extensions/markdown/_toc.scss","src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","src/assets/stylesheets/main/extensions/pymdownx/_details.scss","src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss","src/assets/stylesheets/main/_modifiers.scss"],"names":[],"mappings":"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","file":"src/assets/stylesheets/main.scss","sourcesContent":["////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Keyboard key\n .keys {\n\n // Keyboard key icon\n kbd::before,\n kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n }\n\n // Surrounding text\n span {\n padding: 0 px2em(3.2px);\n color: var(--md-default-fg-color--light);\n }\n\n // Define keyboard keys with left icon\n @each $name, $code in (\n\n // Modifiers\n \"alt\": \"\\2387\",\n \"left-alt\": \"\\2387\",\n \"right-alt\": \"\\2387\",\n \"command\": \"\\2318\",\n \"left-command\": \"\\2318\",\n \"right-command\": \"\\2318\",\n \"control\": \"\\2303\",\n \"left-control\": \"\\2303\",\n \"right-control\": \"\\2303\",\n \"meta\": \"\\25C6\",\n \"left-meta\": \"\\25C6\",\n \"right-meta\": \"\\25C6\",\n \"option\": \"\\2325\",\n \"left-option\": \"\\2325\",\n \"right-option\": \"\\2325\",\n \"shift\": \"\\21E7\",\n \"left-shift\": \"\\21E7\",\n \"right-shift\": \"\\21E7\",\n \"super\": \"\\2756\",\n \"left-super\": \"\\2756\",\n \"right-super\": \"\\2756\",\n \"windows\": \"\\229E\",\n \"left-windows\": \"\\229E\",\n \"right-windows\": \"\\229E\",\n\n // Other keys\n \"arrow-down\": \"\\2193\",\n \"arrow-left\": \"\\2190\",\n \"arrow-right\": \"\\2192\",\n \"arrow-up\": \"\\2191\",\n \"backspace\": \"\\232B\",\n \"backtab\": \"\\21E4\",\n \"caps-lock\": \"\\21EA\",\n \"clear\": \"\\2327\",\n \"context-menu\": \"\\2630\",\n \"delete\": \"\\2326\",\n \"eject\": \"\\23CF\",\n \"end\": \"\\2913\",\n \"escape\": \"\\238B\",\n \"home\": \"\\2912\",\n \"insert\": \"\\2380\",\n \"page-down\": \"\\21DF\",\n \"page-up\": \"\\21DE\",\n \"print-screen\": \"\\2399\"\n ) {\n .key-#{$name} {\n &::before {\n padding-right: px2em(6.4px);\n content: $code;\n }\n }\n }\n\n // Define keyboard keys with right icon\n @each $name, $code in (\n \"tab\": \"\\21E5\",\n \"num-enter\": \"\\2324\",\n \"enter\": \"\\23CE\"\n ) {\n .key-#{$name} {\n &::after {\n padding-left: px2em(6.4px);\n content: $code;\n }\n }\n }\n }\n}\n","@charset \"UTF-8\";\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\nbody {\n margin: 0;\n}\n\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\na {\n color: inherit;\n text-decoration: none;\n}\n\nhr {\n display: block;\n box-sizing: content-box;\n height: 0.05rem;\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n line-height: 1em;\n}\n\nimg {\n border-style: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\ninput {\n border: 0;\n outline: none;\n}\n\n:root {\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n --md-primary-fg-color: hsla(231, 48%, 48%, 1);\n --md-primary-fg-color--light: hsla(231, 44%, 56%, 1);\n --md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-accent-fg-color: hsla(231, 99%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n}\n:root > * {\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-hl-color: hsla(60, 100%, 50%, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n --md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n --md-typeset-table-color: hsla(0, 0%, 0%, 0.12);\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n}\n\n.md-icon svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family: var(--md-text-font-family, _), -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family: var(--md-code-font-family, _), SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n:root {\n --md-typeset-table-sort-icon: svg-load(\"material/sort.svg\");\n --md-typeset-table-sort-icon--asc: svg-load(\"material/sort-ascending.svg\");\n --md-typeset-table-sort-icon--desc: svg-load(\"material/sort-descending.svg\");\n}\n\n.md-typeset {\n font-size: 0.8rem;\n line-height: 1.6;\n color-adjust: exact;\n}\n@media print {\n .md-typeset {\n font-size: 0.68rem;\n }\n}\n.md-typeset ul,\n.md-typeset ol,\n.md-typeset dl,\n.md-typeset figure,\n.md-typeset blockquote,\n.md-typeset pre {\n margin: 1em 0;\n}\n.md-typeset h1 {\n margin: 0 0 1.25em;\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: 2em;\n line-height: 1.3;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 {\n margin: 1.6em 0 0.64em;\n font-weight: 300;\n font-size: 1.5625em;\n line-height: 1.4;\n letter-spacing: -0.01em;\n}\n.md-typeset h3 {\n margin: 1.6em 0 0.8em;\n font-weight: 400;\n font-size: 1.25em;\n line-height: 1.5;\n letter-spacing: -0.01em;\n}\n.md-typeset h2 + h3 {\n margin-top: 0.8em;\n}\n.md-typeset h4 {\n margin: 1em 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n}\n.md-typeset h5,\n.md-typeset h6 {\n margin: 1.25em 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.8em;\n letter-spacing: -0.01em;\n}\n.md-typeset h5 {\n text-transform: uppercase;\n}\n.md-typeset hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: 0.05rem solid var(--md-default-fg-color--lightest);\n}\n.md-typeset a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n}\n.md-typeset a, .md-typeset a::before {\n transition: color 125ms;\n}\n.md-typeset a:focus, .md-typeset a:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset a.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n}\n@media print {\n .md-typeset code,\n.md-typeset pre,\n.md-typeset kbd {\n white-space: pre-wrap;\n }\n}\n.md-typeset code {\n padding: 0 0.2941176471em;\n font-size: 0.85em;\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: 0.1rem;\n box-decoration-break: clone;\n}\n.md-typeset code:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset h1 code,\n.md-typeset h2 code,\n.md-typeset h3 code,\n.md-typeset h4 code,\n.md-typeset h5 code,\n.md-typeset h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n}\n.md-typeset a code {\n color: currentColor;\n}\n.md-typeset pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n}\n.md-typeset pre > code {\n display: block;\n margin: 0;\n padding: 0.7720588235em 1.1764705882em;\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-typeset pre > code:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-typeset pre > code::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset pre > code::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > pre {\n margin: 1em -0.8rem;\n }\n .md-typeset > pre code {\n border-radius: 0;\n }\n}\n.md-typeset kbd {\n display: inline-block;\n padding: 0 0.6666666667em;\n color: var(--md-default-fg-color);\n font-size: 0.75em;\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.1rem 0 0.05rem var(--md-typeset-kbd-border-color), 0 0.1rem 0 var(--md-typeset-kbd-border-color), 0 -0.1rem 0.2rem var(--md-typeset-kbd-accent-color) inset;\n}\n.md-typeset mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n}\n.md-typeset abbr {\n text-decoration: none;\n border-bottom: 0.05rem dotted var(--md-default-fg-color--light);\n cursor: help;\n}\n@media (hover: none) {\n .md-typeset abbr {\n position: relative;\n }\n .md-typeset abbr[title]:focus::after, .md-typeset abbr[title]:hover::after {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: 0.2rem 0.3rem;\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n content: attr(title);\n }\n}\n.md-typeset small {\n opacity: 0.75;\n}\n.md-typeset sup,\n.md-typeset sub {\n margin-left: 0.078125em;\n}\n[dir=rtl] .md-typeset sup,\n[dir=rtl] .md-typeset sub {\n margin-right: 0.078125em;\n margin-left: initial;\n}\n.md-typeset blockquote {\n padding-left: 0.6rem;\n color: var(--md-default-fg-color--light);\n border-left: 0.2rem solid var(--md-default-fg-color--lighter);\n}\n[dir=rtl] .md-typeset blockquote {\n padding-right: 0.6rem;\n padding-left: initial;\n border-right: 0.2rem solid var(--md-default-fg-color--lighter);\n border-left: initial;\n}\n.md-typeset ul {\n list-style-type: disc;\n}\n.md-typeset ul,\n.md-typeset ol {\n display: flow-root;\n margin-left: 0.625em;\n padding: 0;\n}\n[dir=rtl] .md-typeset ul,\n[dir=rtl] .md-typeset ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset ul ol,\n.md-typeset ol ol {\n list-style-type: lower-alpha;\n}\n.md-typeset ul ol ol,\n.md-typeset ol ol ol {\n list-style-type: lower-roman;\n}\n.md-typeset ul li,\n.md-typeset ol li {\n margin-bottom: 0.5em;\n margin-left: 1.25em;\n}\n[dir=rtl] .md-typeset ul li,\n[dir=rtl] .md-typeset ol li {\n margin-right: 1.25em;\n margin-left: initial;\n}\n.md-typeset ul li p,\n.md-typeset ul li blockquote,\n.md-typeset ol li p,\n.md-typeset ol li blockquote {\n margin: 0.5em 0;\n}\n.md-typeset ul li:last-child,\n.md-typeset ol li:last-child {\n margin-bottom: 0;\n}\n.md-typeset ul li ul,\n.md-typeset ul li ol,\n.md-typeset ol li ul,\n.md-typeset ol li ol {\n margin: 0.5em 0 0.5em 0.625em;\n}\n[dir=rtl] .md-typeset ul li ul,\n[dir=rtl] .md-typeset ul li ol,\n[dir=rtl] .md-typeset ol li ul,\n[dir=rtl] .md-typeset ol li ol {\n margin-right: 0.625em;\n margin-left: initial;\n}\n.md-typeset dd {\n margin: 1em 0 1.5em 1.875em;\n}\n[dir=rtl] .md-typeset dd {\n margin-right: 1.875em;\n margin-left: initial;\n}\n.md-typeset img,\n.md-typeset svg {\n max-width: 100%;\n height: auto;\n}\n.md-typeset img[align=left],\n.md-typeset svg[align=left] {\n margin: 1em;\n margin-left: 0;\n}\n.md-typeset img[align=right],\n.md-typeset svg[align=right] {\n margin: 1em;\n margin-right: 0;\n}\n.md-typeset img[align]:only-child,\n.md-typeset svg[align]:only-child {\n margin-top: 0;\n}\n.md-typeset figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n}\n.md-typeset figure img {\n display: block;\n}\n.md-typeset figcaption {\n max-width: 24rem;\n margin: 1em auto 2em;\n font-style: italic;\n}\n.md-typeset iframe {\n max-width: 100%;\n}\n.md-typeset table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: 0.64rem;\n background-color: var(--md-default-bg-color);\n border: 0.05rem solid var(--md-typeset-table-color);\n border-radius: 0.1rem;\n touch-action: auto;\n}\n@media print {\n .md-typeset table:not([class]) {\n display: table;\n }\n}\n.md-typeset table:not([class]) + * {\n margin-top: 1.5em;\n}\n.md-typeset table:not([class]) th > *:first-child,\n.md-typeset table:not([class]) td > *:first-child {\n margin-top: 0;\n}\n.md-typeset table:not([class]) th > *:last-child,\n.md-typeset table:not([class]) td > *:last-child {\n margin-bottom: 0;\n}\n.md-typeset table:not([class]) th:not([align]),\n.md-typeset table:not([class]) td:not([align]) {\n text-align: left;\n}\n[dir=rtl] .md-typeset table:not([class]) th:not([align]),\n[dir=rtl] .md-typeset table:not([class]) td:not([align]) {\n text-align: right;\n}\n.md-typeset table:not([class]) th {\n min-width: 5rem;\n padding: 0.9375em 1.25em;\n font-weight: 700;\n vertical-align: top;\n}\n.md-typeset table:not([class]) th a {\n color: inherit;\n}\n.md-typeset table:not([class]) td {\n padding: 0.9375em 1.25em;\n vertical-align: top;\n border-top: 0.05rem solid var(--md-typeset-table-color);\n}\n.md-typeset table:not([class]) tbody tr {\n transition: background-color 125ms;\n}\n.md-typeset table:not([class]) tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 0.05rem 0 var(--md-default-bg-color) inset;\n}\n.md-typeset table:not([class]) a {\n word-break: normal;\n}\n.md-typeset table th[role=columnheader] {\n cursor: pointer;\n}\n.md-typeset table th[role=columnheader]::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: text-bottom;\n mask-image: var(--md-typeset-table-sort-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transition: background-color 125ms;\n content: \"\";\n}\n.md-typeset table th[role=columnheader]:hover::after {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-typeset table th[role=columnheader][aria-sort=ascending]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--asc);\n}\n.md-typeset table th[role=columnheader][aria-sort=descending]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--desc);\n}\n.md-typeset__scrollwrap {\n margin: 1em -0.8rem;\n overflow-x: auto;\n touch-action: auto;\n}\n.md-typeset__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 0.8rem;\n}\n@media print {\n .md-typeset__table {\n display: block;\n }\n}\nhtml .md-typeset__table table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n}\n\nhtml {\n height: 100%;\n overflow-x: hidden;\n font-size: 125%;\n}\n@media screen and (min-width: 100em) {\n html {\n font-size: 137.5%;\n }\n}\n@media screen and (min-width: 125em) {\n html {\n font-size: 150%;\n }\n}\n\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n font-size: 0.5rem;\n background-color: var(--md-default-bg-color);\n}\n@media print {\n body {\n display: block;\n }\n}\n@media screen and (max-width: 59.9375em) {\n body[data-md-state=lock] {\n position: fixed;\n }\n}\n\n.md-grid {\n max-width: 61rem;\n margin-right: auto;\n margin-left: auto;\n}\n\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n}\n@media print {\n .md-container {\n display: block;\n }\n}\n\n.md-main {\n flex-grow: 1;\n}\n.md-main__inner {\n display: flex;\n height: 100%;\n margin-top: 1.5rem;\n}\n\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.md-toggle {\n display: none;\n}\n\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-option:checked + label:not([hidden]) {\n display: block;\n}\n.md-option.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n\n.md-skip {\n position: fixed;\n z-index: -1;\n margin: 0.5rem;\n padding: 0.3rem 0.5rem;\n color: var(--md-default-bg-color);\n font-size: 0.64rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n transform: translateY(0.4rem);\n opacity: 0;\n}\n.md-skip:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 175ms 75ms;\n}\n\n@page {\n margin: 25mm;\n}\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-announce {\n display: none;\n }\n}\n.md-announce__inner {\n margin: 0.6rem auto;\n padding: 0 0.8rem;\n color: var(--md-footer-fg-color);\n font-size: 0.7rem;\n}\n\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n.md-clipboard {\n position: absolute;\n top: 0.5em;\n right: 0.5em;\n z-index: 1;\n width: 1.5em;\n height: 1.5em;\n color: var(--md-default-fg-color--lightest);\n border-radius: 0.1rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.1rem;\n cursor: pointer;\n transition: color 250ms;\n}\n@media print {\n .md-clipboard {\n display: none;\n }\n}\n.md-clipboard:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n:hover > .md-clipboard {\n color: var(--md-default-fg-color--light);\n}\n.md-clipboard:focus, .md-clipboard:hover {\n color: var(--md-accent-fg-color);\n}\n.md-clipboard::after {\n display: block;\n width: 1.125em;\n height: 1.125em;\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-clipboard--inline {\n cursor: pointer;\n}\n.md-clipboard--inline code {\n transition: color 250ms, background-color 250ms;\n}\n.md-clipboard--inline:focus code, .md-clipboard--inline:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n\n.md-content {\n flex-grow: 1;\n overflow: hidden;\n scroll-padding-top: 51.2rem;\n}\n.md-content__inner {\n margin: 0 0.8rem 1.2rem;\n padding-top: 0.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-left: 1.2rem;\n }\n [dir=rtl] .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n margin-left: 0.8rem;\n }\n .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 1.2rem;\n }\n [dir=rtl] .md-sidebar--secondary:not([hidden]) ~ .md-content > .md-content__inner {\n margin-right: 0.8rem;\n margin-left: 1.2rem;\n }\n}\n.md-content__inner::before {\n display: block;\n height: 0.4rem;\n content: \"\";\n}\n.md-content__inner > :last-child {\n margin-bottom: 0;\n}\n.md-content__button {\n float: right;\n margin: 0.4rem 0;\n margin-left: 0.4rem;\n padding: 0;\n}\n@media print {\n .md-content__button {\n display: none;\n }\n}\n[dir=rtl] .md-content__button {\n float: left;\n margin-right: 0.4rem;\n margin-left: initial;\n}\n[dir=rtl] .md-content__button svg {\n transform: scaleX(-1);\n}\n.md-typeset .md-content__button {\n color: var(--md-default-fg-color--lighter);\n}\n.md-content__button svg {\n display: inline;\n vertical-align: top;\n}\n\n.md-dialog {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);\n position: fixed;\n right: 0.8rem;\n bottom: 0.8rem;\n left: initial;\n z-index: 3;\n min-width: 11.1rem;\n padding: 0.4rem 0.6rem;\n background-color: var(--md-default-fg-color);\n border-radius: 0.1rem;\n transform: translateY(100%);\n opacity: 0;\n transition: transform 0ms 400ms, opacity 400ms;\n pointer-events: none;\n}\n@media print {\n .md-dialog {\n display: none;\n }\n}\n[dir=rtl] .md-dialog {\n right: initial;\n left: 0.8rem;\n}\n.md-dialog[data-md-state=open] {\n transform: translateY(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1), opacity 400ms;\n pointer-events: initial;\n}\n.md-dialog__inner {\n color: var(--md-default-bg-color);\n font-size: 0.7rem;\n}\n\n.md-typeset .md-button {\n display: inline-block;\n padding: 0.625em 2em;\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: 0.1rem solid currentColor;\n border-radius: 0.1rem;\n cursor: pointer;\n transition: color 125ms, background-color 125ms, border-color 125ms;\n}\n.md-typeset .md-button--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n}\n.md-typeset .md-button:focus, .md-typeset .md-button:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .md-input {\n height: 1.8rem;\n padding: 0 0.6rem;\n font-size: 0.8rem;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.1);\n transition: box-shadow 250ms;\n}\n.md-typeset .md-input:focus, .md-typeset .md-input:hover {\n box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.15), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.15);\n}\n.md-typeset .md-input--stretch {\n width: 100%;\n}\n\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 3;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0), 0 0.2rem 0.4rem rgba(0, 0, 0, 0);\n}\n@media print {\n .md-header {\n display: none;\n }\n}\n.md-header[data-md-state=shadow] {\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2);\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), box-shadow 250ms;\n}\n.md-header[data-md-state=hidden] {\n transform: translateY(-100%);\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), box-shadow 250ms;\n}\n.md-header__inner {\n display: flex;\n align-items: center;\n padding: 0 0.2rem;\n}\n.md-header__button {\n position: relative;\n z-index: 1;\n margin: 0.2rem;\n padding: 0.4rem;\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n}\n.md-header__button:hover {\n opacity: 0.7;\n}\n.md-header__button:not([hidden]) {\n display: inline-block;\n}\n.md-header__button:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-header__button.md-logo {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n@media screen and (max-width: 76.1875em) {\n .md-header__button.md-logo {\n display: none;\n }\n}\n.md-header__button.md-logo img,\n.md-header__button.md-logo svg {\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n fill: currentColor;\n}\n@media screen and (min-width: 60em) {\n .md-header__button[for=__search] {\n display: none;\n }\n}\n.no-js .md-header__button[for=__search] {\n display: none;\n}\n[dir=rtl] .md-header__button[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (min-width: 76.25em) {\n .md-header__button[for=__drawer] {\n display: none;\n }\n}\n.md-header__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-header__topic + .md-header__topic {\n z-index: -1;\n transform: translateX(1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__topic + .md-header__topic {\n transform: translateX(-1.25rem);\n}\n.md-header__title {\n flex-grow: 1;\n height: 2.4rem;\n margin-right: 0.4rem;\n margin-left: 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-header__title[data-md-state=active] .md-header__topic {\n z-index: -1;\n transform: translateX(-1.25rem);\n opacity: 0;\n transition: transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1), opacity 150ms;\n pointer-events: none;\n}\n[dir=rtl] .md-header__title[data-md-state=active] .md-header__topic {\n transform: translateX(1.25rem);\n}\n.md-header__title[data-md-state=active] .md-header__topic + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n pointer-events: initial;\n}\n.md-header__title > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.md-header__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition: max-width 0ms 250ms, opacity 250ms 250ms;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-header__option {\n max-width: 0;\n opacity: 0;\n transition: max-width 0ms, opacity 0ms;\n}\n.md-header__source {\n display: none;\n}\n@media screen and (min-width: 60em) {\n .md-header__source {\n display: block;\n width: 11.7rem;\n max-width: 11.7rem;\n margin-left: 1rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1rem;\n margin-left: initial;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-header__source {\n margin-left: 1.4rem;\n }\n [dir=rtl] .md-header__source {\n margin-right: 1.4rem;\n }\n}\n\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n}\n@media print {\n .md-footer {\n display: none;\n }\n}\n.md-footer__inner {\n padding: 0.2rem;\n overflow: auto;\n}\n.md-footer__link {\n display: flex;\n padding-top: 1.4rem;\n padding-bottom: 0.4rem;\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n}\n@media screen and (min-width: 45em) {\n .md-footer__link {\n width: 50%;\n }\n}\n.md-footer__link:focus, .md-footer__link:hover {\n opacity: 0.7;\n}\n.md-footer__link--prev {\n float: left;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--prev {\n width: 25%;\n }\n .md-footer__link--prev .md-footer__title {\n display: none;\n }\n}\n[dir=rtl] .md-footer__link--prev {\n float: right;\n}\n[dir=rtl] .md-footer__link--prev svg {\n transform: scaleX(-1);\n}\n.md-footer__link--next {\n float: right;\n text-align: right;\n}\n@media screen and (max-width: 44.9375em) {\n .md-footer__link--next {\n width: 75%;\n }\n}\n[dir=rtl] .md-footer__link--next {\n float: left;\n text-align: left;\n}\n[dir=rtl] .md-footer__link--next svg {\n transform: scaleX(-1);\n}\n.md-footer__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - 2.4rem);\n padding: 0 1rem;\n font-size: 0.9rem;\n line-height: 2.4rem;\n}\n.md-footer__button {\n margin: 0.2rem;\n padding: 0.4rem;\n}\n.md-footer__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: -1rem;\n padding: 0 1rem;\n font-size: 0.64rem;\n opacity: 0.7;\n}\n\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n}\n.md-footer-meta__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: 0.2rem;\n}\nhtml .md-footer-meta.md-typeset a {\n color: var(--md-footer-fg-color--light);\n}\nhtml .md-footer-meta.md-typeset a:focus, html .md-footer-meta.md-typeset a:hover {\n color: var(--md-footer-fg-color);\n}\n\n.md-footer-copyright {\n width: 100%;\n margin: auto 0.6rem;\n padding: 0.4rem 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: 0.64rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-copyright {\n width: auto;\n }\n}\n.md-footer-copyright__highlight {\n color: var(--md-footer-fg-color--light);\n}\n\n.md-footer-social {\n margin: 0 0.4rem;\n padding: 0.2rem 0 0.6rem;\n}\n@media screen and (min-width: 45em) {\n .md-footer-social {\n padding: 0.6rem 0;\n }\n}\n.md-footer-social__link {\n display: inline-block;\n width: 1.6rem;\n height: 1.6rem;\n text-align: center;\n}\n.md-footer-social__link::before {\n line-height: 1.9;\n}\n.md-footer-social__link svg {\n max-height: 0.8rem;\n vertical-align: -25%;\n fill: currentColor;\n}\n\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n.md-nav {\n font-size: 0.7rem;\n line-height: 1.3;\n}\n.md-nav__title {\n display: block;\n padding: 0 0.6rem;\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n}\n.md-nav__title .md-nav__button {\n display: none;\n}\n.md-nav__title .md-nav__button img {\n width: auto;\n height: 100%;\n}\n.md-nav__title .md-nav__button.md-logo img,\n.md-nav__title .md-nav__button.md-logo svg {\n display: block;\n width: 2.4rem;\n height: 2.4rem;\n fill: currentColor;\n}\n.md-nav__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-nav__item {\n padding: 0 0.6rem;\n}\n.md-nav__item .md-nav__item {\n padding-right: 0;\n}\n[dir=rtl] .md-nav__item .md-nav__item {\n padding-right: 0.6rem;\n padding-left: 0;\n}\n.md-nav__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n}\n.md-nav__link[data-md-state=blur] {\n color: var(--md-default-fg-color--light);\n}\n.md-nav__item .md-nav__link--active {\n color: var(--md-typeset-a-color);\n}\n.md-nav__item--nested > .md-nav__link {\n color: inherit;\n}\n.md-nav__link:focus, .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-nav__link.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-nav--primary .md-nav__link[for=__toc] {\n display: none;\n}\n.md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n}\n.md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: none;\n}\n.md-nav__source {\n display: none;\n}\n@media screen and (max-width: 76.1875em) {\n .md-nav--primary, .md-nav--primary .md-nav {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n .md-nav--primary .md-nav__title,\n.md-nav--primary .md-nav__item {\n font-size: 0.8rem;\n line-height: 1.5;\n }\n .md-nav--primary .md-nav__title {\n position: relative;\n height: 5.6rem;\n padding: 3rem 0.8rem 0.2rem;\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: 2.4rem;\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n position: absolute;\n top: 0.4rem;\n left: 0.4rem;\n display: block;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon {\n right: 0.4rem;\n left: initial;\n }\n .md-nav--primary .md-nav__title .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0.05rem 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n }\n .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child {\n border-top: 0;\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n .md-nav--primary .md-nav__title .md-logo {\n position: absolute;\n top: 0.2rem;\n left: 0.2rem;\n display: block;\n margin: 0.2rem;\n padding: 0.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__title .md-logo {\n right: 0.2rem;\n left: initial;\n }\n .md-nav--primary .md-nav__list {\n flex: 1;\n }\n .md-nav--primary .md-nav__item {\n padding: 0;\n border-top: 0.05rem solid var(--md-default-fg-color--lightest);\n }\n .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__item--nested > .md-nav__link {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n }\n .md-nav--primary .md-nav__item--active > .md-nav__link:focus, .md-nav--primary .md-nav__item--active > .md-nav__link:hover {\n color: var(--md-accent-fg-color);\n }\n .md-nav--primary .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: 0.6rem 0.8rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon {\n position: absolute;\n top: 50%;\n right: 0.6rem;\n width: 1.2rem;\n height: 1.2rem;\n margin-top: -0.6rem;\n color: inherit;\n font-size: 1.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon {\n right: initial;\n left: 0.6rem;\n }\n .md-nav--primary .md-nav__link .md-nav__icon::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n [dir=rtl] .md-nav--primary .md-nav__icon::after {\n transform: scale(-1);\n }\n .md-nav--primary .md-nav--secondary .md-nav__link {\n position: static;\n }\n .md-nav--primary .md-nav--secondary .md-nav {\n position: static;\n background-color: transparent;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-left: 1.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link {\n padding-right: 1.4rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-left: 2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link {\n padding-right: 2rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 2.6rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 2.6rem;\n padding-left: initial;\n }\n .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: 3.2rem;\n }\n [dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link {\n padding-right: 3.2rem;\n padding-left: initial;\n }\n .md-nav--secondary {\n background-color: transparent;\n }\n .md-nav__toggle ~ .md-nav {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition: transform 250ms cubic-bezier(0.8, 0, 0.6, 1), opacity 125ms 50ms;\n }\n [dir=rtl] .md-nav__toggle ~ .md-nav {\n transform: translateX(-100%);\n }\n .md-nav__toggle:checked ~ .md-nav {\n transform: translateX(0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms 125ms;\n }\n .md-nav__toggle:checked ~ .md-nav > .md-nav__list {\n backface-visibility: hidden;\n }\n}\n@media screen and (max-width: 59.9375em) {\n .md-nav--primary .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n }\n [dir=rtl] .md-nav--primary .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--primary .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--primary .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--primary .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n .md-nav__source {\n display: block;\n padding: 0 0.2rem;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-nav--integrated .md-nav__link[for=__toc] {\n display: block;\n padding-right: 2.4rem;\n scroll-snap-align: initial;\n }\n [dir=rtl] .md-nav--integrated .md-nav__link[for=__toc] {\n padding-right: 0.8rem;\n padding-left: 2.4rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] .md-icon::after {\n content: \"\";\n }\n .md-nav--integrated .md-nav__link[for=__toc] + .md-nav__link {\n display: none;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: flex;\n }\n}\n@media screen and (min-width: 60em) {\n .md-nav--secondary .md-nav__title[for=__toc] {\n scroll-snap-align: start;\n }\n .md-nav--secondary .md-nav__title .md-nav__icon {\n display: none;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-nav {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n }\n .md-nav--primary .md-nav__title[for=__drawer] {\n scroll-snap-align: start;\n }\n .md-nav--primary .md-nav__title .md-nav__icon {\n display: none;\n }\n .md-nav__toggle ~ .md-nav {\n display: none;\n }\n .md-nav__toggle:checked ~ .md-nav, .md-nav__toggle:indeterminate ~ .md-nav {\n display: block;\n }\n .md-nav__item--nested > .md-nav > .md-nav__title {\n display: none;\n }\n .md-nav__item--section {\n display: block;\n margin: 1.25em 0;\n }\n .md-nav__item--section:last-child {\n margin-bottom: 0;\n }\n .md-nav__item--section > .md-nav__link {\n display: none;\n }\n .md-nav__item--section > .md-nav {\n display: block;\n }\n .md-nav__item--section > .md-nav > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav__item--section > .md-nav > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n .md-nav__icon {\n float: right;\n width: 0.9rem;\n height: 0.9rem;\n transition: transform 250ms;\n }\n [dir=rtl] .md-nav__icon {\n float: left;\n transform: rotate(180deg);\n }\n .md-nav__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: -0.1rem;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link .md-nav__icon, .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link .md-nav__icon {\n transform: rotate(90deg);\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--nested,\n.md-nav--lifted > .md-nav__title {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active {\n display: block;\n padding: 0;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link {\n display: none;\n }\n .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav > .md-nav__title {\n display: block;\n padding: 0 0.6rem;\n pointer-events: none;\n scroll-snap-align: start;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] {\n display: block;\n }\n .md-nav--lifted .md-nav[data-md-level=\"1\"] > .md-nav__list > .md-nav__item {\n padding-right: 0.6rem;\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: 0.05rem solid var(--md-primary-fg-color);\n }\n .md-nav--integrated .md-nav__link[for=__toc] ~ .md-nav > .md-nav__title {\n display: none;\n }\n}\n\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n.md-search {\n position: relative;\n}\n@media screen and (min-width: 60em) {\n .md-search {\n padding: 0.2rem 0;\n }\n}\n.no-js .md-search {\n display: none;\n}\n.md-search__overlay {\n z-index: 1;\n opacity: 0;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__overlay {\n position: absolute;\n top: -1rem;\n left: -2.2rem;\n width: 2rem;\n height: 2rem;\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: 1rem;\n transform-origin: center;\n transition: transform 300ms 100ms, opacity 200ms 200ms;\n pointer-events: none;\n }\n [dir=rtl] .md-search__overlay {\n right: -2.2rem;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n opacity: 1;\n transition: transform 400ms, opacity 100ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n cursor: pointer;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [dir=rtl] .md-search__overlay {\n right: 0;\n left: initial;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n width: 100%;\n height: 200vh;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@media screen and (max-width: 29.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(45);\n }\n}\n@media screen and (min-width: 30em) and (max-width: 44.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(60);\n }\n}\n@media screen and (min-width: 45em) and (max-width: 59.9375em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__overlay {\n transform: scale(75);\n }\n}\n.md-search__inner {\n backface-visibility: hidden;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__inner {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 2;\n width: 0;\n height: 0;\n overflow: hidden;\n transform: translateX(5%);\n opacity: 0;\n transition: width 0ms 300ms, height 0ms 300ms, transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms 150ms;\n }\n [dir=rtl] .md-search__inner {\n right: 0;\n left: initial;\n transform: translateX(-5%);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 100%;\n height: 100%;\n transform: translateX(0);\n opacity: 1;\n transition: width 0ms 0ms, height 0ms 0ms, transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms 150ms;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__inner {\n position: relative;\n float: right;\n width: 11.7rem;\n padding: 0.1rem 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n }\n [dir=rtl] .md-search__inner {\n float: left;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n [data-md-toggle=search]:checked ~ .md-header .md-search__inner {\n width: 34.4rem;\n }\n}\n.md-search__form {\n position: relative;\n z-index: 2;\n height: 2.4rem;\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 0.6rem transparent;\n transition: color 250ms, background-color 250ms;\n}\n@media screen and (min-width: 60em) {\n .md-search__form {\n height: 1.8rem;\n background-color: rgba(0, 0, 0, 0.26);\n border-radius: 0.1rem;\n }\n .md-search__form:hover {\n background-color: rgba(255, 255, 255, 0.12);\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__form {\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem 0.1rem 0 0;\n box-shadow: 0 0 0.6rem rgba(0, 0, 0, 0.07);\n}\n.md-search__input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: 100%;\n padding: 0 2.2rem 0 3.6rem;\n font-size: 0.9rem;\n text-overflow: ellipsis;\n background: transparent;\n}\n[dir=rtl] .md-search__input {\n padding: 0 3.6rem 0 2.2rem;\n}\n.md-search__input::placeholder {\n transition: color 250ms;\n}\n.md-search__input ~ .md-search__icon, .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n}\n.md-search__input::-ms-clear {\n display: none;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__input {\n width: 100%;\n height: 2.4rem;\n font-size: 0.9rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__input {\n padding-left: 2.2rem;\n color: inherit;\n font-size: 0.8rem;\n }\n [dir=rtl] .md-search__input {\n padding-right: 2.2rem;\n }\n .md-search__input::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n .md-search__input + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input {\n text-overflow: clip;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle=search]:checked ~ .md-header .md-search__input::placeholder {\n color: var(--md-default-fg-color--light);\n }\n}\n.md-search__icon {\n display: inline-block;\n width: 1.2rem;\n height: 1.2rem;\n cursor: pointer;\n transition: color 250ms, opacity 250ms;\n}\n.md-search__icon:hover {\n opacity: 0.7;\n}\n.md-search__icon[for=__search] {\n position: absolute;\n top: 0.3rem;\n left: 0.5rem;\n z-index: 2;\n}\n[dir=rtl] .md-search__icon[for=__search] {\n right: 0.5rem;\n left: initial;\n}\n[dir=rtl] .md-search__icon[for=__search] svg {\n transform: scaleX(-1);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__icon[for=__search] {\n top: 0.6rem;\n left: 0.8rem;\n }\n [dir=rtl] .md-search__icon[for=__search] {\n right: 0.8rem;\n left: initial;\n }\n .md-search__icon[for=__search] svg:first-child {\n display: none;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__icon[for=__search] {\n pointer-events: none;\n }\n .md-search__icon[for=__search] svg:last-child {\n display: none;\n }\n}\n.md-search__options {\n position: absolute;\n top: 0.3rem;\n right: 0.5rem;\n z-index: 2;\n pointer-events: none;\n}\n[dir=rtl] .md-search__options {\n right: initial;\n left: 0.5rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__options {\n top: 0.6rem;\n right: 0.8rem;\n }\n [dir=rtl] .md-search__options {\n right: initial;\n left: 0.8rem;\n }\n}\n.md-search__options > * {\n margin-left: 0.2rem;\n color: var(--md-default-fg-color--light);\n transform: scale(0.75);\n opacity: 0;\n transition: transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 150ms;\n}\n.md-search__options > *:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__options > * {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__input:valid ~ .md-search__options > *:hover {\n opacity: 0.7;\n}\n.md-search__suggest {\n position: absolute;\n top: 0;\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n padding: 0 2.2rem 0 3.6rem;\n color: var(--md-default-fg-color--lighter);\n font-size: 0.9rem;\n white-space: nowrap;\n opacity: 0;\n transition: opacity 50ms;\n}\n[dir=rtl] .md-search__suggest {\n padding: 0 3.6rem 0 2.2rem;\n}\n@media screen and (min-width: 60em) {\n .md-search__suggest {\n padding-left: 2.2rem;\n font-size: 0.8rem;\n }\n [dir=rtl] .md-search__suggest {\n padding-right: 2.2rem;\n }\n}\n[data-md-toggle=search]:checked ~ .md-header .md-search__suggest {\n opacity: 1;\n transition: opacity 300ms 100ms;\n}\n.md-search__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 0.1rem 0.1rem;\n}\n@media screen and (max-width: 59.9375em) {\n .md-search__output {\n top: 2.4rem;\n bottom: 0;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__output {\n top: 1.9rem;\n opacity: 0;\n transition: opacity 400ms;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__output {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4);\n opacity: 1;\n }\n}\n.md-search__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n backface-visibility: hidden;\n touch-action: pan-y;\n}\n@media (max-resolution: 1dppx) {\n .md-search__scrollwrap {\n transform: translateZ(0);\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search__scrollwrap {\n width: 23.4rem;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-search__scrollwrap {\n width: 34.4rem;\n }\n}\n@media screen and (min-width: 60em) {\n .md-search__scrollwrap {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n }\n [data-md-toggle=search]:checked ~ .md-header .md-search__scrollwrap {\n max-height: 75vh;\n }\n .md-search__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n .md-search__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n }\n .md-search__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n }\n}\n\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n}\n.md-search-result__meta {\n padding: 0 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.8rem;\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__meta {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__meta {\n padding-right: 2.2rem;\n padding-left: initial;\n }\n}\n.md-search-result__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.md-search-result__item {\n box-shadow: 0 -0.05rem 0 var(--md-default-fg-color--lightest);\n}\n.md-search-result__item:first-child {\n box-shadow: none;\n}\n.md-search-result__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n}\n.md-search-result__link:focus, .md-search-result__link:hover {\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__link:last-child p:last-child {\n margin-bottom: 0.6rem;\n}\n.md-search-result__more summary {\n display: block;\n padding: 0.75em 0.8rem;\n color: var(--md-typeset-a-color);\n font-size: 0.64rem;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__more summary {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__more summary {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__more summary:focus, .md-search-result__more summary:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n}\n.md-search-result__more summary::marker, .md-search-result__more summary::-webkit-details-marker {\n display: none;\n}\n.md-search-result__more summary ~ * > * {\n opacity: 0.65;\n}\n.md-search-result__article {\n position: relative;\n padding: 0 0.8rem;\n overflow: hidden;\n}\n@media screen and (min-width: 60em) {\n .md-search-result__article {\n padding-left: 2.2rem;\n }\n [dir=rtl] .md-search-result__article {\n padding-right: 2.2rem;\n padding-left: 0.8rem;\n }\n}\n.md-search-result__article--document .md-search-result__title {\n margin: 0.55rem 0;\n font-weight: 400;\n font-size: 0.8rem;\n line-height: 1.4;\n}\n.md-search-result__icon {\n position: absolute;\n left: 0;\n width: 1.2rem;\n height: 1.2rem;\n margin: 0.5rem;\n color: var(--md-default-fg-color--light);\n}\n@media screen and (max-width: 59.9375em) {\n .md-search-result__icon {\n display: none;\n }\n}\n.md-search-result__icon::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-search-result__icon {\n right: 0;\n left: initial;\n}\n[dir=rtl] .md-search-result__icon::after {\n transform: scaleX(-1);\n}\n.md-search-result__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: 0.64rem;\n line-height: 1.6;\n}\n.md-search-result__teaser {\n display: -webkit-box;\n max-height: 2rem;\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n@media screen and (max-width: 44.9375em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n@media screen and (min-width: 60em) and (max-width: 76.1875em) {\n .md-search-result__teaser {\n max-height: 3rem;\n -webkit-line-clamp: 3;\n }\n}\n.md-search-result__teaser mark {\n text-decoration: underline;\n background-color: transparent;\n}\n.md-search-result__terms {\n margin: 0.5em 0;\n font-size: 0.64rem;\n font-style: italic;\n}\n.md-search-result mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n}\n\n.md-select {\n position: relative;\n z-index: 1;\n}\n.md-select__inner {\n position: absolute;\n top: calc(100% - 0.2rem);\n left: 50%;\n max-height: 0;\n margin-top: 0.2rem;\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate3d(-50%, 0.3rem, 0);\n opacity: 0;\n transition: transform 250ms 375ms, opacity 250ms 250ms, max-height 0ms 500ms;\n}\n.md-select:focus-within .md-select__inner, .md-select:hover .md-select__inner {\n max-height: 10rem;\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition: transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms, max-height 0ms;\n}\n.md-select__inner::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: -0.2rem;\n margin-left: -0.2rem;\n border: 0.2rem solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n}\n.md-select__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: 0.8rem;\n list-style-type: none;\n border-radius: 0.1rem;\n}\n.md-select__item {\n line-height: 1.8rem;\n}\n.md-select__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n outline: none;\n cursor: pointer;\n transition: background-color 250ms, color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-select__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-select__link:focus, .md-select__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-select__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n.md-sidebar {\n position: sticky;\n top: 2.4rem;\n flex-shrink: 0;\n align-self: flex-start;\n width: 12.1rem;\n padding: 1.2rem 0;\n}\n@media print {\n .md-sidebar {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-sidebar--primary {\n position: fixed;\n top: 0;\n left: -12.1rem;\n z-index: 4;\n display: block;\n width: 12.1rem;\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 250ms;\n }\n [dir=rtl] .md-sidebar--primary {\n right: -12.1rem;\n left: initial;\n }\n [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4);\n transform: translateX(12.1rem);\n }\n [dir=rtl] [data-md-toggle=drawer]:checked ~ .md-container .md-sidebar--primary {\n transform: translateX(-12.1rem);\n }\n .md-sidebar--primary .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n}\n@media screen and (min-width: 76.25em) {\n .md-sidebar {\n height: 0;\n }\n .no-js .md-sidebar {\n height: auto;\n }\n}\n.md-sidebar--secondary {\n display: none;\n order: 2;\n}\n@media screen and (min-width: 60em) {\n .md-sidebar--secondary {\n height: 0;\n }\n .no-js .md-sidebar--secondary {\n height: auto;\n }\n .md-sidebar--secondary:not([hidden]) {\n display: block;\n }\n .md-sidebar--secondary .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n}\n.md-sidebar__scrollwrap {\n margin: 0 0.2rem;\n overflow-y: auto;\n backface-visibility: hidden;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n}\n.md-sidebar__scrollwrap:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar {\n width: 0.2rem;\n height: 0.2rem;\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n}\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover {\n background-color: var(--md-accent-fg-color);\n}\n\n@media screen and (max-width: 76.1875em) {\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 4;\n width: 0;\n height: 0;\n background-color: rgba(0, 0, 0, 0.54);\n opacity: 0;\n transition: width 0ms 250ms, height 0ms 250ms, opacity 250ms;\n }\n [data-md-toggle=drawer]:checked ~ .md-overlay {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition: width 0ms, height 0ms, opacity 250ms;\n }\n}\n@keyframes facts {\n 0% {\n height: 0;\n }\n 100% {\n height: 0.65rem;\n }\n}\n@keyframes fact {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n.md-source {\n display: block;\n font-size: 0.65rem;\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n backface-visibility: hidden;\n transition: opacity 250ms;\n}\n.md-source:hover {\n opacity: 0.7;\n}\n.md-source__icon {\n display: inline-block;\n width: 2rem;\n height: 2.4rem;\n vertical-align: middle;\n}\n.md-source__icon svg {\n margin-top: 0.6rem;\n margin-left: 0.6rem;\n}\n[dir=rtl] .md-source__icon svg {\n margin-right: 0.6rem;\n margin-left: initial;\n}\n.md-source__icon + .md-source__repository {\n margin-left: -2rem;\n padding-left: 2rem;\n}\n[dir=rtl] .md-source__icon + .md-source__repository {\n margin-right: -2rem;\n margin-left: initial;\n padding-right: 2rem;\n padding-left: initial;\n}\n.md-source__repository {\n display: inline-block;\n max-width: calc(100% - 1.2rem);\n margin-left: 0.6rem;\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n}\n.md-source__facts {\n margin: 0.1rem 0 0;\n padding: 0;\n overflow: hidden;\n font-size: 0.55rem;\n list-style-type: none;\n opacity: 0.75;\n}\n[data-md-state=done] .md-source__facts {\n animation: facts 250ms ease-in;\n}\n.md-source__fact {\n display: inline-block;\n}\n[data-md-state=done] .md-source__fact {\n animation: fact 400ms ease-out;\n}\n.md-source__fact::before {\n display: inline-block;\n width: 0.6rem;\n height: 0.6rem;\n margin-right: 0.1rem;\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-source__fact:nth-child(1n+2)::before {\n margin-left: 0.4rem;\n}\n[dir=rtl] .md-source__fact {\n margin-right: initial;\n margin-left: 0.1rem;\n}\n[dir=rtl] .md-source__fact:nth-child(1n+2)::before {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-source__fact--version::before {\n mask-image: var(--md-source-version-icon);\n}\n.md-source__fact--stars::before {\n mask-image: var(--md-source-stars-icon);\n}\n.md-source__fact--forks::before {\n mask-image: var(--md-source-forks-icon);\n}\n.md-source__fact--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n}\n\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n}\n@media print {\n .md-tabs {\n display: none;\n }\n}\n@media screen and (max-width: 76.1875em) {\n .md-tabs {\n display: none;\n }\n}\n.md-tabs[data-md-state=hidden] {\n pointer-events: none;\n}\n.md-tabs__list {\n margin: 0;\n margin-left: 0.2rem;\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n}\n[dir=rtl] .md-tabs__list {\n margin-right: 0.2rem;\n margin-left: initial;\n}\n.md-tabs__item {\n display: inline-block;\n height: 2.4rem;\n padding-right: 0.6rem;\n padding-left: 0.6rem;\n}\n.md-tabs__link {\n display: block;\n margin-top: 0.8rem;\n font-size: 0.7rem;\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n backface-visibility: hidden;\n opacity: 0.7;\n transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1), opacity 250ms;\n}\n.md-tabs__link--active, .md-tabs__link:focus, .md-tabs__link:hover {\n color: inherit;\n opacity: 1;\n}\n.md-tabs__item:nth-child(2) .md-tabs__link {\n transition-delay: 20ms;\n}\n.md-tabs__item:nth-child(3) .md-tabs__link {\n transition-delay: 40ms;\n}\n.md-tabs__item:nth-child(4) .md-tabs__link {\n transition-delay: 60ms;\n}\n.md-tabs__item:nth-child(5) .md-tabs__link {\n transition-delay: 80ms;\n}\n.md-tabs__item:nth-child(6) .md-tabs__link {\n transition-delay: 100ms;\n}\n.md-tabs__item:nth-child(7) .md-tabs__link {\n transition-delay: 120ms;\n}\n.md-tabs__item:nth-child(8) .md-tabs__link {\n transition-delay: 140ms;\n}\n.md-tabs__item:nth-child(9) .md-tabs__link {\n transition-delay: 160ms;\n}\n.md-tabs__item:nth-child(10) .md-tabs__link {\n transition-delay: 180ms;\n}\n.md-tabs__item:nth-child(11) .md-tabs__link {\n transition-delay: 200ms;\n}\n.md-tabs__item:nth-child(12) .md-tabs__link {\n transition-delay: 220ms;\n}\n.md-tabs__item:nth-child(13) .md-tabs__link {\n transition-delay: 240ms;\n}\n.md-tabs__item:nth-child(14) .md-tabs__link {\n transition-delay: 260ms;\n}\n.md-tabs__item:nth-child(15) .md-tabs__link {\n transition-delay: 280ms;\n}\n.md-tabs__item:nth-child(16) .md-tabs__link {\n transition-delay: 300ms;\n}\n.md-tabs[data-md-state=hidden] .md-tabs__link {\n transform: translateY(50%);\n opacity: 0;\n transition: transform 0ms 100ms, opacity 100ms;\n}\n\n.md-top {\n position: fixed;\n top: 3.2rem;\n z-index: 2;\n margin-left: 50%;\n padding: 0.4rem 0.8rem;\n color: var(--md-default-fg-color--light);\n font-size: 0.7rem;\n background-color: var(--md-default-bg-color);\n border-radius: 1.6rem;\n outline: none;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n transform: translate(-50%, 0);\n transition: color 125ms, background-color 125ms, transform 125ms cubic-bezier(0.4, 0, 0.2, 1), opacity 125ms;\n}\n@media print {\n .md-top {\n display: none;\n }\n}\n[dir=rtl] .md-top {\n float: left;\n}\n.md-top[data-md-state=hidden] {\n transform: translate(-50%, 0.2rem);\n opacity: 0;\n transition-duration: 0ms;\n pointer-events: none;\n}\n.md-top:focus, .md-top:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n}\n.md-top svg {\n display: inline-block;\n vertical-align: -0.5em;\n}\n\n@keyframes hoverfix {\n 0% {\n pointer-events: none;\n }\n}\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n.md-version {\n flex-shrink: 0;\n height: 2.4rem;\n font-size: 0.8rem;\n}\n.md-version__current {\n position: relative;\n top: 0.05rem;\n margin-right: 0.4rem;\n margin-left: 1.4rem;\n color: inherit;\n outline: none;\n cursor: pointer;\n}\n[dir=rtl] .md-version__current {\n margin-right: 1.4rem;\n margin-left: 0.4rem;\n}\n.md-version__current::after {\n display: inline-block;\n width: 0.4rem;\n height: 0.6rem;\n margin-left: 0.4rem;\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n}\n[dir=rtl] .md-version__current::after {\n margin-right: 0.4rem;\n margin-left: initial;\n}\n.md-version__list {\n position: absolute;\n top: 0.15rem;\n z-index: 1;\n max-height: 0;\n margin: 0.2rem 0.8rem;\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.1), 0 0 0.05rem rgba(0, 0, 0, 0.25);\n opacity: 0;\n transition: max-height 0ms 500ms, opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n}\n.md-version:focus-within .md-version__list, .md-version:hover .md-version__list {\n max-height: 10rem;\n opacity: 1;\n transition: max-height 0ms, opacity 250ms;\n}\n@media (pointer: coarse) {\n .md-version:hover .md-version__list {\n animation: hoverfix 250ms forwards;\n }\n .md-version:focus-within .md-version__list {\n animation: none;\n }\n}\n.md-version__item {\n line-height: 1.8rem;\n}\n.md-version__link {\n display: block;\n width: 100%;\n padding-right: 1.2rem;\n padding-left: 0.6rem;\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition: color 250ms, background-color 250ms;\n scroll-snap-align: start;\n}\n[dir=rtl] .md-version__link {\n padding-right: 0.6rem;\n padding-left: 1.2rem;\n}\n.md-version__link:focus, .md-version__link:hover {\n color: var(--md-accent-fg-color);\n}\n.md-version__link:focus {\n background-color: var(--md-default-fg-color--lightest);\n}\n\n:root {\n --md-admonition-icon--note:\n svg-load(\"material/pencil.svg\");\n --md-admonition-icon--abstract:\n svg-load(\"material/clipboard-text.svg\");\n --md-admonition-icon--info:\n svg-load(\"material/information.svg\");\n --md-admonition-icon--tip:\n svg-load(\"material/fire.svg\");\n --md-admonition-icon--success:\n svg-load(\"material/check-bold.svg\");\n --md-admonition-icon--question:\n svg-load(\"material/help-circle.svg\");\n --md-admonition-icon--warning:\n svg-load(\"material/alert.svg\");\n --md-admonition-icon--failure:\n svg-load(\"material/close-thick.svg\");\n --md-admonition-icon--danger:\n svg-load(\"material/lightning-bolt.svg\");\n --md-admonition-icon--bug:\n svg-load(\"material/bug.svg\");\n --md-admonition-icon--example:\n svg-load(\"material/format-list-numbered.svg\");\n --md-admonition-icon--quote:\n svg-load(\"material/format-quote-close.svg\");\n}\n\n.md-typeset .admonition, .md-typeset details {\n margin: 1.5625em 0;\n padding: 0 0.6rem;\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: 0.64rem;\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: 0.2rem solid #448aff;\n border-radius: 0.1rem;\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0.025rem 0.05rem rgba(0, 0, 0, 0.05);\n}\n@media print {\n .md-typeset .admonition, .md-typeset details {\n box-shadow: none;\n }\n}\n[dir=rtl] .md-typeset .admonition, [dir=rtl] .md-typeset details {\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\n.md-typeset .admonition .admonition, .md-typeset details .admonition, .md-typeset .admonition details, .md-typeset details details {\n margin-top: 1em;\n margin-bottom: 1em;\n}\n.md-typeset .admonition .md-typeset__scrollwrap, .md-typeset details .md-typeset__scrollwrap {\n margin: 1em -0.6rem;\n}\n.md-typeset .admonition .md-typeset__table, .md-typeset details .md-typeset__table {\n padding: 0 0.6rem;\n}\n.md-typeset .admonition > .tabbed-set:only-child, .md-typeset details > .tabbed-set:only-child {\n margin-top: 0;\n}\nhtml .md-typeset .admonition > :last-child, html .md-typeset details > :last-child {\n margin-bottom: 0.6rem;\n}\n.md-typeset .admonition-title, .md-typeset summary {\n position: relative;\n margin: 0 -0.6rem 0 -0.8rem;\n padding: 0.4rem 0.6rem 0.4rem 2rem;\n font-weight: 700;\n background-color: rgba(68, 138, 255, 0.1);\n border-left: 0.2rem solid #448aff;\n}\n[dir=rtl] .md-typeset .admonition-title, [dir=rtl] .md-typeset summary {\n margin: 0 -0.8rem 0 -0.6rem;\n padding: 0.4rem 2rem 0.4rem 0.6rem;\n border-right: 0.2rem solid #448aff;\n border-left: none;\n}\nhtml .md-typeset .admonition-title:last-child, html .md-typeset summary:last-child {\n margin-bottom: 0;\n}\n.md-typeset .admonition-title::before, .md-typeset summary::before {\n position: absolute;\n left: 0.6rem;\n width: 1rem;\n height: 1rem;\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .admonition-title::before, [dir=rtl] .md-typeset summary::before {\n right: 0.6rem;\n left: initial;\n}\n.md-typeset .admonition-title + .tabbed-set:last-child, .md-typeset summary + .tabbed-set:last-child {\n margin-top: 0;\n}\n\n.md-typeset .admonition.note, .md-typeset details.note {\n border-color: #448aff;\n}\n\n.md-typeset .note > .admonition-title, .md-typeset .note > summary {\n background-color: rgba(68, 138, 255, 0.1);\n border-color: #448aff;\n}\n.md-typeset .note > .admonition-title::before, .md-typeset .note > summary::before {\n background-color: #448aff;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.abstract, .md-typeset details.abstract, .md-typeset .admonition.tldr, .md-typeset details.tldr, .md-typeset .admonition.summary, .md-typeset details.summary {\n border-color: #00b0ff;\n}\n\n.md-typeset .abstract > .admonition-title, .md-typeset .abstract > summary, .md-typeset .tldr > .admonition-title, .md-typeset .tldr > summary, .md-typeset .summary > .admonition-title, .md-typeset .summary > summary {\n background-color: rgba(0, 176, 255, 0.1);\n border-color: #00b0ff;\n}\n.md-typeset .abstract > .admonition-title::before, .md-typeset .abstract > summary::before, .md-typeset .tldr > .admonition-title::before, .md-typeset .tldr > summary::before, .md-typeset .summary > .admonition-title::before, .md-typeset .summary > summary::before {\n background-color: #00b0ff;\n mask-image: var(--md-admonition-icon--abstract);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.info, .md-typeset details.info, .md-typeset .admonition.todo, .md-typeset details.todo {\n border-color: #00b8d4;\n}\n\n.md-typeset .info > .admonition-title, .md-typeset .info > summary, .md-typeset .todo > .admonition-title, .md-typeset .todo > summary {\n background-color: rgba(0, 184, 212, 0.1);\n border-color: #00b8d4;\n}\n.md-typeset .info > .admonition-title::before, .md-typeset .info > summary::before, .md-typeset .todo > .admonition-title::before, .md-typeset .todo > summary::before {\n background-color: #00b8d4;\n mask-image: var(--md-admonition-icon--info);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.tip, .md-typeset details.tip, .md-typeset .admonition.important, .md-typeset details.important, .md-typeset .admonition.hint, .md-typeset details.hint {\n border-color: #00bfa5;\n}\n\n.md-typeset .tip > .admonition-title, .md-typeset .tip > summary, .md-typeset .important > .admonition-title, .md-typeset .important > summary, .md-typeset .hint > .admonition-title, .md-typeset .hint > summary {\n background-color: rgba(0, 191, 165, 0.1);\n border-color: #00bfa5;\n}\n.md-typeset .tip > .admonition-title::before, .md-typeset .tip > summary::before, .md-typeset .important > .admonition-title::before, .md-typeset .important > summary::before, .md-typeset .hint > .admonition-title::before, .md-typeset .hint > summary::before {\n background-color: #00bfa5;\n mask-image: var(--md-admonition-icon--tip);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.success, .md-typeset details.success, .md-typeset .admonition.done, .md-typeset details.done, .md-typeset .admonition.check, .md-typeset details.check {\n border-color: #00c853;\n}\n\n.md-typeset .success > .admonition-title, .md-typeset .success > summary, .md-typeset .done > .admonition-title, .md-typeset .done > summary, .md-typeset .check > .admonition-title, .md-typeset .check > summary {\n background-color: rgba(0, 200, 83, 0.1);\n border-color: #00c853;\n}\n.md-typeset .success > .admonition-title::before, .md-typeset .success > summary::before, .md-typeset .done > .admonition-title::before, .md-typeset .done > summary::before, .md-typeset .check > .admonition-title::before, .md-typeset .check > summary::before {\n background-color: #00c853;\n mask-image: var(--md-admonition-icon--success);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.question, .md-typeset details.question, .md-typeset .admonition.faq, .md-typeset details.faq, .md-typeset .admonition.help, .md-typeset details.help {\n border-color: #64dd17;\n}\n\n.md-typeset .question > .admonition-title, .md-typeset .question > summary, .md-typeset .faq > .admonition-title, .md-typeset .faq > summary, .md-typeset .help > .admonition-title, .md-typeset .help > summary {\n background-color: rgba(100, 221, 23, 0.1);\n border-color: #64dd17;\n}\n.md-typeset .question > .admonition-title::before, .md-typeset .question > summary::before, .md-typeset .faq > .admonition-title::before, .md-typeset .faq > summary::before, .md-typeset .help > .admonition-title::before, .md-typeset .help > summary::before {\n background-color: #64dd17;\n mask-image: var(--md-admonition-icon--question);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.warning, .md-typeset details.warning, .md-typeset .admonition.attention, .md-typeset details.attention, .md-typeset .admonition.caution, .md-typeset details.caution {\n border-color: #ff9100;\n}\n\n.md-typeset .warning > .admonition-title, .md-typeset .warning > summary, .md-typeset .attention > .admonition-title, .md-typeset .attention > summary, .md-typeset .caution > .admonition-title, .md-typeset .caution > summary {\n background-color: rgba(255, 145, 0, 0.1);\n border-color: #ff9100;\n}\n.md-typeset .warning > .admonition-title::before, .md-typeset .warning > summary::before, .md-typeset .attention > .admonition-title::before, .md-typeset .attention > summary::before, .md-typeset .caution > .admonition-title::before, .md-typeset .caution > summary::before {\n background-color: #ff9100;\n mask-image: var(--md-admonition-icon--warning);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.failure, .md-typeset details.failure, .md-typeset .admonition.missing, .md-typeset details.missing, .md-typeset .admonition.fail, .md-typeset details.fail {\n border-color: #ff5252;\n}\n\n.md-typeset .failure > .admonition-title, .md-typeset .failure > summary, .md-typeset .missing > .admonition-title, .md-typeset .missing > summary, .md-typeset .fail > .admonition-title, .md-typeset .fail > summary {\n background-color: rgba(255, 82, 82, 0.1);\n border-color: #ff5252;\n}\n.md-typeset .failure > .admonition-title::before, .md-typeset .failure > summary::before, .md-typeset .missing > .admonition-title::before, .md-typeset .missing > summary::before, .md-typeset .fail > .admonition-title::before, .md-typeset .fail > summary::before {\n background-color: #ff5252;\n mask-image: var(--md-admonition-icon--failure);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.danger, .md-typeset details.danger, .md-typeset .admonition.error, .md-typeset details.error {\n border-color: #ff1744;\n}\n\n.md-typeset .danger > .admonition-title, .md-typeset .danger > summary, .md-typeset .error > .admonition-title, .md-typeset .error > summary {\n background-color: rgba(255, 23, 68, 0.1);\n border-color: #ff1744;\n}\n.md-typeset .danger > .admonition-title::before, .md-typeset .danger > summary::before, .md-typeset .error > .admonition-title::before, .md-typeset .error > summary::before {\n background-color: #ff1744;\n mask-image: var(--md-admonition-icon--danger);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.bug, .md-typeset details.bug {\n border-color: #f50057;\n}\n\n.md-typeset .bug > .admonition-title, .md-typeset .bug > summary {\n background-color: rgba(245, 0, 87, 0.1);\n border-color: #f50057;\n}\n.md-typeset .bug > .admonition-title::before, .md-typeset .bug > summary::before {\n background-color: #f50057;\n mask-image: var(--md-admonition-icon--bug);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.example, .md-typeset details.example {\n border-color: #7c4dff;\n}\n\n.md-typeset .example > .admonition-title, .md-typeset .example > summary {\n background-color: rgba(124, 77, 255, 0.1);\n border-color: #7c4dff;\n}\n.md-typeset .example > .admonition-title::before, .md-typeset .example > summary::before {\n background-color: #7c4dff;\n mask-image: var(--md-admonition-icon--example);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n.md-typeset .admonition.quote, .md-typeset details.quote, .md-typeset .admonition.cite, .md-typeset details.cite {\n border-color: #9e9e9e;\n}\n\n.md-typeset .quote > .admonition-title, .md-typeset .quote > summary, .md-typeset .cite > .admonition-title, .md-typeset .cite > summary {\n background-color: rgba(158, 158, 158, 0.1);\n border-color: #9e9e9e;\n}\n.md-typeset .quote > .admonition-title::before, .md-typeset .quote > summary::before, .md-typeset .cite > .admonition-title::before, .md-typeset .cite > summary::before {\n background-color: #9e9e9e;\n mask-image: var(--md-admonition-icon--quote);\n mask-repeat: no-repeat;\n mask-size: contain;\n}\n\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n.md-typeset .footnote {\n color: var(--md-default-fg-color--light);\n font-size: 0.64rem;\n}\n.md-typeset .footnote > ol {\n margin-left: 0;\n}\n.md-typeset .footnote > ol > li {\n transition: color 125ms;\n}\n.md-typeset .footnote > ol > li:target {\n color: var(--md-default-fg-color);\n}\n.md-typeset .footnote > ol > li:hover .footnote-backref, .md-typeset .footnote > ol > li:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n}\n.md-typeset .footnote > ol > li > :first-child {\n margin-top: 0;\n}\n.md-typeset .footnote-ref {\n font-weight: 700;\n font-size: 0.75em;\n}\nhtml .md-typeset .footnote-ref {\n outline-offset: 0.1rem;\n}\n.md-typeset .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(0.25rem);\n opacity: 0;\n transition: color 250ms, transform 250ms 250ms, opacity 125ms 250ms;\n}\n@media print {\n .md-typeset .footnote-backref {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n}\n[dir=rtl] .md-typeset .footnote-backref {\n transform: translateX(-0.25rem);\n}\n.md-typeset .footnote-backref:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset .footnote-backref::before {\n display: inline-block;\n width: 0.8rem;\n height: 0.8rem;\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .footnote-backref::before svg {\n transform: scaleX(-1);\n}\n.md-typeset [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n}\n.md-typeset [id^=\"fnref:\"]:target > .footnote-ref {\n outline: auto;\n}\n.md-typeset [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n}\n\n.md-typeset .headerlink {\n display: inline-block;\n margin-left: 0.5rem;\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition: color 250ms, opacity 125ms;\n}\n@media print {\n .md-typeset .headerlink {\n display: none;\n }\n}\n[dir=rtl] .md-typeset .headerlink {\n margin-right: 0.5rem;\n margin-left: initial;\n}\n.md-typeset :hover > .headerlink,\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus {\n opacity: 1;\n transition: color 250ms, opacity 125ms;\n}\n.md-typeset :target > .headerlink,\n.md-typeset .headerlink:focus,\n.md-typeset .headerlink:hover {\n color: var(--md-accent-fg-color);\n}\n.md-typeset :target {\n scroll-margin-top: 3.6rem;\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset :target {\n scroll-margin-top: 6rem;\n }\n}\n.md-typeset h1:target,\n.md-typeset h2:target,\n.md-typeset h3:target {\n scroll-margin-top: initial;\n}\n.md-typeset h1:target::before,\n.md-typeset h2:target::before,\n.md-typeset h3:target::before {\n display: block;\n margin-top: -3.4rem;\n padding-top: 3.4rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h1:target,\n.md-header--lifted ~ .md-container .md-typeset h2:target,\n.md-header--lifted ~ .md-container .md-typeset h3:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h1:target::before,\n.md-header--lifted ~ .md-container .md-typeset h2:target::before,\n.md-header--lifted ~ .md-container .md-typeset h3:target::before {\n margin-top: -5.8rem;\n padding-top: 5.8rem;\n }\n}\n.md-typeset h4:target {\n scroll-margin-top: initial;\n}\n.md-typeset h4:target::before {\n display: block;\n margin-top: -3.45rem;\n padding-top: 3.45rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h4:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h4:target::before {\n margin-top: -5.85rem;\n padding-top: 5.85rem;\n }\n}\n.md-typeset h5:target,\n.md-typeset h6:target {\n scroll-margin-top: initial;\n}\n.md-typeset h5:target::before,\n.md-typeset h6:target::before {\n display: block;\n margin-top: -3.6rem;\n padding-top: 3.6rem;\n content: \"\";\n}\n@media screen and (min-width: 76.25em) {\n .md-header--lifted ~ .md-container .md-typeset h5:target,\n.md-header--lifted ~ .md-container .md-typeset h6:target {\n scroll-margin-top: initial;\n }\n .md-header--lifted ~ .md-container .md-typeset h5:target::before,\n.md-header--lifted ~ .md-container .md-typeset h6:target::before {\n margin-top: -6rem;\n padding-top: 6rem;\n }\n}\n\n.md-typeset div.arithmatex {\n overflow: auto;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset div.arithmatex {\n margin: 0 -0.8rem;\n }\n}\n.md-typeset div.arithmatex > * {\n width: min-content;\n margin: 1em auto !important;\n padding: 0 0.8rem;\n touch-action: auto;\n}\n\n.md-typeset del.critic,\n.md-typeset ins.critic,\n.md-typeset .critic.comment {\n box-decoration-break: clone;\n}\n.md-typeset del.critic {\n background-color: var(--md-typeset-del-color);\n}\n.md-typeset ins.critic {\n background-color: var(--md-typeset-ins-color);\n}\n.md-typeset .critic.comment {\n color: var(--md-code-hl-comment-color);\n}\n.md-typeset .critic.comment::before {\n content: \"/* \";\n}\n.md-typeset .critic.comment::after {\n content: \" */\";\n}\n.md-typeset .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: 0.8rem;\n padding-left: 0.8rem;\n overflow: auto;\n box-shadow: none;\n}\n.md-typeset .critic.block > :first-child {\n margin-top: 0.5em;\n}\n.md-typeset .critic.block > :last-child {\n margin-bottom: 0.5em;\n}\n\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n.md-typeset details {\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n}\n.md-typeset details[open] > summary::after {\n transform: rotate(90deg);\n}\n.md-typeset details:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n}\n.md-typeset details:not([open]) > summary {\n border-radius: 0.1rem;\n}\n.md-typeset details::after {\n display: table;\n content: \"\";\n}\n.md-typeset summary {\n display: block;\n min-height: 1rem;\n padding: 0.4rem 1.8rem 0.4rem 2rem;\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n cursor: pointer;\n}\n[dir=rtl] .md-typeset summary {\n padding: 0.4rem 2.2rem 0.4rem 1.8rem;\n}\n.md-typeset summary.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: 0.2rem;\n}\n.md-typeset summary:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset summary::after {\n position: absolute;\n top: 0.4rem;\n right: 0.4rem;\n width: 1rem;\n height: 1rem;\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n}\n[dir=rtl] .md-typeset summary::after {\n right: initial;\n left: 0.4rem;\n transform: rotate(180deg);\n}\n.md-typeset summary::marker, .md-typeset summary::-webkit-details-marker {\n display: none;\n}\n\n.md-typeset .emojione,\n.md-typeset .twemoji,\n.md-typeset .gemoji {\n display: inline-flex;\n height: 1.125em;\n vertical-align: text-top;\n}\n.md-typeset .emojione svg,\n.md-typeset .twemoji svg,\n.md-typeset .gemoji svg {\n width: 1.125em;\n max-height: 100%;\n fill: currentColor;\n}\n\n.highlight .o,\n.highlight .ow {\n color: var(--md-code-hl-operator-color);\n}\n.highlight .p {\n color: var(--md-code-hl-punctuation-color);\n}\n.highlight .cpf,\n.highlight .l,\n.highlight .s,\n.highlight .sb,\n.highlight .sc,\n.highlight .s2,\n.highlight .si,\n.highlight .s1,\n.highlight .ss {\n color: var(--md-code-hl-string-color);\n}\n.highlight .cp,\n.highlight .se,\n.highlight .sh,\n.highlight .sr,\n.highlight .sx {\n color: var(--md-code-hl-special-color);\n}\n.highlight .m,\n.highlight .mb,\n.highlight .mf,\n.highlight .mh,\n.highlight .mi,\n.highlight .il,\n.highlight .mo {\n color: var(--md-code-hl-number-color);\n}\n.highlight .k,\n.highlight .kd,\n.highlight .kn,\n.highlight .kp,\n.highlight .kr,\n.highlight .kt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .kc,\n.highlight .n {\n color: var(--md-code-hl-name-color);\n}\n.highlight .no,\n.highlight .nb,\n.highlight .bp {\n color: var(--md-code-hl-constant-color);\n}\n.highlight .nc,\n.highlight .ne,\n.highlight .nf,\n.highlight .nn {\n color: var(--md-code-hl-function-color);\n}\n.highlight .nd,\n.highlight .ni,\n.highlight .nl,\n.highlight .nt {\n color: var(--md-code-hl-keyword-color);\n}\n.highlight .c,\n.highlight .cm,\n.highlight .c1,\n.highlight .ch,\n.highlight .cs,\n.highlight .sd {\n color: var(--md-code-hl-comment-color);\n}\n.highlight .na,\n.highlight .nv,\n.highlight .vc,\n.highlight .vg,\n.highlight .vi {\n color: var(--md-code-hl-variable-color);\n}\n.highlight .ge,\n.highlight .gr,\n.highlight .gh,\n.highlight .go,\n.highlight .gp,\n.highlight .gs,\n.highlight .gu,\n.highlight .gt {\n color: var(--md-code-hl-generic-color);\n}\n.highlight .gd,\n.highlight .gi {\n margin: 0 -0.125em;\n padding: 0 0.125em;\n border-radius: 0.1rem;\n}\n.highlight .gd {\n background-color: var(--md-typeset-del-color);\n}\n.highlight .gi {\n background-color: var(--md-typeset-ins-color);\n}\n.highlight .hll {\n display: block;\n margin: 0 -1.1764705882em;\n padding: 0 1.1764705882em;\n background-color: var(--md-code-hl-color);\n}\n.highlight [data-linenos]::before {\n position: sticky;\n left: -1.1764705882em;\n float: left;\n margin-right: 1.1764705882em;\n margin-left: -1.1764705882em;\n padding-left: 1.1764705882em;\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n}\n\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n}\n.highlighttable tbody,\n.highlighttable td {\n display: block;\n padding: 0;\n}\n.highlighttable tr {\n display: flex;\n}\n.highlighttable pre {\n margin: 0;\n}\n.highlighttable .linenos {\n padding: 0.7720588235em 1.1764705882em;\n padding-right: 0;\n font-size: 0.85em;\n background-color: var(--md-code-bg-color);\n user-select: none;\n}\n.highlighttable .linenodiv {\n padding-right: 0.5882352941em;\n box-shadow: -0.05rem 0 var(--md-default-fg-color--lightest) inset;\n}\n.highlighttable .linenodiv pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n}\n.highlighttable .code {\n flex: 1;\n overflow: hidden;\n}\n\n.md-typeset .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: 0.1rem;\n}\n.md-typeset .highlighttable code {\n border-radius: 0;\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > .highlight {\n margin: 1em -0.8rem;\n }\n .md-typeset > .highlight .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n .md-typeset > .highlight code {\n border-radius: 0;\n }\n .md-typeset > .highlighttable {\n margin: 1em -0.8rem;\n border-radius: 0;\n }\n .md-typeset > .highlighttable .hll {\n margin: 0 -0.8rem;\n padding: 0 0.8rem;\n }\n}\n\n.md-typeset .keys kbd::before,\n.md-typeset .keys kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n}\n.md-typeset .keys span {\n padding: 0 0.2em;\n color: var(--md-default-fg-color--light);\n}\n.md-typeset .keys .key-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-left-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-right-alt::before {\n padding-right: 0.4em;\n content: \"⎇\";\n}\n.md-typeset .keys .key-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-left-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-right-command::before {\n padding-right: 0.4em;\n content: \"⌘\";\n}\n.md-typeset .keys .key-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-left-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-right-control::before {\n padding-right: 0.4em;\n content: \"⌃\";\n}\n.md-typeset .keys .key-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-left-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-right-meta::before {\n padding-right: 0.4em;\n content: \"◆\";\n}\n.md-typeset .keys .key-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-left-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-right-option::before {\n padding-right: 0.4em;\n content: \"⌥\";\n}\n.md-typeset .keys .key-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-left-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-right-shift::before {\n padding-right: 0.4em;\n content: \"⇧\";\n}\n.md-typeset .keys .key-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-left-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-right-super::before {\n padding-right: 0.4em;\n content: \"❖\";\n}\n.md-typeset .keys .key-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-left-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-right-windows::before {\n padding-right: 0.4em;\n content: \"⊞\";\n}\n.md-typeset .keys .key-arrow-down::before {\n padding-right: 0.4em;\n content: \"↓\";\n}\n.md-typeset .keys .key-arrow-left::before {\n padding-right: 0.4em;\n content: \"←\";\n}\n.md-typeset .keys .key-arrow-right::before {\n padding-right: 0.4em;\n content: \"→\";\n}\n.md-typeset .keys .key-arrow-up::before {\n padding-right: 0.4em;\n content: \"↑\";\n}\n.md-typeset .keys .key-backspace::before {\n padding-right: 0.4em;\n content: \"⌫\";\n}\n.md-typeset .keys .key-backtab::before {\n padding-right: 0.4em;\n content: \"⇤\";\n}\n.md-typeset .keys .key-caps-lock::before {\n padding-right: 0.4em;\n content: \"⇪\";\n}\n.md-typeset .keys .key-clear::before {\n padding-right: 0.4em;\n content: \"⌧\";\n}\n.md-typeset .keys .key-context-menu::before {\n padding-right: 0.4em;\n content: \"☰\";\n}\n.md-typeset .keys .key-delete::before {\n padding-right: 0.4em;\n content: \"⌦\";\n}\n.md-typeset .keys .key-eject::before {\n padding-right: 0.4em;\n content: \"⏏\";\n}\n.md-typeset .keys .key-end::before {\n padding-right: 0.4em;\n content: \"⤓\";\n}\n.md-typeset .keys .key-escape::before {\n padding-right: 0.4em;\n content: \"⎋\";\n}\n.md-typeset .keys .key-home::before {\n padding-right: 0.4em;\n content: \"⤒\";\n}\n.md-typeset .keys .key-insert::before {\n padding-right: 0.4em;\n content: \"⎀\";\n}\n.md-typeset .keys .key-page-down::before {\n padding-right: 0.4em;\n content: \"⇟\";\n}\n.md-typeset .keys .key-page-up::before {\n padding-right: 0.4em;\n content: \"⇞\";\n}\n.md-typeset .keys .key-print-screen::before {\n padding-right: 0.4em;\n content: \"⎙\";\n}\n.md-typeset .keys .key-tab::after {\n padding-left: 0.4em;\n content: \"⇥\";\n}\n.md-typeset .keys .key-num-enter::after {\n padding-left: 0.4em;\n content: \"⌤\";\n}\n.md-typeset .keys .key-enter::after {\n padding-left: 0.4em;\n content: \"⏎\";\n}\n\n.md-typeset .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 -0.05rem var(--md-default-fg-color--lightest);\n}\n@media print {\n .md-typeset .tabbed-content {\n display: block;\n order: initial;\n }\n}\n.md-typeset .tabbed-content > pre:only-child,\n.md-typeset .tabbed-content > .highlight:only-child pre,\n.md-typeset .tabbed-content > .highlighttable:only-child {\n margin: 0;\n}\n.md-typeset .tabbed-content > pre:only-child > code,\n.md-typeset .tabbed-content > .highlight:only-child pre > code,\n.md-typeset .tabbed-content > .highlighttable:only-child > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .tabbed-content > .tabbed-set {\n margin: 0;\n}\n.md-typeset .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: 0.1rem;\n}\n.md-typeset .tabbed-set > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n}\n.md-typeset .tabbed-set > input:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:checked + label + .tabbed-content {\n display: block;\n}\n.md-typeset .tabbed-set > input:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n}\n.md-typeset .tabbed-set > input:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.md-typeset .tabbed-set > label {\n z-index: 1;\n width: auto;\n padding: 0.9375em 1.25em 0.78125em;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: 0.64rem;\n border-bottom: 0.1rem solid transparent;\n cursor: pointer;\n transition: color 250ms;\n}\n.md-typeset .tabbed-set > label:hover {\n color: var(--md-accent-fg-color);\n}\n\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n.md-typeset .task-list-item {\n position: relative;\n list-style-type: none;\n}\n.md-typeset .task-list-item [type=checkbox] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n}\n[dir=rtl] .md-typeset .task-list-item [type=checkbox] {\n right: -2em;\n left: initial;\n}\n.md-typeset .task-list-control [type=checkbox] {\n z-index: -1;\n opacity: 0;\n}\n.md-typeset .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: -1.5em;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n[dir=rtl] .md-typeset .task-list-indicator::before {\n right: -1.5em;\n left: initial;\n}\n.md-typeset [type=checkbox]:checked + .task-list-indicator::before {\n background-color: #00e676;\n mask-image: var(--md-tasklist-icon--checked);\n}\n\n@media screen and (min-width: 45em) {\n .md-typeset .inline {\n float: left;\n width: 11.7rem;\n margin-top: 0;\n margin-right: 0.8rem;\n margin-bottom: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n .md-typeset .inline.end {\n float: right;\n margin-right: 0;\n margin-left: 0.8rem;\n }\n [dir=rtl] .md-typeset .inline.end {\n float: left;\n margin-right: 0.8rem;\n margin-left: 0;\n }\n}\n\n/*# sourceMappingURL=main.css.map */","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Enforce correct box model and prevent adjustments of font size after\n// orientation changes in IE and iOS\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n// All elements shall inherit the document default\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n// Remove margin in all browsers\nbody {\n margin: 0;\n}\n\n// Reset tap outlines on iOS and Android\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\n// Reset link styles\na {\n color: inherit;\n text-decoration: none;\n}\n\n// Normalize horizontal separator styles\nhr {\n display: block;\n box-sizing: content-box;\n height: px2rem(1px);\n padding: 0;\n overflow: visible;\n border: 0;\n}\n\n// Normalize font-size in all browsers\nsmall {\n font-size: 80%;\n}\n\n// Prevent subscript and superscript from affecting line-height\nsub,\nsup {\n line-height: 1em;\n}\n\n// Remove border on image\nimg {\n border-style: none;\n}\n\n// Reset table styles\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\n// Reset table cell styles\ntd,\nth {\n font-weight: 400;\n vertical-align: top;\n}\n\n// Reset button styles\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n font-family: inherit;\n background: transparent;\n border: 0;\n}\n\n// Reset input styles\ninput {\n border: 0;\n outline: none;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Color definitions\n:root {\n\n // Default color shades\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n\n // Primary color shades\n --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-400)}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Accent color shades\n --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Light theme (default)\n > * {\n\n // Code color shades\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n\n // Typeset color shades\n --md-typeset-color: var(--md-default-fg-color);\n\n // Typeset `a` color shades\n --md-typeset-a-color: var(--md-primary-fg-color);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n\n // Typeset `del` and `ins` color shades\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n\n // Typeset `table` color shades\n --md-typeset-table-color: hsla(0, 0%, 0%, 0.12);\n\n // Admonition color shades\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n\n // Footer color shades\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon\n.md-icon {\n\n // SVG defaults\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: font definitions\n// ----------------------------------------------------------------------------\n\n// Enable font-smoothing in Webkit and FF\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Define default fonts\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family:\n var(--md-text-font-family, _),\n -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\n// Define monospaced fonts\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family:\n var(--md-code-font-family, _),\n SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: typesetted content\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-typeset-table-sort-icon: svg-load(\"material/sort.svg\");\n --md-typeset-table-sort-icon--asc: svg-load(\"material/sort-ascending.svg\");\n --md-typeset-table-sort-icon--desc: svg-load(\"material/sort-descending.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Content that is typeset - if possible, all margins, paddings and font sizes\n// should be set in ems, so nested blocks (e.g. admonitions) render correctly.\n.md-typeset {\n font-size: px2rem(16px);\n line-height: 1.6;\n color-adjust: exact;\n\n // [print]: We'll use a smaller `font-size` for printing, so code examples\n // don't break too early, and `16px` looks too big anyway.\n @media print {\n font-size: px2rem(13.6px);\n }\n\n // Default spacing\n ul,\n ol,\n dl,\n figure,\n blockquote,\n pre {\n margin: 1em 0;\n }\n\n // Headline on level 1\n h1 {\n margin: 0 0 px2em(40px, 32px);\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: px2em(32px);\n line-height: 1.3;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 2\n h2 {\n margin: px2em(40px, 25px) 0 px2em(16px, 25px);\n font-weight: 300;\n font-size: px2em(25px);\n line-height: 1.4;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3\n h3 {\n margin: px2em(32px, 20px) 0 px2em(16px, 20px);\n font-weight: 400;\n font-size: px2em(20px);\n line-height: 1.5;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 3 following level 2\n h2 + h3 {\n margin-top: px2em(16px, 20px);\n }\n\n // Headline on level 4\n h4 {\n margin: px2em(16px) 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5-6\n h5,\n h6 {\n margin: px2em(16px, 12.8px) 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2em(12.8px);\n letter-spacing: -0.01em;\n }\n\n // Headline on level 5\n h5 {\n text-transform: uppercase;\n }\n\n // Horizontal separator\n hr {\n display: flow-root;\n margin: 1.5em 0;\n border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Text link\n a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n\n // Also enable color transition on pseudo elements\n &,\n &::before {\n transition: color 125ms;\n }\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n }\n\n // Code block\n code,\n pre,\n kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n\n // [print]: Wrap text and hide scollbars\n @media print {\n white-space: pre-wrap;\n }\n }\n\n // Inline code block\n code {\n padding: 0 px2em(4px, 13.6px);\n font-size: px2em(13.6px);\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n box-decoration-break: clone;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Code block in headline\n h1 code,\n h2 code,\n h3 code,\n h4 code,\n h5 code,\n h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n }\n\n // Ensure link color in code blocks\n a code {\n color: currentColor;\n }\n\n // Unformatted content\n pre {\n position: relative;\n display: flow-root;\n line-height: 1.4;\n\n // Code block\n > code {\n display: block;\n margin: 0;\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Code block on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Unformatted text\n > pre {\n margin: 1em px2rem(-16px);\n\n // Code block\n code {\n border-radius: 0;\n }\n }\n }\n\n // Keyboard key\n kbd {\n display: inline-block;\n padding: 0 px2em(8px, 12px);\n color: var(--md-default-fg-color);\n font-size: px2em(12px);\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(2px) 0 px2rem(1px) var(--md-typeset-kbd-border-color),\n 0 px2rem(2px) 0 var(--md-typeset-kbd-border-color),\n 0 px2rem(-2px) px2rem(4px) var(--md-typeset-kbd-accent-color) inset;\n }\n\n // Text highlighting marker\n mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n }\n\n // Abbreviation\n abbr {\n text-decoration: none;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--light);\n cursor: help;\n\n // Show tooltip for touch devices\n @media (hover: none) {\n position: relative;\n\n // Tooltip\n &[title]:focus::after,\n &[title]:hover::after {\n @include z-depth(2);\n\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: px2rem(4px) px2rem(6px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n content: attr(title);\n }\n }\n }\n\n // Small text\n small {\n opacity: 0.75;\n }\n\n // Superscript and subscript\n sup,\n sub {\n margin-left: px2em(1px, 12.8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(1px, 12.8px);\n margin-left: initial;\n }\n }\n\n // Blockquotes, possibly nested\n blockquote {\n padding-left: px2rem(12px);\n color: var(--md-default-fg-color--light);\n border-left: px2rem(4px) solid var(--md-default-fg-color--lighter);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: initial;\n border-right: px2rem(4px) solid var(--md-default-fg-color--lighter);\n border-left: initial;\n }\n }\n\n // Unordered list\n ul {\n list-style-type: disc;\n }\n\n // Unordered and ordered list\n ul,\n ol {\n display: flow-root;\n margin-left: px2em(10px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n\n // Nested ordered list\n ol {\n list-style-type: lower-alpha;\n\n // Triply nested ordered list\n ol {\n list-style-type: lower-roman;\n }\n }\n\n // List element\n li {\n margin-bottom: 0.5em;\n margin-left: px2em(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(20px);\n margin-left: initial;\n }\n\n // Adjust spacing\n p,\n blockquote {\n margin: 0.5em 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Nested list\n ul,\n ol {\n margin: 0.5em 0 0.5em px2em(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n }\n }\n }\n\n // Definition list\n dd {\n margin: 1em 0 1.5em px2em(30px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(30px);\n margin-left: initial;\n }\n }\n\n // Image or icon\n img,\n svg {\n max-width: 100%;\n height: auto;\n\n // Adjust spacing when left-aligned\n &[align=\"left\"] {\n margin: 1em;\n margin-left: 0;\n }\n\n // Adjust spacing when right-aligned\n &[align=\"right\"] {\n margin: 1em;\n margin-right: 0;\n }\n\n // Adjust spacing when sole children\n &[align]:only-child {\n margin-top: 0;\n }\n }\n\n // Figure\n figure {\n display: flow-root;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n\n // Figure images\n img {\n display: block;\n }\n }\n\n // Figure caption\n figcaption {\n max-width: px2rem(480px);\n margin: 1em auto 2em;\n font-style: italic;\n }\n\n // Limit width to container\n iframe {\n max-width: 100%;\n }\n\n // Data table\n table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: px2rem(12.8px);\n background-color: var(--md-default-bg-color);\n border: px2rem(1px) solid var(--md-typeset-table-color);\n border-radius: px2rem(2px);\n touch-action: auto;\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: table;\n }\n\n // Due to margin collapse because of the necessary inline-block hack, we\n // cannot increase the bottom margin on the table, so we just increase the\n // top margin on the following element\n + * {\n margin-top: 1.5em;\n }\n\n // Elements in table heading and cell\n th > *,\n td > * {\n\n // Adjust spacing on first child\n &:first-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Table heading and cell\n th:not([align]),\n td:not([align]) {\n text-align: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n text-align: right;\n }\n }\n\n // Table heading\n th {\n min-width: px2rem(100px);\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n font-weight: 700;\n vertical-align: top;\n\n // Links in table headings\n a {\n color: inherit;\n }\n }\n\n // Table cell\n td {\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n vertical-align: top;\n border-top: px2rem(1px) solid var(--md-typeset-table-color);\n }\n\n // Table body row\n tbody tr {\n transition: background-color 125ms;\n\n // Table row on hover\n &:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 px2rem(1px) 0 var(--md-default-bg-color) inset;\n }\n }\n\n // Text link in table\n a {\n word-break: normal;\n }\n }\n\n // Sortable table\n table th[role=\"columnheader\"] {\n cursor: pointer;\n\n // Sort icon\n &::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: text-bottom;\n mask-image: var(--md-typeset-table-sort-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transition: background-color 125ms;\n content: \"\";\n }\n\n // Show sort icon on hover\n &:hover::after {\n background-color: var(--md-default-fg-color--lighter);\n }\n\n // Sort ascending icon\n &[aria-sort=\"ascending\"]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--asc);\n }\n\n // Sort descending icon\n &[aria-sort=\"descending\"]::after {\n background-color: var(--md-default-fg-color--light);\n mask-image: var(--md-typeset-table-sort-icon--desc);\n }\n }\n\n // Data table scroll wrapper\n &__scrollwrap {\n margin: 1em px2rem(-16px);\n overflow-x: auto;\n touch-action: auto;\n }\n\n // Data table wrapper\n &__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 px2rem(16px);\n\n // [print]: Reset display mode so table header wraps when printing\n @media print {\n display: block;\n }\n\n // Data table\n html & table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","//\n// Name: Material Shadows\n// Description: Mixins for Material Design Shadows.\n// Version: 3.0.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-shadows\n//\n// twitter: @mrmlnc\n//\n// ------------------------------------\n\n\n// Mixins\n// ------------------------------------\n\n@mixin z-depth-transition() {\n transition: box-shadow .28s cubic-bezier(.4, 0, .2, 1);\n}\n\n@mixin z-depth-focus() {\n box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);\n}\n\n@mixin z-depth-2dp() {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),\n 0 1px 5px 0 rgba(0, 0, 0, .12),\n 0 3px 1px -2px rgba(0, 0, 0, .2);\n}\n\n@mixin z-depth-3dp() {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .14),\n 0 1px 8px 0 rgba(0, 0, 0, .12),\n 0 3px 3px -2px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-4dp() {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14),\n 0 1px 10px 0 rgba(0, 0, 0, .12),\n 0 2px 4px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-6dp() {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .14),\n 0 1px 18px 0 rgba(0, 0, 0, .12),\n 0 3px 5px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-8dp() {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14),\n 0 3px 14px 2px rgba(0, 0, 0, .12),\n 0 5px 5px -3px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-16dp() {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14),\n 0 6px 30px 5px rgba(0, 0, 0, .12),\n 0 8px 10px -5px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-24dp() {\n box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14),\n 0 24px 38px 3px rgba(0, 0, 0, .12),\n 0 11px 15px -7px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth($dp: 2) {\n @if $dp == 2 {\n @include z-depth-2dp();\n } @else if $dp == 3 {\n @include z-depth-3dp();\n } @else if $dp == 4 {\n @include z-depth-4dp();\n } @else if $dp == 6 {\n @include z-depth-6dp();\n } @else if $dp == 8 {\n @include z-depth-8dp();\n } @else if $dp == 16 {\n @include z-depth-16dp();\n } @else if $dp == 24 {\n @include z-depth-24dp();\n }\n}\n\n\n// Class generator\n// ------------------------------------\n\n@mixin z-depth-classes($transition: false, $focus: false) {\n @if $transition == true {\n &-transition {\n @include z-depth-transition();\n }\n }\n\n @if $focus == true {\n &-focus {\n @include z-depth-focus();\n }\n }\n\n // The available values for the shadow depth\n @each $depth in 2, 3, 4, 6, 8, 16, 24 {\n &-#{$depth}dp {\n @include z-depth($depth);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: base grid and containers\n// ----------------------------------------------------------------------------\n\n// Stretch container to viewport and set base `font-size`\nhtml {\n height: 100%;\n overflow-x: hidden;\n // Hack: normally, we would set the base `font-size` to `62.5%`, so we can\n // base all calculations on `10px`, but Chromium and Chrome define a minimal\n // `font-size` of `12px` if the system language is set to Chinese. For this\n // reason we just double the `font-size` and set it to `20px`.\n //\n // See https://github.com/squidfunk/mkdocs-material/issues/911\n font-size: 125%;\n\n // [screen medium +]: Set base `font-size` to `11px`\n @include break-from-device(screen medium) {\n font-size: 137.5%;\n }\n\n // [screen large +]: Set base `font-size` to `12px`\n @include break-from-device(screen large) {\n font-size: 150%;\n }\n}\n\n// Stretch body to container - flexbox is used, so the footer will always be\n// aligned to the bottom of the viewport\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n // Hack: reset `font-size` to `10px`, so the spacing for all inline elements\n // is correct again. Otherwise the spacing would be based on `20px`.\n font-size: px2rem(10px);\n background-color: var(--md-default-bg-color);\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n\n // Body in locked state\n &[data-md-state=\"lock\"] {\n\n // [tablet portrait -]: Omit scroll bubbling\n @include break-to-device(tablet portrait) {\n position: fixed;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Grid container - this class is applied to wrapper elements within the\n// header, content area and footer, and makes sure that their width is limited\n// to `1220px`, and they are rendered centered if the screen is larger.\n.md-grid {\n max-width: px2rem(1220px);\n margin-right: auto;\n margin-left: auto;\n}\n\n// Main container\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n\n // [print]: Omit flexbox layout due to a Firefox bug (https://mzl.la/39DgR3m)\n @media print {\n display: block;\n }\n}\n\n// Main area - stretch to remaining space of container\n.md-main {\n flex-grow: 1;\n\n // Main area wrapper\n &__inner {\n display: flex;\n height: 100%;\n margin-top: px2rem(24px + 6px);\n }\n}\n\n// Add ellipsis in case of overflowing text\n.md-ellipsis {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: navigational elements\n// ----------------------------------------------------------------------------\n\n// Toggle - this class is applied to checkbox elements, which are used to\n// implement the CSS-only drawer and navigation, as well as the search\n.md-toggle {\n display: none;\n}\n\n// Option - this class is applied to radio elements, which are used to\n// implement the color palette toggle\n.md-option {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Option label for checked radio button\n &:checked + label:not([hidden]) {\n display: block;\n }\n\n // Show outline for keyboard devices\n &.focus-visible + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n}\n\n// Skip link\n.md-skip {\n position: fixed;\n // Hack: if we don't set the negative `z-index`, the skip link will force the\n // creation of new layers when code blocks are near the header on scrolling\n z-index: -1;\n margin: px2rem(10px);\n padding: px2rem(6px) px2rem(10px);\n color: var(--md-default-bg-color);\n font-size: px2rem(12.8px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n transform: translateY(px2rem(8px));\n opacity: 0;\n\n // Show skip link on focus\n &:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 175ms 75ms;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: print styles\n// ----------------------------------------------------------------------------\n\n// Add margins to page\n@page {\n margin: 25mm;\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Announcement bar\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide announcement bar\n @media print {\n display: none;\n }\n\n // Announcement wrapper\n &__inner {\n margin: px2rem(12px) auto;\n padding: 0 px2rem(16px);\n color: var(--md-footer-fg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-clipboard-icon: svg-load(\"material/content-copy.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Button to copy to clipboard\n.md-clipboard {\n position: absolute;\n top: px2em(8px);\n right: px2em(8px);\n z-index: 1;\n width: px2em(24px);\n height: px2em(24px);\n color: var(--md-default-fg-color--lightest);\n border-radius: px2rem(2px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(2px);\n cursor: pointer;\n transition: color 250ms;\n\n // [print]: Hide button\n @media print {\n display: none;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Darken color on code block hover\n :hover > & {\n color: var(--md-default-fg-color--light);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Button icon - the width and height are defined in `em`, so the size is\n // automatically adjusted for nested code blocks (e.g. in admonitions)\n &::after {\n display: block;\n width: px2em(18px);\n height: px2em(18px);\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Inline button\n &--inline {\n cursor: pointer;\n\n // Code block\n code {\n transition:\n color 250ms,\n background-color 250ms;\n }\n\n // Code block on focus/hover\n &:focus code,\n &:hover code {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Content area\n.md-content {\n flex-grow: 1;\n // Hack: we must use `overflow: hidden`, so the content area is capped by\n // the dimensions of its parent. Otherwise, long code blocks might lead to\n // a wider content area which will break everything. This, however, induces\n // margin collapse, which will break scroll margins. Adding a large enough\n // scroll padding seems to do the trick, at least in Chrome and Firefox.\n overflow: hidden;\n scroll-padding-top: px2rem(1024px);\n\n // Content wrapper\n &__inner {\n margin: 0 px2rem(16px) px2rem(24px);\n padding-top: px2rem(12px);\n\n // [screen +]: Adjust spacing between content area and sidebars\n @include break-from-device(screen) {\n\n // Sidebar with navigation is visible\n .md-sidebar--primary:not([hidden]) ~ .md-content > & {\n margin-left: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(24px);\n margin-left: px2rem(16px);\n }\n }\n\n // Sidebar with table of contents is visible\n .md-sidebar--secondary:not([hidden]) ~ .md-content > & {\n margin-right: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(16px);\n margin-left: px2rem(24px);\n }\n }\n }\n\n // Hack: add pseudo element for spacing, as the overflow of the content\n // container may not be hidden due to an imminent offset error on targets\n &::before {\n display: block;\n height: px2rem(8px);\n content: \"\";\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0;\n }\n }\n\n // Button inside of the content area - these buttons are meant for actions on\n // a document-level, i.e. linking to related source code files, printing etc.\n &__button {\n float: right;\n margin: px2rem(8px) 0;\n margin-left: px2rem(8px);\n padding: 0;\n\n // [print]: Hide buttons\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(8px);\n margin-left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Adjust default link color for icons\n .md-typeset & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Align with body copy located next to icon\n svg {\n display: inline;\n vertical-align: top;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Dialog\n.md-dialog {\n @include z-depth(2);\n\n position: fixed;\n right: px2rem(16px);\n bottom: px2rem(16px);\n left: initial;\n z-index: 3;\n min-width: px2rem(222px);\n padding: px2rem(8px) px2rem(12px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(100%);\n opacity: 0;\n transition:\n transform 0ms 400ms,\n opacity 400ms;\n pointer-events: none;\n\n // [print]: Hide dialog\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n\n // Dialog in open state\n &[data-md-state=\"open\"] {\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),\n opacity 400ms;\n pointer-events: initial;\n }\n\n // Dialog wrapper\n &__inner {\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Form button\n .md-button {\n display: inline-block;\n padding: px2em(10px) px2em(32px);\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: px2rem(2px) solid currentColor;\n border-radius: px2rem(2px);\n cursor: pointer;\n transition:\n color 125ms,\n background-color 125ms,\n border-color 125ms;\n\n // Primary button\n &--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n }\n\n // Button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n }\n\n // Form input\n .md-input {\n height: px2rem(36px);\n padding: 0 px2rem(12px);\n font-size: px2rem(16px);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);\n transition: box-shadow 250ms;\n\n // Input on focus/hover\n &:focus,\n &:hover {\n box-shadow:\n 0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);\n }\n\n // Stretch to full width\n &--stretch {\n width: 100%;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Header - by default, the header will be sticky and stay always on top of the\n// viewport. If this behavior is not desired, just set `position: static`.\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 3;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n // Hack: reduce jitter by adding a transparent box shadow of the same size\n // so the size of the layer doesn't change during animation\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0);\n\n // [print]: Hide header\n @media print {\n display: none;\n }\n\n // Header in shadow state, i.e. shadow is visible\n &[data-md-state=\"shadow\"] {\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0.1),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0.2);\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n box-shadow 250ms;\n }\n\n // Header in hidden state, i.e. moved out of sight\n &[data-md-state=\"hidden\"] {\n transform: translateY(-100%);\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n box-shadow 250ms;\n }\n\n // Header wrapper\n &__inner {\n display: flex;\n align-items: center;\n padding: 0 px2rem(4px);\n }\n\n // Header button\n &__button {\n position: relative;\n z-index: 1;\n margin: px2rem(4px);\n padding: px2rem(8px);\n color: currentColor;\n vertical-align: middle;\n outline-color: var(--md-accent-fg-color);\n cursor: pointer;\n transition: opacity 250ms;\n\n // Button on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Header button is visible\n &:not([hidden]) {\n display: inline-block;\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // [tablet -]: Hide button\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n }\n\n // Button for search\n &[for=\"__search\"] {\n\n // [tablet landscape +]: Hide button\n @include break-from-device(tablet landscape) {\n display: none;\n }\n\n // [no-js]: Hide button\n .no-js & {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Button for drawer\n &[for=\"__drawer\"] {\n\n // [screen +]: Hide button\n @include break-from-device(screen) {\n display: none;\n }\n }\n }\n\n // Header topic\n &__topic {\n position: absolute;\n display: flex;\n max-width: 100%;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Second header topic - title of the current page\n & + & {\n z-index: -1;\n transform: translateX(px2rem(25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-25px));\n }\n }\n }\n\n // Header title\n &__title {\n flex-grow: 1;\n height: px2rem(48px);\n margin-right: px2rem(8px);\n margin-left: px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n\n // Header title in active state, i.e. page title is visible\n &[data-md-state=\"active\"] .md-header__topic {\n z-index: -1;\n transform: translateX(px2rem(-25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(25px));\n }\n\n // Second header topic - title of the current page\n + .md-header__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: initial;\n }\n }\n\n // Add ellipsis in case of overflowing text\n > .md-header__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n }\n }\n\n // Header option\n &__option {\n display: flex;\n flex-shrink: 0;\n max-width: 100%;\n white-space: nowrap;\n transition:\n max-width 0ms 250ms,\n opacity 250ms 250ms;\n\n // Hide toggle when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-width: 0;\n opacity: 0;\n transition:\n max-width 0ms,\n opacity 0ms;\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n\n // [tablet landscape +]: Show repository information\n @include break-from-device(tablet landscape) {\n display: block;\n width: px2rem(234px);\n max-width: px2rem(234px);\n margin-left: px2rem(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(20px);\n margin-left: initial;\n }\n }\n\n // [screen +]: Adjust spacing of search bar\n @include break-from-device(screen) {\n margin-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Footer\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n\n // [print]: Hide footer\n @media print {\n display: none;\n }\n\n // Footer wrapper\n &__inner {\n padding: px2rem(4px);\n overflow: auto;\n }\n\n // Footer link to previous and next page\n &__link {\n display: flex;\n padding-top: px2rem(28px);\n padding-bottom: px2rem(8px);\n outline-color: var(--md-accent-fg-color);\n transition: opacity 250ms;\n\n // [tablet +]: Adjust width to 50/50\n @include break-from-device(tablet) {\n width: 50%;\n }\n\n // Footer link on focus/hover\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Footer link to previous page\n &--prev {\n float: left;\n\n // [mobile -]: Adjust width to 25/75 and hide title\n @include break-to-device(mobile) {\n width: 25%;\n\n // Hide footer title\n .md-footer__title {\n display: none;\n }\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n\n // Footer link to next page\n &--next {\n float: right;\n text-align: right;\n\n // [mobile -]: Adjust width to 25/75\n @include break-to-device(mobile) {\n width: 75%;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n text-align: left;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footer title\n &__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - #{px2rem(48px)});\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n }\n\n // Footer link button\n &__button {\n margin: px2rem(4px);\n padding: px2rem(8px);\n }\n\n // Footer link direction (i.e. prev and next)\n &__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: px2rem(-20px);\n padding: 0 px2rem(20px);\n font-size: px2rem(12.8px);\n opacity: 0.7;\n }\n}\n\n// Footer metadata\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n\n // Footer metadata wrapper\n &__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: px2rem(4px);\n }\n\n // Lighten color for non-hovered text links\n html &.md-typeset a {\n color: var(--md-footer-fg-color--light);\n\n // Text link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-footer-fg-color);\n }\n }\n}\n\n// Footer copyright and theme information\n.md-footer-copyright {\n width: 100%;\n margin: auto px2rem(12px);\n padding: px2rem(8px) 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: px2rem(12.8px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n width: auto;\n }\n\n // Footer copyright highlight - this is the upper part of the copyright and\n // theme information, which will include a darker color than the theme link\n &__highlight {\n color: var(--md-footer-fg-color--light);\n }\n}\n\n// Footer social links\n.md-footer-social {\n margin: 0 px2rem(8px);\n padding: px2rem(4px) 0 px2rem(12px);\n\n // [tablet portrait +]: Show copyright and social links in one line\n @include break-from-device(tablet portrait) {\n padding: px2rem(12px) 0;\n }\n\n // Footer social link\n &__link {\n display: inline-block;\n width: px2rem(32px);\n height: px2rem(32px);\n text-align: center;\n\n // Adjust line-height to match height for correct alignment\n &::before {\n line-height: 1.9;\n }\n\n // Fill icon with current color\n svg {\n max-height: px2rem(16px);\n vertical-align: -25%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-nav-icon--prev: svg-load(\"material/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"material/chevron-right.svg\");\n --md-toc-icon: svg-load(\"material/table-of-contents.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Navigation\n.md-nav {\n font-size: px2rem(14px);\n line-height: 1.3;\n\n // Navigation title\n &__title {\n display: block;\n padding: 0 px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n\n // Navigaton button\n .md-nav__button {\n display: none;\n\n // Stretch images based on height, as it's the smaller dimension\n img {\n width: auto;\n height: 100%;\n }\n\n // Button with logo, pointing to `config.site_url`\n &.md-logo {\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(48px);\n height: px2rem(48px);\n fill: currentColor;\n }\n }\n }\n }\n\n // Navigation list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Navigation item\n &__item {\n padding: 0 px2rem(12px);\n\n // Navigation item on level 2\n & & {\n padding-right: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: 0;\n }\n }\n }\n\n // Navigation link\n &__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n\n // Link in blurred state\n &[data-md-state=\"blur\"] {\n color: var(--md-default-fg-color--light);\n }\n\n // Active link\n .md-nav__item &--active {\n color: var(--md-typeset-a-color);\n }\n\n // Navigation link in nested list\n .md-nav__item--nested > & {\n color: inherit;\n }\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n\n // Navigation link to table of contents\n .md-nav--primary &[for=\"__toc\"] {\n display: none;\n\n // Table of contents icon\n .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n }\n\n // Hide table of contents\n ~ .md-nav {\n display: none;\n }\n }\n }\n\n // Repository information container\n &__source {\n display: none;\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n\n // Primary and nested navigation\n &--primary,\n &--primary & {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-default-bg-color);\n }\n\n // Primary navigation\n &--primary {\n\n // Navigation title and item\n .md-nav__title,\n .md-nav__item {\n font-size: px2rem(16px);\n line-height: 1.5;\n }\n\n // Navigation title\n .md-nav__title {\n position: relative;\n height: px2rem(112px);\n padding: px2rem(60px) px2rem(16px) px2rem(4px);\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: px2rem(48px);\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: px2rem(8px);\n left: px2rem(8px);\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(4px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(8px);\n left: initial;\n }\n\n // Navigation icon in link to previous level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n\n // Navigation list\n ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow:\n 0 px2rem(1px) 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Omit border on first child\n > :first-child {\n border-top: 0;\n }\n }\n\n // Top-level navigation title\n &[for=\"__drawer\"] {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n }\n\n // Button with logo, pointing to `config.site_url`\n .md-logo {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(4px);\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(4px);\n left: initial;\n }\n }\n }\n\n // Navigation list\n .md-nav__list {\n flex: 1;\n }\n\n // Navigation item\n .md-nav__item {\n padding: 0;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n\n // Navigation link in nested navigation\n &--nested > .md-nav__link {\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n }\n\n // Navigation link in active navigation\n &--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n\n // Navigation link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Navigation link\n .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: px2rem(12px) px2rem(16px);\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: 50%;\n right: px2rem(12px);\n width: px2rem(24px);\n height: px2rem(24px);\n margin-top: px2rem(-12px);\n color: inherit;\n font-size: px2rem(24px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(12px);\n }\n\n // Navigation icon in link to next level\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n }\n\n // Flip icon vertically\n .md-nav__icon {\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] &::after {\n transform: scale(-1);\n }\n }\n\n // Table of contents contained in primary navigation\n .md-nav--secondary {\n\n // Navigation link - omit unnecessary layering\n .md-nav__link {\n position: static;\n }\n\n // Navigation on level 2-6\n .md-nav {\n position: static;\n background-color: transparent;\n\n // Navigation link on level 3\n .md-nav__link {\n padding-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(28px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 4\n .md-nav .md-nav__link {\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 5\n .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(52px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(52px);\n padding-left: initial;\n }\n }\n\n // Navigation link on level 6\n .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(64px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(64px);\n padding-left: initial;\n }\n }\n }\n }\n }\n\n // Table of contents\n &--secondary {\n background-color: transparent;\n }\n\n // Toggle for nested navigation\n &__toggle ~ & {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n opacity 125ms 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(-100%);\n }\n }\n\n // Show nested navigation when toggle is active\n &__toggle:checked ~ & {\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms 125ms;\n\n // Navigation list\n > .md-nav__list {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n }\n }\n }\n\n // [tablet portrait -]: Layered navigation with table of contents\n @include break-to-device(tablet portrait) {\n\n // Show link to table of contents\n &--primary &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n\n // Repository information container\n &__source {\n display: block;\n padding: 0 px2rem(4px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n }\n\n // [tablet landscape]: Layered navigation with table of contents\n @include break-at-device(tablet landscape) {\n\n // Show link to integrated table of contents\n &--integrated &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n scroll-snap-align: initial;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Show table of contents icon\n .md-icon::after {\n content: \"\";\n }\n\n // Hide navigation link to current page\n + .md-nav__link {\n display: none;\n }\n\n // Show table of contents\n ~ .md-nav {\n display: flex;\n }\n }\n }\n\n // [tablet landscape +]: Tree-like table of contents\n @include break-from-device(tablet landscape) {\n\n // Navigation title\n &--secondary &__title {\n\n // Adjust snapping behavior\n &[for=\"__toc\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n\n // [screen +]: Tree-like navigation\n @include break-from-device(screen) {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n\n // Navigation title\n &--primary &__title {\n\n // Adjust snapping behavior\n &[for=\"__drawer\"] {\n scroll-snap-align: start;\n }\n\n // Hide navigation icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Hide toggle for nested navigation\n &__toggle ~ & {\n display: none;\n }\n\n // Show nested navigation when toggle is active or indeterminate\n &__toggle:checked ~ &,\n &__toggle:indeterminate ~ & {\n display: block;\n }\n\n // Hide navigation title in nested navigation\n &__item--nested > & > &__title {\n display: none;\n }\n\n // Navigation section\n &__item--section {\n display: block;\n margin: 1.25em 0;\n\n // Adjust spacing on last child\n &:last-child {\n margin-bottom: 0;\n }\n\n // Hide navigation link, as sections are always expanded\n > .md-nav__link {\n display: none;\n }\n\n // Navigation\n > .md-nav {\n display: block;\n\n // Navigation title\n > .md-nav__title {\n display: block;\n padding: 0;\n pointer-events: none;\n scroll-snap-align: start;\n }\n\n // Adjust spacing on next level item\n > .md-nav__list > .md-nav__item {\n padding: 0;\n }\n }\n }\n\n // Navigation icon\n &__icon {\n float: right;\n width: px2rem(18px);\n height: px2rem(18px);\n transition: transform 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n transform: rotate(180deg);\n }\n\n // Navigation icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: px2rem(-2px);\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Navigation icon - rotate icon when toggle is active or indeterminate\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link &,\n .md-nav__item--nested .md-nav__toggle:indeterminate ~ .md-nav__link & {\n transform: rotate(90deg);\n }\n }\n\n // Modifier for when navigation tabs are rendered\n &--lifted {\n\n // Hide nested level 0 items and site title\n > .md-nav__list > .md-nav__item--nested,\n > .md-nav__title {\n display: none;\n }\n\n // Hide level 0 items\n > .md-nav__list > .md-nav__item {\n display: none;\n\n // Active parent navigation item\n &--active {\n display: block;\n padding: 0;\n\n // Hide nested links\n > .md-nav__link {\n display: none;\n }\n\n // Show title and adjust spacing\n > .md-nav > .md-nav__title {\n display: block;\n padding: 0 px2rem(12px);\n pointer-events: none;\n scroll-snap-align: start;\n }\n }\n }\n\n // Hack: Always show active navigation tab on breakpoint screen, despite\n // of checkbox being checked or not. Fixes #1655.\n .md-nav[data-md-level=\"1\"] {\n display: block;\n\n // Adjust spacing for level 1 items\n > .md-nav__list > .md-nav__item {\n padding-right: px2rem(12px);\n }\n }\n }\n\n // Modifier for when table of contents is rendered in primary navigation\n &--integrated &__link[for=\"__toc\"] ~ .md-nav {\n display: block;\n margin-bottom: 1.25em;\n border-left: px2rem(1px) solid var(--md-primary-fg-color);\n\n // Hide navigation title\n > .md-nav__title {\n display: none;\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-search-result-icon: svg-load(\"material/file-search-outline.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Search\n.md-search {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding: px2rem(4px) 0;\n }\n\n // [no-js]: Hide search\n .no-js & {\n display: none;\n }\n\n // Search overlay\n &__overlay {\n z-index: 1;\n opacity: 0;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: absolute;\n top: px2rem(-20px);\n left: px2rem(-44px);\n width: px2rem(40px);\n height: px2rem(40px);\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(20px);\n transform-origin: center;\n transition:\n transform 300ms 100ms,\n opacity 200ms 200ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-44px);\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition:\n transform 400ms,\n opacity 100ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n cursor: pointer;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n\n // Show overlay when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n // Hack: when the header is translated upon scrolling, a new layer is\n // induced, which means that the height will now refer to the height of\n // the header, albeit positioning is fixed. This should be mitigated\n // in all cases when setting the height to 2x the viewport.\n height: 200vh;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [mobile portrait -]: Scale up 45 times\n @include break-to-device(mobile portrait) {\n transform: scale(45);\n }\n\n // [mobile landscape]: Scale up 60 times\n @include break-at-device(mobile landscape) {\n transform: scale(60);\n }\n\n // [tablet portrait]: Scale up 75 times\n @include break-at-device(tablet portrait) {\n transform: scale(75);\n }\n }\n }\n\n // Search wrapper\n &__inner {\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 2;\n width: 0;\n height: 0;\n overflow: hidden;\n transform: translateX(5%);\n opacity: 0;\n transition:\n width 0ms 300ms,\n height 0ms 300ms,\n transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 150ms 150ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n transform: translateX(-5%);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n width: 100%;\n height: 100%;\n transform: translateX(0);\n opacity: 1;\n transition:\n width 0ms 0ms,\n height 0ms 0ms,\n transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms 150ms;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: relative;\n float: right;\n width: px2rem(234px);\n padding: px2rem(2px) 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n\n // [tablet landscape]: Omit overlaying header title\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Match width of content area\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n }\n }\n\n // Search form\n &__form {\n position: relative;\n z-index: 2;\n height: px2rem(48px);\n background-color: var(--md-default-bg-color);\n box-shadow: 0 0 px2rem(12px) transparent;\n transition:\n color 250ms,\n background-color 250ms;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n height: px2rem(36px);\n background-color: hsla(0, 0%, 0%, 0.26);\n border-radius: px2rem(2px);\n\n // Search form on hover\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.12);\n }\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px) px2rem(2px) 0 0;\n box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);\n }\n }\n\n // Search input\n &__input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: 100%;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n font-size: px2rem(18px);\n text-overflow: ellipsis;\n background: transparent;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n transition: color 250ms;\n }\n\n // Search icon and placeholder\n ~ .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n\n // Remove the \"x\" rendered by Internet Explorer\n &::-ms-clear {\n display: none;\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n width: 100%;\n height: px2rem(48px);\n font-size: px2rem(18px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n color: inherit;\n font-size: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n\n // Search placeholder\n &::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n\n // Search icon\n + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n\n // Adjust appearance when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n text-overflow: clip;\n\n // Search icon and placeholder\n + .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n }\n }\n }\n\n // Search icon\n &__icon {\n display: inline-block;\n width: px2rem(24px);\n height: px2rem(24px);\n cursor: pointer;\n transition:\n color 250ms,\n opacity 250ms;\n\n // Search icon on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Search focus button\n &[for=\"__search\"] {\n position: absolute;\n top: px2rem(6px);\n left: px2rem(10px);\n z-index: 2;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(10px);\n left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n\n // Hide the magnifying glass\n svg:first-child {\n display: none;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n pointer-events: none;\n\n // Hide the back arrow\n svg:last-child {\n display: none;\n }\n }\n }\n }\n\n // Search options\n &__options {\n position: absolute;\n top: px2rem(6px);\n right: px2rem(10px);\n z-index: 2;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(10px);\n }\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n right: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n }\n\n // Search option buttons\n > * {\n margin-left: px2rem(4px);\n color: var(--md-default-fg-color--light);\n transform: scale(0.75);\n opacity: 0;\n transition:\n transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Show reset button when search is active and input non-empty\n [data-md-toggle=\"search\"]:checked ~ .md-header\n .md-search__input:valid ~ & {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n\n // Search focus icon\n &:hover {\n opacity: 0.7;\n }\n }\n }\n }\n\n // Search suggestions\n &__suggest {\n position: absolute;\n top: 0;\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n color: var(--md-default-fg-color--lighter);\n font-size: px2rem(18px);\n white-space: nowrap;\n opacity: 0;\n transition: opacity 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n font-size: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n }\n\n // Show suggestions when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n opacity: 1;\n transition: opacity 300ms 100ms;\n }\n }\n\n // Search output\n &__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 px2rem(2px) px2rem(2px);\n\n // [tablet portrait -]: Search modal\n @include break-to-device(tablet portrait) {\n top: px2rem(48px);\n bottom: 0;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n top: px2rem(38px);\n opacity: 0;\n transition: opacity 400ms;\n\n // Show output when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n @include z-depth(6);\n\n opacity: 1;\n }\n }\n }\n\n // Search scroll wrapper\n &__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 88+ has weird overscroll behavior. Overall, scroll snapping\n // seems to be something that is not ready for prime time on some browsers.\n // scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Mitigiate excessive repaints on non-retina devices\n @media (max-resolution: 1dppx) {\n transform: translateZ(0);\n }\n\n // [tablet landscape]: Set fixed width to omit unnecessary reflow\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Set fixed width to omit unnecessary reflow\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n\n // [tablet landscape +]: Limit height to viewport\n @include break-from-device(tablet landscape) {\n max-height: 0;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Show scroll wrapper when search is active\n [data-md-toggle=\"search\"]:checked ~ .md-header & {\n max-height: 75vh;\n }\n\n // Search scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n}\n\n// Search result\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n\n // Search result metadata\n &__meta {\n padding: 0 px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: px2rem(36px);\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: initial;\n }\n }\n }\n\n // Search result list\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // Search result item\n &__item {\n box-shadow: 0 px2rem(-1px) 0 var(--md-default-fg-color--lightest);\n\n // Omit border on first child\n &:first-child {\n box-shadow: none;\n }\n }\n\n // Search result link\n &__link {\n display: block;\n outline: none;\n transition: background-color 250ms;\n scroll-snap-align: start;\n\n // Search result link on focus/hover\n &:focus,\n &:hover {\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Adjust spacing on last child of last link\n &:last-child p:last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Search result more link\n &__more summary {\n display: block;\n padding: px2em(12px) px2rem(16px);\n color: var(--md-typeset-a-color);\n font-size: px2rem(12.8px);\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result more link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n\n // Adjust transparency of less relevant results\n ~ * > * {\n opacity: 0.65;\n }\n }\n\n // Search result article\n &__article {\n position: relative;\n padding: 0 px2rem(16px);\n overflow: hidden;\n\n // [tablet landscape +]: Adjust spacing\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Search result article document\n &--document {\n\n // Search result title\n .md-search-result__title {\n margin: px2rem(11px) 0;\n font-weight: 400;\n font-size: px2rem(16px);\n line-height: 1.4;\n }\n }\n }\n\n // Search result icon\n &__icon {\n position: absolute;\n left: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(10px);\n color: var(--md-default-fg-color--light);\n\n // [tablet portrait -]: Hide icon\n @include break-to-device(tablet portrait) {\n display: none;\n }\n\n // Search result icon content\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n\n // Flip icon vertically\n &::after {\n transform: scaleX(-1);\n }\n }\n }\n\n // Search result title\n &__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: px2rem(12.8px);\n line-height: 1.6;\n }\n\n // Search result teaser\n &__teaser {\n display: -webkit-box;\n max-height: px2rem(40px);\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n\n // [mobile -]: Adjust number of lines\n @include break-to-device(mobile) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // [tablet landscape]: Adjust number of lines\n @include break-at-device(tablet landscape) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // Search term highlighting\n mark {\n text-decoration: underline;\n background-color: transparent;\n }\n }\n\n // Search result terms\n &__terms {\n margin: 0.5em 0;\n font-size: px2rem(12.8px);\n font-style: italic;\n }\n\n // Search term highlighting\n mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Selection\n.md-select {\n position: relative;\n z-index: 1;\n\n // Selection bubble\n &__inner {\n position: absolute;\n top: calc(100% - #{px2rem(4px)});\n left: 50%;\n max-height: 0;\n margin-top: px2rem(4px);\n color: var(--md-default-fg-color);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate3d(-50%, px2rem(6px), 0);\n opacity: 0;\n transition:\n transform 250ms 375ms,\n opacity 250ms 250ms,\n max-height 0ms 500ms;\n\n // Selection bubble on parent focus/hover\n .md-select:focus-within &,\n .md-select:hover & {\n max-height: px2rem(200px);\n transform: translate3d(-50%, 0, 0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms,\n max-height 0ms;\n }\n\n // Selection bubble handle\n &::after {\n position: absolute;\n top: 0;\n left: 50%;\n width: 0;\n height: 0;\n margin-top: px2rem(-4px);\n margin-left: px2rem(-4px);\n border: px2rem(4px) solid transparent;\n border-top: 0;\n border-bottom-color: var(--md-default-bg-color);\n content: \"\";\n }\n }\n\n // Selection list\n &__list {\n max-height: inherit;\n margin: 0;\n padding: 0;\n overflow: auto;\n font-size: px2rem(16px);\n list-style-type: none;\n border-radius: px2rem(2px);\n }\n\n // Selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n outline: none;\n cursor: pointer;\n transition:\n background-color 250ms,\n color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Sidebar\n.md-sidebar {\n position: sticky;\n top: px2rem(48px);\n flex-shrink: 0;\n align-self: flex-start;\n width: px2rem(242px);\n padding: px2rem(24px) 0;\n\n // [print]: Hide sidebar\n @media print {\n display: none;\n }\n\n // [tablet -]: Show navigation as drawer\n @include break-to-device(tablet) {\n\n // Primary sidebar with navigation\n &--primary {\n position: fixed;\n top: 0;\n left: px2rem(-242px);\n z-index: 4;\n display: block;\n width: px2rem(242px);\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-242px);\n left: initial;\n }\n\n // Show sidebar when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ .md-container & {\n @include z-depth(8);\n\n transform: translateX(px2rem(242px));\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-242px));\n }\n }\n\n // Stretch scroll wrapper for primary sidebar\n .md-sidebar__scrollwrap {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n overflow: hidden;\n }\n }\n }\n\n // [screen +]: Show navigation as sidebar\n @include break-from-device(screen) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n }\n\n // Secondary sidebar with table of contents\n &--secondary {\n display: none;\n order: 2;\n\n // [tablet landscape +]: Show table of contents as sidebar\n @include break-from-device(tablet landscape) {\n height: 0;\n\n // [no-js]: Switch to native sticky behavior\n .no-js & {\n height: auto;\n }\n\n // Sidebar is visible\n &:not([hidden]) {\n display: block;\n }\n\n // Ensure smooth scrolling on iOS\n .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n }\n }\n\n // Sidebar scroll wrapper\n &__scrollwrap {\n margin: 0 px2rem(4px);\n overflow-y: auto;\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n // Hack: Chrome 81+ exhibits a strange bug, where it scrolls the container\n // to the bottom if `scroll-snap-type` is set on the initial render. For\n // this reason, we disable scroll snapping until this is resolved (#1667).\n // scroll-snap-type: y mandatory;\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Sidebar scroll wrapper on hover\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Webkit scrollbar\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Webkit scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Webkit scrollbar thumb on hover\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n}\n\n// [tablet -]: Show overlay on active drawer\n@include break-to-device(tablet) {\n\n // Sidebar overlay\n .md-overlay {\n position: fixed;\n top: 0;\n z-index: 4;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n opacity: 0;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Show overlay when drawer is active\n [data-md-toggle=\"drawer\"]:checked ~ & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Show repository facts\n@keyframes facts {\n 0% {\n height: 0;\n }\n\n 100% {\n height: px2rem(13px);\n }\n}\n\n// Show repository fact\n@keyframes fact {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-source-forks-icon: svg-load(\"octicons/repo-forked-16.svg\");\n --md-source-repositories-icon: svg-load(\"octicons/repo-16.svg\");\n --md-source-stars-icon: svg-load(\"octicons/star-16.svg\");\n --md-source-version-icon: svg-load(\"octicons/tag-16.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Repository information\n.md-source {\n display: block;\n font-size: px2rem(13px);\n line-height: 1.2;\n white-space: nowrap;\n outline-color: var(--md-accent-fg-color);\n // Hack: promote to own layer to reduce jitter\n backface-visibility: hidden;\n transition: opacity 250ms;\n\n // Repository information on hover\n &:hover {\n opacity: 0.7;\n }\n\n // Repository icon\n &__icon {\n display: inline-block;\n width: px2rem(40px);\n height: px2rem(48px);\n vertical-align: middle;\n\n // Align with margin only (as opposed to normal button alignment)\n svg {\n margin-top: px2rem(12px);\n margin-left: px2rem(12px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(12px);\n margin-left: initial;\n }\n }\n\n // Adjust spacing if icon is present\n + .md-source__repository {\n margin-left: px2rem(-40px);\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(-40px);\n margin-left: initial;\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n }\n\n // Repository name\n &__repository {\n display: inline-block;\n max-width: calc(100% - #{px2rem(24px)});\n margin-left: px2rem(12px);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n }\n\n // Repository facts\n &__facts {\n margin: px2rem(2px) 0 0;\n padding: 0;\n overflow: hidden;\n font-size: px2rem(11px);\n list-style-type: none;\n opacity: 0.75;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: facts 250ms ease-in;\n }\n }\n\n // Repository fact\n &__fact {\n display: inline-block;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: fact 400ms ease-out;\n }\n\n // Repository fact icon\n &::before {\n display: inline-block;\n width: px2rem(12px);\n height: px2rem(12px);\n margin-right: px2rem(2px);\n vertical-align: text-top;\n background-color: currentColor;\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-left: px2rem(8px);\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: initial;\n margin-left: px2rem(2px);\n\n // Adjust spacing for repository fact icon\n &:nth-child(1n+2)::before {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n\n // Repository fact: version\n &--version::before {\n mask-image: var(--md-source-version-icon);\n }\n\n // Repository fact: stars\n &--stars::before {\n mask-image: var(--md-source-stars-icon);\n }\n\n // Repository fact: forks\n &--forks::before {\n mask-image: var(--md-source-forks-icon);\n }\n\n // Repository fact: repositories\n &--repositories::before {\n mask-image: var(--md-source-repositories-icon);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Navigation tabs\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n\n // [print]: Hide tabs\n @media print {\n display: none;\n }\n\n // [tablet -]: Hide tabs\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Tabs in hidden state, i.e. when scrolling down\n &[data-md-state=\"hidden\"] {\n pointer-events: none;\n }\n\n // Navigation tabs list\n &__list {\n margin: 0;\n margin-left: px2rem(4px);\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(4px);\n margin-left: initial;\n }\n }\n\n // Navigation tabs item\n &__item {\n display: inline-block;\n height: px2rem(48px);\n padding-right: px2rem(12px);\n padding-left: px2rem(12px);\n }\n\n // Navigation tabs link - could be defined as block elements and aligned via\n // line height, but this would imply more repaints when scrolling\n &__link {\n display: block;\n margin-top: px2rem(16px);\n font-size: px2rem(14px);\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n // Hack: save a repaint when tabs are appearing on scrolling up\n backface-visibility: hidden;\n opacity: 0.7;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms;\n\n // Active link and link on focus/hover\n &--active,\n &:focus,\n &:hover {\n color: inherit;\n opacity: 1;\n }\n\n // Delay transitions by a small amount\n @for $i from 2 through 16 {\n .md-tabs__item:nth-child(#{$i}) & {\n transition-delay: 20ms * ($i - 1);\n }\n }\n\n // Hide tabs upon scrolling - disable transition to minimizes repaints\n // while scrolling down, while scrolling up seems to be okay\n .md-tabs[data-md-state=\"hidden\"] & {\n transform: translateY(50%);\n opacity: 0;\n transition:\n transform 0ms 100ms,\n opacity 100ms;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Back-to-top button\n.md-top {\n position: fixed;\n top: px2rem(48px + 16px);\n z-index: 2;\n margin-left: 50%;\n padding: px2rem(8px) px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(14px);\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(32px);\n outline: none;\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n transform: translate(-50%, 0);\n transition:\n color 125ms,\n background-color 125ms,\n transform 125ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms;\n\n // [print]: Hide back-to-top button\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n\n // Back-to-top button in hidden state\n &[data-md-state=\"hidden\"] {\n transform: translate(-50%, px2rem(4px));\n opacity: 0;\n transition-duration: 0ms;\n pointer-events: none;\n }\n\n // Back-to-top button on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n }\n\n // Inline icon\n svg {\n display: inline-block;\n vertical-align: -0.5em;\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// See https://github.com/squidfunk/mkdocs-material/issues/2429\n@keyframes hoverfix {\n 0% {\n pointer-events: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-version-icon: svg-load(\"fontawesome/solid/caret-down.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Version selection\n.md-version {\n flex-shrink: 0;\n height: px2rem(48px);\n font-size: px2rem(16px);\n\n // Current selection\n &__current {\n position: relative;\n // Hack: in general, we would use `vertical-align` to align the version at\n // the bottom with the title, but since the list uses absolute positioning,\n // this won't work consistently. Furthermore, we would need to use inline\n // positioning to align the links, which looks jagged.\n top: px2rem(1px);\n margin-right: px2rem(8px);\n margin-left: px2rem(28px);\n color: inherit;\n outline: none;\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n margin-left: px2rem(8px);\n }\n\n // Version selection icon\n &::after {\n display: inline-block;\n width: px2rem(8px);\n height: px2rem(12px);\n margin-left: px2rem(8px);\n background-color: currentColor;\n mask-image: var(--md-version-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(8px);\n margin-left: initial;\n }\n }\n }\n\n // Version selection list\n &__list {\n position: absolute;\n top: px2rem(3px);\n z-index: 1;\n max-height: 0;\n margin: px2rem(4px) px2rem(16px);\n padding: 0;\n overflow: auto;\n color: var(--md-default-fg-color);\n list-style-type: none;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);\n opacity: 0;\n transition:\n max-height 0ms 500ms,\n opacity 250ms 250ms;\n scroll-snap-type: y mandatory;\n\n // Version selection list on parent focus/hover\n .md-version:focus-within &,\n .md-version:hover & {\n max-height: px2rem(200px);\n opacity: 1;\n transition:\n max-height 0ms,\n opacity 250ms;\n }\n\n // Fix hover on touch devices\n @media (pointer: coarse) {\n\n // Switch off on hover\n .md-version:hover & {\n animation: hoverfix 250ms forwards;\n }\n\n // Enable on focus\n .md-version:focus-within & {\n animation: none;\n }\n }\n }\n\n // Version selection item\n &__item {\n line-height: px2rem(36px);\n }\n\n // Version selection link\n &__link {\n display: block;\n width: 100%;\n padding-right: px2rem(24px);\n padding-left: px2rem(12px);\n white-space: nowrap;\n outline: none;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: px2rem(24px);\n }\n\n // Link on focus/hover\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Link on focus\n &:focus {\n background-color: var(--md-default-fg-color--lightest);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n/// Admonition flavours\n$admonitions: (\n note: pencil $clr-blue-a200,\n abstract summary tldr: clipboard-text $clr-light-blue-a400,\n info todo: information $clr-cyan-a700,\n tip hint important: fire $clr-teal-a700,\n success check done: check-bold $clr-green-a700,\n question help faq: help-circle $clr-light-green-a700,\n warning caution attention: alert $clr-orange-a400,\n failure fail missing: close-thick $clr-red-a200,\n danger error: lightning-bolt $clr-red-a400,\n bug: bug $clr-pink-a400,\n example: format-list-numbered $clr-deep-purple-a200,\n quote cite: format-quote-close $clr-grey\n) !default;\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n @each $names, $props in $admonitions {\n --md-admonition-icon--#{nth($names, 1)}:\n svg-load(\"material/#{nth($props, 1)}.svg\");\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Admonition\n .admonition {\n margin: px2em(20px, 12.8px) 0;\n padding: 0 px2rem(12px);\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: px2rem(12.8px);\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: px2rem(4px) solid $clr-blue-a200;\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.05);\n\n // [print]: Omit shadow as it may lead to rendering errors\n @media print {\n box-shadow: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust vertical spacing for nested admonitions\n .admonition {\n margin-top: 1em;\n margin-bottom: 1em;\n }\n\n // Adjust spacing for contained table wrappers\n .md-typeset__scrollwrap {\n margin: 1em px2rem(-12px);\n }\n\n // Adjust spacing for contained tables\n .md-typeset__table {\n padding: 0 px2rem(12px);\n }\n\n // Adjust spacing for single-child tabbed block container\n > .tabbed-set:only-child {\n margin-top: 0;\n }\n\n // Adjust spacing on last child\n html & > :last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Admonition title\n .admonition-title {\n position: relative;\n margin: 0 px2rem(-12px) 0 px2rem(-16px);\n padding: px2rem(8px) px2rem(12px) px2rem(8px) px2rem(40px);\n font-weight: 700;\n background-color: transparentize($clr-blue-a200, 0.9);\n border-left: px2rem(4px) solid $clr-blue-a200;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin: 0 px2rem(-16px) 0 px2rem(-12px);\n padding: px2rem(8px) px2rem(40px) px2rem(8px) px2rem(12px);\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Adjust spacing for title-only admonitions\n html &:last-child {\n margin-bottom: 0;\n }\n\n // Admonition icon\n &::before {\n position: absolute;\n left: px2rem(12px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: $clr-blue-a200;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(12px);\n left: initial;\n }\n }\n\n // Adjust spacing on last tabbed block container child - if the tabbed\n // block container is the sole child, it looks better to omit the margin\n + .tabbed-set:last-child {\n margin-top: 0;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: flavours\n// ----------------------------------------------------------------------------\n\n@each $names, $props in $admonitions {\n $name: nth($names, 1);\n $tint: nth($props, 2);\n\n // Admonition flavour\n .md-typeset .admonition.#{$name} {\n border-color: $tint;\n }\n\n // Admonition flavour title\n .md-typeset .#{$name} > .admonition-title {\n background-color: transparentize($tint, 0.9);\n border-color: $tint;\n\n // Admonition icon\n &::before {\n background-color: $tint;\n mask-image: var(--md-admonition-icon--#{$name});\n mask-repeat: no-repeat;\n mask-size: contain;\n }\n }\n\n // Define synonyms for flavours\n @if length($names) > 1 {\n @for $n from 2 through length($names) {\n .#{nth($names, $n)} {\n @extend .#{$name};\n }\n }\n }\n}\n","// ==========================================================================\n//\n// Name: UI Color Palette\n// Description: The color palette of material design.\n// Version: 2.3.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-color\n//\n// twitter: @mrmlnc\n//\n// ==========================================================================\n\n\n//\n// List of base colors\n//\n\n// $clr-red\n// $clr-pink\n// $clr-purple\n// $clr-deep-purple\n// $clr-indigo\n// $clr-blue\n// $clr-light-blue\n// $clr-cyan\n// $clr-teal\n// $clr-green\n// $clr-light-green\n// $clr-lime\n// $clr-yellow\n// $clr-amber\n// $clr-orange\n// $clr-deep-orange\n// $clr-brown\n// $clr-grey\n// $clr-blue-grey\n// $clr-black\n// $clr-white\n\n\n//\n// Red\n//\n\n$clr-red-list: (\n \"base\": #f44336,\n \"50\": #ffebee,\n \"100\": #ffcdd2,\n \"200\": #ef9a9a,\n \"300\": #e57373,\n \"400\": #ef5350,\n \"500\": #f44336,\n \"600\": #e53935,\n \"700\": #d32f2f,\n \"800\": #c62828,\n \"900\": #b71c1c,\n \"a100\": #ff8a80,\n \"a200\": #ff5252,\n \"a400\": #ff1744,\n \"a700\": #d50000\n);\n\n$clr-red: map-get($clr-red-list, \"base\");\n\n$clr-red-50: map-get($clr-red-list, \"50\");\n$clr-red-100: map-get($clr-red-list, \"100\");\n$clr-red-200: map-get($clr-red-list, \"200\");\n$clr-red-300: map-get($clr-red-list, \"300\");\n$clr-red-400: map-get($clr-red-list, \"400\");\n$clr-red-500: map-get($clr-red-list, \"500\");\n$clr-red-600: map-get($clr-red-list, \"600\");\n$clr-red-700: map-get($clr-red-list, \"700\");\n$clr-red-800: map-get($clr-red-list, \"800\");\n$clr-red-900: map-get($clr-red-list, \"900\");\n$clr-red-a100: map-get($clr-red-list, \"a100\");\n$clr-red-a200: map-get($clr-red-list, \"a200\");\n$clr-red-a400: map-get($clr-red-list, \"a400\");\n$clr-red-a700: map-get($clr-red-list, \"a700\");\n\n\n//\n// Pink\n//\n\n$clr-pink-list: (\n \"base\": #e91e63,\n \"50\": #fce4ec,\n \"100\": #f8bbd0,\n \"200\": #f48fb1,\n \"300\": #f06292,\n \"400\": #ec407a,\n \"500\": #e91e63,\n \"600\": #d81b60,\n \"700\": #c2185b,\n \"800\": #ad1457,\n \"900\": #880e4f,\n \"a100\": #ff80ab,\n \"a200\": #ff4081,\n \"a400\": #f50057,\n \"a700\": #c51162\n);\n\n$clr-pink: map-get($clr-pink-list, \"base\");\n\n$clr-pink-50: map-get($clr-pink-list, \"50\");\n$clr-pink-100: map-get($clr-pink-list, \"100\");\n$clr-pink-200: map-get($clr-pink-list, \"200\");\n$clr-pink-300: map-get($clr-pink-list, \"300\");\n$clr-pink-400: map-get($clr-pink-list, \"400\");\n$clr-pink-500: map-get($clr-pink-list, \"500\");\n$clr-pink-600: map-get($clr-pink-list, \"600\");\n$clr-pink-700: map-get($clr-pink-list, \"700\");\n$clr-pink-800: map-get($clr-pink-list, \"800\");\n$clr-pink-900: map-get($clr-pink-list, \"900\");\n$clr-pink-a100: map-get($clr-pink-list, \"a100\");\n$clr-pink-a200: map-get($clr-pink-list, \"a200\");\n$clr-pink-a400: map-get($clr-pink-list, \"a400\");\n$clr-pink-a700: map-get($clr-pink-list, \"a700\");\n\n\n//\n// Purple\n//\n\n$clr-purple-list: (\n \"base\": #9c27b0,\n \"50\": #f3e5f5,\n \"100\": #e1bee7,\n \"200\": #ce93d8,\n \"300\": #ba68c8,\n \"400\": #ab47bc,\n \"500\": #9c27b0,\n \"600\": #8e24aa,\n \"700\": #7b1fa2,\n \"800\": #6a1b9a,\n \"900\": #4a148c,\n \"a100\": #ea80fc,\n \"a200\": #e040fb,\n \"a400\": #d500f9,\n \"a700\": #aa00ff\n);\n\n$clr-purple: map-get($clr-purple-list, \"base\");\n\n$clr-purple-50: map-get($clr-purple-list, \"50\");\n$clr-purple-100: map-get($clr-purple-list, \"100\");\n$clr-purple-200: map-get($clr-purple-list, \"200\");\n$clr-purple-300: map-get($clr-purple-list, \"300\");\n$clr-purple-400: map-get($clr-purple-list, \"400\");\n$clr-purple-500: map-get($clr-purple-list, \"500\");\n$clr-purple-600: map-get($clr-purple-list, \"600\");\n$clr-purple-700: map-get($clr-purple-list, \"700\");\n$clr-purple-800: map-get($clr-purple-list, \"800\");\n$clr-purple-900: map-get($clr-purple-list, \"900\");\n$clr-purple-a100: map-get($clr-purple-list, \"a100\");\n$clr-purple-a200: map-get($clr-purple-list, \"a200\");\n$clr-purple-a400: map-get($clr-purple-list, \"a400\");\n$clr-purple-a700: map-get($clr-purple-list, \"a700\");\n\n\n//\n// Deep purple\n//\n\n$clr-deep-purple-list: (\n \"base\": #673ab7,\n \"50\": #ede7f6,\n \"100\": #d1c4e9,\n \"200\": #b39ddb,\n \"300\": #9575cd,\n \"400\": #7e57c2,\n \"500\": #673ab7,\n \"600\": #5e35b1,\n \"700\": #512da8,\n \"800\": #4527a0,\n \"900\": #311b92,\n \"a100\": #b388ff,\n \"a200\": #7c4dff,\n \"a400\": #651fff,\n \"a700\": #6200ea\n);\n\n$clr-deep-purple: map-get($clr-deep-purple-list, \"base\");\n\n$clr-deep-purple-50: map-get($clr-deep-purple-list, \"50\");\n$clr-deep-purple-100: map-get($clr-deep-purple-list, \"100\");\n$clr-deep-purple-200: map-get($clr-deep-purple-list, \"200\");\n$clr-deep-purple-300: map-get($clr-deep-purple-list, \"300\");\n$clr-deep-purple-400: map-get($clr-deep-purple-list, \"400\");\n$clr-deep-purple-500: map-get($clr-deep-purple-list, \"500\");\n$clr-deep-purple-600: map-get($clr-deep-purple-list, \"600\");\n$clr-deep-purple-700: map-get($clr-deep-purple-list, \"700\");\n$clr-deep-purple-800: map-get($clr-deep-purple-list, \"800\");\n$clr-deep-purple-900: map-get($clr-deep-purple-list, \"900\");\n$clr-deep-purple-a100: map-get($clr-deep-purple-list, \"a100\");\n$clr-deep-purple-a200: map-get($clr-deep-purple-list, \"a200\");\n$clr-deep-purple-a400: map-get($clr-deep-purple-list, \"a400\");\n$clr-deep-purple-a700: map-get($clr-deep-purple-list, \"a700\");\n\n\n//\n// Indigo\n//\n\n$clr-indigo-list: (\n \"base\": #3f51b5,\n \"50\": #e8eaf6,\n \"100\": #c5cae9,\n \"200\": #9fa8da,\n \"300\": #7986cb,\n \"400\": #5c6bc0,\n \"500\": #3f51b5,\n \"600\": #3949ab,\n \"700\": #303f9f,\n \"800\": #283593,\n \"900\": #1a237e,\n \"a100\": #8c9eff,\n \"a200\": #536dfe,\n \"a400\": #3d5afe,\n \"a700\": #304ffe\n);\n\n$clr-indigo: map-get($clr-indigo-list, \"base\");\n\n$clr-indigo-50: map-get($clr-indigo-list, \"50\");\n$clr-indigo-100: map-get($clr-indigo-list, \"100\");\n$clr-indigo-200: map-get($clr-indigo-list, \"200\");\n$clr-indigo-300: map-get($clr-indigo-list, \"300\");\n$clr-indigo-400: map-get($clr-indigo-list, \"400\");\n$clr-indigo-500: map-get($clr-indigo-list, \"500\");\n$clr-indigo-600: map-get($clr-indigo-list, \"600\");\n$clr-indigo-700: map-get($clr-indigo-list, \"700\");\n$clr-indigo-800: map-get($clr-indigo-list, \"800\");\n$clr-indigo-900: map-get($clr-indigo-list, \"900\");\n$clr-indigo-a100: map-get($clr-indigo-list, \"a100\");\n$clr-indigo-a200: map-get($clr-indigo-list, \"a200\");\n$clr-indigo-a400: map-get($clr-indigo-list, \"a400\");\n$clr-indigo-a700: map-get($clr-indigo-list, \"a700\");\n\n\n//\n// Blue\n//\n\n$clr-blue-list: (\n \"base\": #2196f3,\n \"50\": #e3f2fd,\n \"100\": #bbdefb,\n \"200\": #90caf9,\n \"300\": #64b5f6,\n \"400\": #42a5f5,\n \"500\": #2196f3,\n \"600\": #1e88e5,\n \"700\": #1976d2,\n \"800\": #1565c0,\n \"900\": #0d47a1,\n \"a100\": #82b1ff,\n \"a200\": #448aff,\n \"a400\": #2979ff,\n \"a700\": #2962ff\n);\n\n$clr-blue: map-get($clr-blue-list, \"base\");\n\n$clr-blue-50: map-get($clr-blue-list, \"50\");\n$clr-blue-100: map-get($clr-blue-list, \"100\");\n$clr-blue-200: map-get($clr-blue-list, \"200\");\n$clr-blue-300: map-get($clr-blue-list, \"300\");\n$clr-blue-400: map-get($clr-blue-list, \"400\");\n$clr-blue-500: map-get($clr-blue-list, \"500\");\n$clr-blue-600: map-get($clr-blue-list, \"600\");\n$clr-blue-700: map-get($clr-blue-list, \"700\");\n$clr-blue-800: map-get($clr-blue-list, \"800\");\n$clr-blue-900: map-get($clr-blue-list, \"900\");\n$clr-blue-a100: map-get($clr-blue-list, \"a100\");\n$clr-blue-a200: map-get($clr-blue-list, \"a200\");\n$clr-blue-a400: map-get($clr-blue-list, \"a400\");\n$clr-blue-a700: map-get($clr-blue-list, \"a700\");\n\n\n//\n// Light Blue\n//\n\n$clr-light-blue-list: (\n \"base\": #03a9f4,\n \"50\": #e1f5fe,\n \"100\": #b3e5fc,\n \"200\": #81d4fa,\n \"300\": #4fc3f7,\n \"400\": #29b6f6,\n \"500\": #03a9f4,\n \"600\": #039be5,\n \"700\": #0288d1,\n \"800\": #0277bd,\n \"900\": #01579b,\n \"a100\": #80d8ff,\n \"a200\": #40c4ff,\n \"a400\": #00b0ff,\n \"a700\": #0091ea\n);\n\n$clr-light-blue: map-get($clr-light-blue-list, \"base\");\n\n$clr-light-blue-50: map-get($clr-light-blue-list, \"50\");\n$clr-light-blue-100: map-get($clr-light-blue-list, \"100\");\n$clr-light-blue-200: map-get($clr-light-blue-list, \"200\");\n$clr-light-blue-300: map-get($clr-light-blue-list, \"300\");\n$clr-light-blue-400: map-get($clr-light-blue-list, \"400\");\n$clr-light-blue-500: map-get($clr-light-blue-list, \"500\");\n$clr-light-blue-600: map-get($clr-light-blue-list, \"600\");\n$clr-light-blue-700: map-get($clr-light-blue-list, \"700\");\n$clr-light-blue-800: map-get($clr-light-blue-list, \"800\");\n$clr-light-blue-900: map-get($clr-light-blue-list, \"900\");\n$clr-light-blue-a100: map-get($clr-light-blue-list, \"a100\");\n$clr-light-blue-a200: map-get($clr-light-blue-list, \"a200\");\n$clr-light-blue-a400: map-get($clr-light-blue-list, \"a400\");\n$clr-light-blue-a700: map-get($clr-light-blue-list, \"a700\");\n\n\n//\n// Cyan\n//\n\n$clr-cyan-list: (\n \"base\": #00bcd4,\n \"50\": #e0f7fa,\n \"100\": #b2ebf2,\n \"200\": #80deea,\n \"300\": #4dd0e1,\n \"400\": #26c6da,\n \"500\": #00bcd4,\n \"600\": #00acc1,\n \"700\": #0097a7,\n \"800\": #00838f,\n \"900\": #006064,\n \"a100\": #84ffff,\n \"a200\": #18ffff,\n \"a400\": #00e5ff,\n \"a700\": #00b8d4\n);\n\n$clr-cyan: map-get($clr-cyan-list, \"base\");\n\n$clr-cyan-50: map-get($clr-cyan-list, \"50\");\n$clr-cyan-100: map-get($clr-cyan-list, \"100\");\n$clr-cyan-200: map-get($clr-cyan-list, \"200\");\n$clr-cyan-300: map-get($clr-cyan-list, \"300\");\n$clr-cyan-400: map-get($clr-cyan-list, \"400\");\n$clr-cyan-500: map-get($clr-cyan-list, \"500\");\n$clr-cyan-600: map-get($clr-cyan-list, \"600\");\n$clr-cyan-700: map-get($clr-cyan-list, \"700\");\n$clr-cyan-800: map-get($clr-cyan-list, \"800\");\n$clr-cyan-900: map-get($clr-cyan-list, \"900\");\n$clr-cyan-a100: map-get($clr-cyan-list, \"a100\");\n$clr-cyan-a200: map-get($clr-cyan-list, \"a200\");\n$clr-cyan-a400: map-get($clr-cyan-list, \"a400\");\n$clr-cyan-a700: map-get($clr-cyan-list, \"a700\");\n\n\n//\n// Teal\n//\n\n$clr-teal-list: (\n \"base\": #009688,\n \"50\": #e0f2f1,\n \"100\": #b2dfdb,\n \"200\": #80cbc4,\n \"300\": #4db6ac,\n \"400\": #26a69a,\n \"500\": #009688,\n \"600\": #00897b,\n \"700\": #00796b,\n \"800\": #00695c,\n \"900\": #004d40,\n \"a100\": #a7ffeb,\n \"a200\": #64ffda,\n \"a400\": #1de9b6,\n \"a700\": #00bfa5\n);\n\n$clr-teal: map-get($clr-teal-list, \"base\");\n\n$clr-teal-50: map-get($clr-teal-list, \"50\");\n$clr-teal-100: map-get($clr-teal-list, \"100\");\n$clr-teal-200: map-get($clr-teal-list, \"200\");\n$clr-teal-300: map-get($clr-teal-list, \"300\");\n$clr-teal-400: map-get($clr-teal-list, \"400\");\n$clr-teal-500: map-get($clr-teal-list, \"500\");\n$clr-teal-600: map-get($clr-teal-list, \"600\");\n$clr-teal-700: map-get($clr-teal-list, \"700\");\n$clr-teal-800: map-get($clr-teal-list, \"800\");\n$clr-teal-900: map-get($clr-teal-list, \"900\");\n$clr-teal-a100: map-get($clr-teal-list, \"a100\");\n$clr-teal-a200: map-get($clr-teal-list, \"a200\");\n$clr-teal-a400: map-get($clr-teal-list, \"a400\");\n$clr-teal-a700: map-get($clr-teal-list, \"a700\");\n\n\n//\n// Green\n//\n\n$clr-green-list: (\n \"base\": #4caf50,\n \"50\": #e8f5e9,\n \"100\": #c8e6c9,\n \"200\": #a5d6a7,\n \"300\": #81c784,\n \"400\": #66bb6a,\n \"500\": #4caf50,\n \"600\": #43a047,\n \"700\": #388e3c,\n \"800\": #2e7d32,\n \"900\": #1b5e20,\n \"a100\": #b9f6ca,\n \"a200\": #69f0ae,\n \"a400\": #00e676,\n \"a700\": #00c853\n);\n\n$clr-green: map-get($clr-green-list, \"base\");\n\n$clr-green-50: map-get($clr-green-list, \"50\");\n$clr-green-100: map-get($clr-green-list, \"100\");\n$clr-green-200: map-get($clr-green-list, \"200\");\n$clr-green-300: map-get($clr-green-list, \"300\");\n$clr-green-400: map-get($clr-green-list, \"400\");\n$clr-green-500: map-get($clr-green-list, \"500\");\n$clr-green-600: map-get($clr-green-list, \"600\");\n$clr-green-700: map-get($clr-green-list, \"700\");\n$clr-green-800: map-get($clr-green-list, \"800\");\n$clr-green-900: map-get($clr-green-list, \"900\");\n$clr-green-a100: map-get($clr-green-list, \"a100\");\n$clr-green-a200: map-get($clr-green-list, \"a200\");\n$clr-green-a400: map-get($clr-green-list, \"a400\");\n$clr-green-a700: map-get($clr-green-list, \"a700\");\n\n\n//\n// Light green\n//\n\n$clr-light-green-list: (\n \"base\": #8bc34a,\n \"50\": #f1f8e9,\n \"100\": #dcedc8,\n \"200\": #c5e1a5,\n \"300\": #aed581,\n \"400\": #9ccc65,\n \"500\": #8bc34a,\n \"600\": #7cb342,\n \"700\": #689f38,\n \"800\": #558b2f,\n \"900\": #33691e,\n \"a100\": #ccff90,\n \"a200\": #b2ff59,\n \"a400\": #76ff03,\n \"a700\": #64dd17\n);\n\n$clr-light-green: map-get($clr-light-green-list, \"base\");\n\n$clr-light-green-50: map-get($clr-light-green-list, \"50\");\n$clr-light-green-100: map-get($clr-light-green-list, \"100\");\n$clr-light-green-200: map-get($clr-light-green-list, \"200\");\n$clr-light-green-300: map-get($clr-light-green-list, \"300\");\n$clr-light-green-400: map-get($clr-light-green-list, \"400\");\n$clr-light-green-500: map-get($clr-light-green-list, \"500\");\n$clr-light-green-600: map-get($clr-light-green-list, \"600\");\n$clr-light-green-700: map-get($clr-light-green-list, \"700\");\n$clr-light-green-800: map-get($clr-light-green-list, \"800\");\n$clr-light-green-900: map-get($clr-light-green-list, \"900\");\n$clr-light-green-a100: map-get($clr-light-green-list, \"a100\");\n$clr-light-green-a200: map-get($clr-light-green-list, \"a200\");\n$clr-light-green-a400: map-get($clr-light-green-list, \"a400\");\n$clr-light-green-a700: map-get($clr-light-green-list, \"a700\");\n\n\n//\n// Lime\n//\n\n$clr-lime-list: (\n \"base\": #cddc39,\n \"50\": #f9fbe7,\n \"100\": #f0f4c3,\n \"200\": #e6ee9c,\n \"300\": #dce775,\n \"400\": #d4e157,\n \"500\": #cddc39,\n \"600\": #c0ca33,\n \"700\": #afb42b,\n \"800\": #9e9d24,\n \"900\": #827717,\n \"a100\": #f4ff81,\n \"a200\": #eeff41,\n \"a400\": #c6ff00,\n \"a700\": #aeea00\n);\n\n$clr-lime: map-get($clr-lime-list, \"base\");\n\n$clr-lime-50: map-get($clr-lime-list, \"50\");\n$clr-lime-100: map-get($clr-lime-list, \"100\");\n$clr-lime-200: map-get($clr-lime-list, \"200\");\n$clr-lime-300: map-get($clr-lime-list, \"300\");\n$clr-lime-400: map-get($clr-lime-list, \"400\");\n$clr-lime-500: map-get($clr-lime-list, \"500\");\n$clr-lime-600: map-get($clr-lime-list, \"600\");\n$clr-lime-700: map-get($clr-lime-list, \"700\");\n$clr-lime-800: map-get($clr-lime-list, \"800\");\n$clr-lime-900: map-get($clr-lime-list, \"900\");\n$clr-lime-a100: map-get($clr-lime-list, \"a100\");\n$clr-lime-a200: map-get($clr-lime-list, \"a200\");\n$clr-lime-a400: map-get($clr-lime-list, \"a400\");\n$clr-lime-a700: map-get($clr-lime-list, \"a700\");\n\n\n//\n// Yellow\n//\n\n$clr-yellow-list: (\n \"base\": #ffeb3b,\n \"50\": #fffde7,\n \"100\": #fff9c4,\n \"200\": #fff59d,\n \"300\": #fff176,\n \"400\": #ffee58,\n \"500\": #ffeb3b,\n \"600\": #fdd835,\n \"700\": #fbc02d,\n \"800\": #f9a825,\n \"900\": #f57f17,\n \"a100\": #ffff8d,\n \"a200\": #ffff00,\n \"a400\": #ffea00,\n \"a700\": #ffd600\n);\n\n$clr-yellow: map-get($clr-yellow-list, \"base\");\n\n$clr-yellow-50: map-get($clr-yellow-list, \"50\");\n$clr-yellow-100: map-get($clr-yellow-list, \"100\");\n$clr-yellow-200: map-get($clr-yellow-list, \"200\");\n$clr-yellow-300: map-get($clr-yellow-list, \"300\");\n$clr-yellow-400: map-get($clr-yellow-list, \"400\");\n$clr-yellow-500: map-get($clr-yellow-list, \"500\");\n$clr-yellow-600: map-get($clr-yellow-list, \"600\");\n$clr-yellow-700: map-get($clr-yellow-list, \"700\");\n$clr-yellow-800: map-get($clr-yellow-list, \"800\");\n$clr-yellow-900: map-get($clr-yellow-list, \"900\");\n$clr-yellow-a100: map-get($clr-yellow-list, \"a100\");\n$clr-yellow-a200: map-get($clr-yellow-list, \"a200\");\n$clr-yellow-a400: map-get($clr-yellow-list, \"a400\");\n$clr-yellow-a700: map-get($clr-yellow-list, \"a700\");\n\n\n//\n// amber\n//\n\n$clr-amber-list: (\n \"base\": #ffc107,\n \"50\": #fff8e1,\n \"100\": #ffecb3,\n \"200\": #ffe082,\n \"300\": #ffd54f,\n \"400\": #ffca28,\n \"500\": #ffc107,\n \"600\": #ffb300,\n \"700\": #ffa000,\n \"800\": #ff8f00,\n \"900\": #ff6f00,\n \"a100\": #ffe57f,\n \"a200\": #ffd740,\n \"a400\": #ffc400,\n \"a700\": #ffab00\n);\n\n$clr-amber: map-get($clr-amber-list, \"base\");\n\n$clr-amber-50: map-get($clr-amber-list, \"50\");\n$clr-amber-100: map-get($clr-amber-list, \"100\");\n$clr-amber-200: map-get($clr-amber-list, \"200\");\n$clr-amber-300: map-get($clr-amber-list, \"300\");\n$clr-amber-400: map-get($clr-amber-list, \"400\");\n$clr-amber-500: map-get($clr-amber-list, \"500\");\n$clr-amber-600: map-get($clr-amber-list, \"600\");\n$clr-amber-700: map-get($clr-amber-list, \"700\");\n$clr-amber-800: map-get($clr-amber-list, \"800\");\n$clr-amber-900: map-get($clr-amber-list, \"900\");\n$clr-amber-a100: map-get($clr-amber-list, \"a100\");\n$clr-amber-a200: map-get($clr-amber-list, \"a200\");\n$clr-amber-a400: map-get($clr-amber-list, \"a400\");\n$clr-amber-a700: map-get($clr-amber-list, \"a700\");\n\n\n//\n// Orange\n//\n\n$clr-orange-list: (\n \"base\": #ff9800,\n \"50\": #fff3e0,\n \"100\": #ffe0b2,\n \"200\": #ffcc80,\n \"300\": #ffb74d,\n \"400\": #ffa726,\n \"500\": #ff9800,\n \"600\": #fb8c00,\n \"700\": #f57c00,\n \"800\": #ef6c00,\n \"900\": #e65100,\n \"a100\": #ffd180,\n \"a200\": #ffab40,\n \"a400\": #ff9100,\n \"a700\": #ff6d00\n);\n\n$clr-orange: map-get($clr-orange-list, \"base\");\n\n$clr-orange-50: map-get($clr-orange-list, \"50\");\n$clr-orange-100: map-get($clr-orange-list, \"100\");\n$clr-orange-200: map-get($clr-orange-list, \"200\");\n$clr-orange-300: map-get($clr-orange-list, \"300\");\n$clr-orange-400: map-get($clr-orange-list, \"400\");\n$clr-orange-500: map-get($clr-orange-list, \"500\");\n$clr-orange-600: map-get($clr-orange-list, \"600\");\n$clr-orange-700: map-get($clr-orange-list, \"700\");\n$clr-orange-800: map-get($clr-orange-list, \"800\");\n$clr-orange-900: map-get($clr-orange-list, \"900\");\n$clr-orange-a100: map-get($clr-orange-list, \"a100\");\n$clr-orange-a200: map-get($clr-orange-list, \"a200\");\n$clr-orange-a400: map-get($clr-orange-list, \"a400\");\n$clr-orange-a700: map-get($clr-orange-list, \"a700\");\n\n\n//\n// Deep orange\n//\n\n$clr-deep-orange-list: (\n \"base\": #ff5722,\n \"50\": #fbe9e7,\n \"100\": #ffccbc,\n \"200\": #ffab91,\n \"300\": #ff8a65,\n \"400\": #ff7043,\n \"500\": #ff5722,\n \"600\": #f4511e,\n \"700\": #e64a19,\n \"800\": #d84315,\n \"900\": #bf360c,\n \"a100\": #ff9e80,\n \"a200\": #ff6e40,\n \"a400\": #ff3d00,\n \"a700\": #dd2c00\n);\n\n$clr-deep-orange: map-get($clr-deep-orange-list, \"base\");\n\n$clr-deep-orange-50: map-get($clr-deep-orange-list, \"50\");\n$clr-deep-orange-100: map-get($clr-deep-orange-list, \"100\");\n$clr-deep-orange-200: map-get($clr-deep-orange-list, \"200\");\n$clr-deep-orange-300: map-get($clr-deep-orange-list, \"300\");\n$clr-deep-orange-400: map-get($clr-deep-orange-list, \"400\");\n$clr-deep-orange-500: map-get($clr-deep-orange-list, \"500\");\n$clr-deep-orange-600: map-get($clr-deep-orange-list, \"600\");\n$clr-deep-orange-700: map-get($clr-deep-orange-list, \"700\");\n$clr-deep-orange-800: map-get($clr-deep-orange-list, \"800\");\n$clr-deep-orange-900: map-get($clr-deep-orange-list, \"900\");\n$clr-deep-orange-a100: map-get($clr-deep-orange-list, \"a100\");\n$clr-deep-orange-a200: map-get($clr-deep-orange-list, \"a200\");\n$clr-deep-orange-a400: map-get($clr-deep-orange-list, \"a400\");\n$clr-deep-orange-a700: map-get($clr-deep-orange-list, \"a700\");\n\n\n//\n// Brown\n//\n\n$clr-brown-list: (\n \"base\": #795548,\n \"50\": #efebe9,\n \"100\": #d7ccc8,\n \"200\": #bcaaa4,\n \"300\": #a1887f,\n \"400\": #8d6e63,\n \"500\": #795548,\n \"600\": #6d4c41,\n \"700\": #5d4037,\n \"800\": #4e342e,\n \"900\": #3e2723,\n);\n\n$clr-brown: map-get($clr-brown-list, \"base\");\n\n$clr-brown-50: map-get($clr-brown-list, \"50\");\n$clr-brown-100: map-get($clr-brown-list, \"100\");\n$clr-brown-200: map-get($clr-brown-list, \"200\");\n$clr-brown-300: map-get($clr-brown-list, \"300\");\n$clr-brown-400: map-get($clr-brown-list, \"400\");\n$clr-brown-500: map-get($clr-brown-list, \"500\");\n$clr-brown-600: map-get($clr-brown-list, \"600\");\n$clr-brown-700: map-get($clr-brown-list, \"700\");\n$clr-brown-800: map-get($clr-brown-list, \"800\");\n$clr-brown-900: map-get($clr-brown-list, \"900\");\n\n\n//\n// Grey\n//\n\n$clr-grey-list: (\n \"base\": #9e9e9e,\n \"50\": #fafafa,\n \"100\": #f5f5f5,\n \"200\": #eeeeee,\n \"300\": #e0e0e0,\n \"400\": #bdbdbd,\n \"500\": #9e9e9e,\n \"600\": #757575,\n \"700\": #616161,\n \"800\": #424242,\n \"900\": #212121,\n);\n\n$clr-grey: map-get($clr-grey-list, \"base\");\n\n$clr-grey-50: map-get($clr-grey-list, \"50\");\n$clr-grey-100: map-get($clr-grey-list, \"100\");\n$clr-grey-200: map-get($clr-grey-list, \"200\");\n$clr-grey-300: map-get($clr-grey-list, \"300\");\n$clr-grey-400: map-get($clr-grey-list, \"400\");\n$clr-grey-500: map-get($clr-grey-list, \"500\");\n$clr-grey-600: map-get($clr-grey-list, \"600\");\n$clr-grey-700: map-get($clr-grey-list, \"700\");\n$clr-grey-800: map-get($clr-grey-list, \"800\");\n$clr-grey-900: map-get($clr-grey-list, \"900\");\n\n\n//\n// Blue grey\n//\n\n$clr-blue-grey-list: (\n \"base\": #607d8b,\n \"50\": #eceff1,\n \"100\": #cfd8dc,\n \"200\": #b0bec5,\n \"300\": #90a4ae,\n \"400\": #78909c,\n \"500\": #607d8b,\n \"600\": #546e7a,\n \"700\": #455a64,\n \"800\": #37474f,\n \"900\": #263238,\n);\n\n$clr-blue-grey: map-get($clr-blue-grey-list, \"base\");\n\n$clr-blue-grey-50: map-get($clr-blue-grey-list, \"50\");\n$clr-blue-grey-100: map-get($clr-blue-grey-list, \"100\");\n$clr-blue-grey-200: map-get($clr-blue-grey-list, \"200\");\n$clr-blue-grey-300: map-get($clr-blue-grey-list, \"300\");\n$clr-blue-grey-400: map-get($clr-blue-grey-list, \"400\");\n$clr-blue-grey-500: map-get($clr-blue-grey-list, \"500\");\n$clr-blue-grey-600: map-get($clr-blue-grey-list, \"600\");\n$clr-blue-grey-700: map-get($clr-blue-grey-list, \"700\");\n$clr-blue-grey-800: map-get($clr-blue-grey-list, \"800\");\n$clr-blue-grey-900: map-get($clr-blue-grey-list, \"900\");\n\n\n//\n// Black\n//\n\n$clr-black-list: (\n \"base\": #000\n);\n\n$clr-black: map-get($clr-black-list, \"base\");\n\n\n//\n// White\n//\n\n$clr-white-list: (\n \"base\": #fff\n);\n\n$clr-white: map-get($clr-white-list, \"base\");\n\n\n//\n// List for all Colors for looping\n//\n\n$clr-list-all: (\n \"red\": $clr-red-list,\n \"pink\": $clr-pink-list,\n \"purple\": $clr-purple-list,\n \"deep-purple\": $clr-deep-purple-list,\n \"indigo\": $clr-indigo-list,\n \"blue\": $clr-blue-list,\n \"light-blue\": $clr-light-blue-list,\n \"cyan\": $clr-cyan-list,\n \"teal\": $clr-teal-list,\n \"green\": $clr-green-list,\n \"light-green\": $clr-light-green-list,\n \"lime\": $clr-lime-list,\n \"yellow\": $clr-yellow-list,\n \"amber\": $clr-amber-list,\n \"orange\": $clr-orange-list,\n \"deep-orange\": $clr-deep-orange-list,\n \"brown\": $clr-brown-list,\n \"grey\": $clr-grey-list,\n \"blue-grey\": $clr-blue-grey-list,\n \"black\": $clr-black-list,\n \"white\": $clr-white-list\n);\n\n\n//\n// Typography\n//\n\n$clr-ui-display-4: $clr-grey-600;\n$clr-ui-display-3: $clr-grey-600;\n$clr-ui-display-2: $clr-grey-600;\n$clr-ui-display-1: $clr-grey-600;\n$clr-ui-headline: $clr-grey-900;\n$clr-ui-title: $clr-grey-900;\n$clr-ui-subhead-1: $clr-grey-900;\n$clr-ui-body-2: $clr-grey-900;\n$clr-ui-body-1: $clr-grey-900;\n$clr-ui-caption: $clr-grey-600;\n$clr-ui-menu: $clr-grey-900;\n$clr-ui-button: $clr-grey-900;\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-footnotes-icon: svg-load(\"material/keyboard-return.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Footnote container\n .footnote {\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n\n // Footnote list - omit left indentation\n > ol {\n margin-left: 0;\n\n // Footnote item - footnote items can contain lists, so we need to scope\n // the spacing adjustments to the top-level footnote item.\n > li {\n transition: color 125ms;\n\n // Darken color on target\n &:target {\n color: var(--md-default-fg-color);\n }\n\n // Show backreferences on footnote hover\n &:hover .footnote-backref,\n &:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0;\n }\n }\n }\n }\n\n // Footnote reference\n .footnote-ref {\n font-weight: 700;\n font-size: px2em(12px, 16px);\n\n // Hack: increase specificity to override default\n html & {\n outline-offset: px2rem(2px);\n }\n }\n\n // Footnote backreference\n .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n // Hack: omit Unicode arrow for replacement with icon\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(px2rem(5px));\n opacity: 0;\n transition:\n color 250ms,\n transform 250ms 250ms,\n opacity 125ms 250ms;\n\n // [print]: Show footnote backreferences\n @media print {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-5px));\n }\n\n // Adjust color on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Footnote backreference icon\n &::before {\n display: inline-block;\n width: px2rem(16px);\n height: px2rem(16px);\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n }\n }\n\n // Footnote reference wrapper\n [id^=\"fnref:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n\n // Show outline for all devices\n > .footnote-ref {\n outline: auto;\n }\n }\n\n // Footnote wrapper\n [id^=\"fn:\"]:target {\n scroll-margin-top: initial;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Headerlink\n .headerlink {\n display: inline-block;\n margin-left: px2rem(10px);\n color: var(--md-default-fg-color--lighter);\n opacity: 0;\n transition:\n color 250ms,\n opacity 125ms;\n\n // [print]: Hide headerlinks\n @media print {\n display: none;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(10px);\n margin-left: initial;\n }\n }\n\n // Show headerlinks on parent hover\n :hover > .headerlink,\n :target > .headerlink,\n .headerlink:focus {\n opacity: 1;\n transition:\n color 250ms,\n opacity 125ms;\n }\n\n // Adjust color on parent target or focus/hover\n :target > .headerlink,\n .headerlink:focus,\n .headerlink:hover {\n color: var(--md-accent-fg-color);\n }\n\n // Adjust scroll offset for all elements with `id` attributes - general scroll\n // margin offset for anything that can be targeted. Browser support is pretty\n // decent by now, but Edge <79 and Safari (iOS and macOS) still don't support\n // it properly, so we settle with a cross-browser anchor correction solution.\n :target {\n scroll-margin-top: px2rem(48px + 24px);\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: px2rem(96px + 24px);\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 1-3\n h1:target,\n h2:target,\n h3:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 4px);\n padding-top: px2rem(48px + 24px - 4px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px - 4px);\n padding-top: px2rem(96px + 24px - 4px);\n }\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 4\n h4:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px - 3px);\n padding-top: px2rem(48px + 24px - 3px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px - 3px);\n padding-top: px2rem(96px + 24px - 3px);\n }\n }\n }\n }\n\n // Adjust scroll offset for headlines of level 5-6\n h5:target,\n h6:target {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n display: block;\n margin-top: -1 * px2rem(48px + 24px);\n padding-top: px2rem(48px + 24px);\n content: \"\";\n }\n\n // [screen +]: Sticky navigation tabs\n @include break-from-device(screen) {\n\n // Adjust scroll offset for sticky navigation tabs\n .md-header--lifted ~ .md-container & {\n scroll-margin-top: initial;\n\n // Anchor correction hack\n &::before {\n margin-top: -1 * px2rem(96px + 24px);\n padding-top: px2rem(96px + 24px);\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Arithmatex container\n div.arithmatex {\n overflow: auto;\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n margin: 0 px2rem(-16px);\n }\n\n // Arithmatex content\n > * {\n width: min-content;\n // stylelint-disable-next-line declaration-no-important\n margin: 1em auto !important;\n padding: 0 px2rem(16px);\n touch-action: auto;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Deletion, addition or comment\n del.critic,\n ins.critic,\n .critic.comment {\n box-decoration-break: clone;\n }\n\n // Deletion\n del.critic {\n background-color: var(--md-typeset-del-color);\n }\n\n // Addition\n ins.critic {\n background-color: var(--md-typeset-ins-color);\n }\n\n // Comment\n .critic.comment {\n color: var(--md-code-hl-comment-color);\n\n // Comment opening mark\n &::before {\n content: \"/* \";\n }\n\n // Comment closing mark\n &::after {\n content: \" */\";\n }\n }\n\n // Critic block\n .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: px2rem(16px);\n padding-left: px2rem(16px);\n overflow: auto;\n box-shadow: none;\n\n // Adjust spacing on first child\n > :first-child {\n margin-top: 0.5em;\n }\n\n // Adjust spacing on last child\n > :last-child {\n margin-bottom: 0.5em;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-details-icon: svg-load(\"material/chevron-right.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Details\n details {\n @extend .admonition;\n\n display: flow-root;\n padding-top: 0;\n overflow: visible;\n\n // Details title icon - rotate icon on transition to open state\n &[open] > summary::after {\n transform: rotate(90deg);\n }\n\n // Adjust spacing for details in closed state\n &:not([open]) {\n padding-bottom: 0;\n box-shadow: none;\n\n // Hack: we cannot set `overflow: hidden` on the `details` element (which\n // is why we set it to `overflow: visible`, as the outline would not be\n // visible when focusing. Therefore, we must set the border radius on the\n // summary explicitly.\n > summary {\n border-radius: px2rem(2px);\n }\n }\n\n // Hack: omit margin collapse\n &::after {\n display: table;\n content: \"\";\n }\n }\n\n // Details title\n summary {\n @extend .admonition-title;\n\n display: block;\n min-height: px2rem(20px);\n padding: px2rem(8px) px2rem(36px) px2rem(8px) px2rem(40px);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px);\n }\n\n // Show outline for keyboard devices\n &.focus-visible {\n outline-color: var(--md-accent-fg-color);\n outline-offset: px2rem(4px);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Details marker\n &::after {\n position: absolute;\n top: px2rem(8px);\n right: px2rem(8px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(8px);\n transform: rotate(180deg);\n }\n }\n\n // Hide native details marker\n &::marker,\n &::-webkit-details-marker {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Emoji and icon container\n .emojione,\n .twemoji,\n .gemoji {\n display: inline-flex;\n height: px2em(18px);\n vertical-align: text-top;\n\n // Icon - inlined via mkdocs-material-extensions\n svg {\n width: px2em(18px);\n max-height: 100%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: syntax highlighting\n// ----------------------------------------------------------------------------\n\n// Code block\n.highlight {\n .o, // Operator\n .ow { // Operator, word\n color: var(--md-code-hl-operator-color);\n }\n\n .p { // Punctuation\n color: var(--md-code-hl-punctuation-color);\n }\n\n .cpf, // Comment, preprocessor file\n .l, // Literal\n .s, // Literal, string\n .sb, // Literal, string backticks\n .sc, // Literal, string char\n .s2, // Literal, string double\n .si, // Literal, string interpol\n .s1, // Literal, string single\n .ss { // Literal, string symbol\n color: var(--md-code-hl-string-color);\n }\n\n .cp, // Comment, pre-processor\n .se, // Literal, string escape\n .sh, // Literal, string heredoc\n .sr, // Literal, string regex\n .sx { // Literal, string other\n color: var(--md-code-hl-special-color);\n }\n\n .m, // Number\n .mb, // Number, binary\n .mf, // Number, float\n .mh, // Number, hex\n .mi, // Number, integer\n .il, // Number, integer long\n .mo { // Number, octal\n color: var(--md-code-hl-number-color);\n }\n\n .k, // Keyword,\n .kd, // Keyword, declaration\n .kn, // Keyword, namespace\n .kp, // Keyword, pseudo\n .kr, // Keyword, reserved\n .kt { // Keyword, type\n color: var(--md-code-hl-keyword-color);\n }\n\n .kc, // Keyword, constant\n .n { // Name\n color: var(--md-code-hl-name-color);\n }\n\n .no, // Name, constant\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n .nf, // Name, function\n .nn { // Name, namespace\n color: var(--md-code-hl-function-color);\n }\n\n .nd, // Name, decorator\n .ni, // Name, entity\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-keyword-color);\n }\n\n .c, // Comment\n .cm, // Comment, multiline\n .c1, // Comment, single\n .ch, // Comment, shebang\n .cs, // Comment, special\n .sd { // Literal, string doc\n color: var(--md-code-hl-comment-color);\n }\n\n .na, // Name, attribute\n .nv, // Variable,\n .vc, // Variable, class\n .vg, // Variable, global\n .vi { // Variable, instance\n color: var(--md-code-hl-variable-color);\n }\n\n .ge, // Generic, emph\n .gr, // Generic, error\n .gh, // Generic, heading\n .go, // Generic, output\n .gp, // Generic, prompt\n .gs, // Generic, strong\n .gu, // Generic, subheading\n .gt { // Generic, traceback\n color: var(--md-code-hl-generic-color);\n }\n\n .gd, // Diff, delete\n .gi { // Diff, insert\n margin: 0 px2em(-2px);\n padding: 0 px2em(2px);\n border-radius: px2rem(2px);\n }\n\n .gd { // Diff, delete\n background-color: var(--md-typeset-del-color);\n }\n\n .gi { // Diff, insert\n background-color: var(--md-typeset-ins-color);\n }\n\n // Highlighted line\n .hll {\n display: block;\n margin: 0 px2em(-16px, 13.6px);\n padding: 0 px2em(16px, 13.6px);\n background-color: var(--md-code-hl-color);\n }\n\n // Code block line numbers (inline)\n [data-linenos]::before {\n position: sticky;\n left: px2em(-16px, 13.6px);\n float: left;\n margin-right: px2em(16px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n content: attr(data-linenos);\n user-select: none;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Code block with line numbers\n.highlighttable {\n display: flow-root;\n overflow: hidden;\n\n // Set table elements to block layout, because otherwise the whole flexbox\n // hacking won't work correctly\n tbody,\n td {\n display: block;\n padding: 0;\n }\n\n // We need to use flexbox layout, because otherwise it's not possible to\n // make the code container scroll while keeping the line numbers static\n tr {\n display: flex;\n }\n\n // The pre tags are nested inside a table, so we need to omit the margin\n // because it collapses below all the overflows\n pre {\n margin: 0;\n }\n\n // Code block line numbers - disable user selection, so code can be easily\n // copied without accidentally also copying the line numbers\n .linenos {\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n padding-right: 0;\n font-size: px2em(13.6px);\n background-color: var(--md-code-bg-color);\n user-select: none;\n }\n\n // Code block line numbers container\n .linenodiv {\n padding-right: px2em(8px, 13.6px);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lightest) inset;\n\n // Adjust colors and alignment\n pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n }\n }\n\n // Code block container - stretch to remaining space\n .code {\n flex: 1;\n overflow: hidden;\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Code block with line numbers\n .highlighttable {\n margin: 1em 0;\n direction: ltr;\n border-radius: px2rem(2px);\n\n // Omit rounded borders on contained code block\n code {\n border-radius: 0;\n }\n }\n\n // [mobile -]: Align with body copy\n @include break-to-device(mobile) {\n\n // Top-level code block\n > .highlight {\n margin: 1em px2rem(-16px);\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n\n // Omit rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // Top-level code block with line numbers\n > .highlighttable {\n margin: 1em px2rem(-16px);\n border-radius: 0;\n\n // Highlighted line\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tabbed block content\n .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest);\n\n // [print]: Show all tabs (even hidden ones) when printing\n @media print {\n display: block;\n order: initial;\n }\n\n // Code block is the only child of a tab - remove margin and mirror\n // previous (now deprecated) SuperFences code block grouping behavior\n > pre:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Omit rounded borders\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Adjust spacing for nested tab\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tabbed block container\n .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: px2rem(2px);\n\n // Tab radio button - the Tabbed extension will generate radio buttons with\n // labels, so tabs can be triggered without the necessity for JavaScript.\n // This is pretty cool, as it has great accessibility out-of-the box, so\n // we just hide the radio button and toggle the label color for indication.\n > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Tab label for checked radio button\n &:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n\n // Show tabbed block content\n + .tabbed-content {\n display: block;\n }\n }\n\n // Tab label on focus\n &:focus + label {\n outline-style: auto;\n outline-color: var(--md-accent-fg-color);\n }\n\n // Hide outline for pointer devices\n &:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n border-bottom: px2rem(2px) solid transparent;\n cursor: pointer;\n transition: color 250ms;\n\n // Tab label on hover\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-tasklist-icon:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n --md-tasklist-icon--checked:\n svg-load(\"octicons/check-circle-fill-24.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tasklist item\n .task-list-item {\n position: relative;\n list-style-type: none;\n\n // Make checkbox items align with normal list items, but position\n // everything in ems for correct layout at smaller font sizes\n [type=\"checkbox\"] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: -2em;\n left: initial;\n }\n }\n }\n\n // Hide native checkbox, when custom classes are enabled\n .task-list-control [type=\"checkbox\"] {\n z-index: -1;\n opacity: 0;\n }\n\n // Tasklist indicator in unchecked state\n .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: px2em(-24px);\n width: px2em(20px);\n height: px2em(20px);\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2em(-24px);\n left: initial;\n }\n }\n\n // Tasklist indicator in checked state\n [type=\"checkbox\"]:checked + .task-list-indicator::before {\n background-color: $clr-green-a400;\n mask-image: var(--md-tasklist-icon--checked);\n }\n}\n","////\n/// Copyright (c) 2016-2021 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // [tablet +]: Allow for rendering content as sidebars\n @include break-from-device(tablet) {\n\n // Modifier to float block elements\n .inline {\n float: left;\n width: px2rem(234px);\n margin-top: 0;\n margin-right: px2rem(16px);\n margin-bottom: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n }\n\n // Modifier to move to end (ltr: right, rtl: left)\n &.end {\n float: right;\n margin-right: 0;\n margin-left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(16px);\n margin-left: 0;\n }\n }\n }\n }\n}\n"]} \ No newline at end of file diff --git a/b_n_r_accidental_deletion/index.html b/b_n_r_accidental_deletion/index.html index 54ea09c3c..6daaedada 100644 --- a/b_n_r_accidental_deletion/index.html +++ b/b_n_r_accidental_deletion/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2110,6 +2110,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2175,20 +2189,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2375,10 +2375,10 @@ docker-compose exec dovecot-mailcow doveadm quota recalc -u restoreme@example.ne
    - + - + diff --git a/b_n_r_backup/index.html b/b_n_r_backup/index.html index 1cce3af06..ef33e4eca 100644 --- a/b_n_r_backup/index.html +++ b/b_n_r_backup/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2130,6 +2130,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2195,20 +2209,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2462,10 +2462,10 @@ In case of a corrupted database you'd need to use the helper script to restore t
    - + - + diff --git a/b_n_r_restore/index.html b/b_n_r_restore/index.html index f62a3d954..c4a37f6ce 100644 --- a/b_n_r_restore/index.html +++ b/b_n_r_restore/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2103,6 +2103,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2168,20 +2182,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2347,10 +2347,10 @@
    - + - + diff --git a/client/client-android/index.html b/client/client-android/index.html index e8101ff46..2e81f228b 100644 --- a/client/client-android/index.html +++ b/client/client-android/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2282,10 +2282,10 @@
    - + - + diff --git a/client/client-apple/index.html b/client/client-apple/index.html index 5aacfc5bc..25b29c3cb 100644 --- a/client/client-apple/index.html +++ b/client/client-apple/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2122,6 +2122,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2187,20 +2201,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2404,10 +2404,10 @@
    - + - + diff --git a/client/client-emclient/index.html b/client/client-emclient/index.html index d3c693c63..f911f3450 100644 --- a/client/client-emclient/index.html +++ b/client/client-emclient/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2284,10 +2284,10 @@
    - + - + diff --git a/client/client-kontact/index.html b/client/client-kontact/index.html index e28bfe8db..4ff0be935 100644 --- a/client/client-kontact/index.html +++ b/client/client-kontact/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2292,10 +2292,10 @@
    - + - + diff --git a/client/client-manual/index.html b/client/client-manual/index.html index a5cf4f58f..a0dcfca3f 100644 --- a/client/client-manual/index.html +++ b/client/client-manual/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2108,6 +2108,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2173,20 +2187,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2410,10 +2410,10 @@
    - + - + diff --git a/client/client-outlook/index.html b/client/client-outlook/index.html index c68723205..0ab1e263c 100644 --- a/client/client-outlook/index.html +++ b/client/client-outlook/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2129,6 +2129,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2194,20 +2208,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2436,10 +2436,10 @@
    - + - + diff --git a/client/client-thunderbird/index.html b/client/client-thunderbird/index.html index 3218c0889..1d41e2313 100644 --- a/client/client-thunderbird/index.html +++ b/client/client-thunderbird/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2101,6 +2101,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2166,20 +2180,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2422,10 +2422,10 @@
    - + - + diff --git a/client/client-windows/index.html b/client/client-windows/index.html index b508bad4a..2681565b0 100644 --- a/client/client-windows/index.html +++ b/client/client-windows/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2285,10 +2285,10 @@
    - + - + diff --git a/client/client-windowsphone/index.html b/client/client-windowsphone/index.html index 58c0b6720..5bc48b01b 100644 --- a/client/client-windowsphone/index.html +++ b/client/client-windowsphone/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2281,10 +2281,10 @@
    - + - + diff --git a/client/index.html b/client/index.html index 899a4d8d2..d24b61576 100644 --- a/client/index.html +++ b/client/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2300,10 +2300,10 @@ Since you accessed this page after logging into your mailcow server, all of the
    - + - + diff --git a/debug-admin_login_sogo/index.html b/debug-admin_login_sogo/index.html index 71f04a358..5cbb34fb9 100644 --- a/debug-admin_login_sogo/index.html +++ b/debug-admin_login_sogo/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2115,6 +2115,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2180,20 +2194,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2402,10 +2402,10 @@ In most cases, this should not be noticeable but should be kept in mind if you f
    - + - + diff --git a/debug-asan_rspamd/index.html b/debug-asan_rspamd/index.html index e46047b7b..2f59f1cde 100644 --- a/debug-asan_rspamd/index.html +++ b/debug-asan_rspamd/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2295,10 +2295,10 @@ export ASAN_OPTIONS=new_delete_type_mismatch=0:detect_leaks=1:detect_odr_violati
    - + - + diff --git a/debug-attach_service/index.html b/debug-attach_service/index.html index 8feac9bbf..c046ca415 100644 --- a/debug-attach_service/index.html +++ b/debug-attach_service/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2141,6 +2141,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2206,20 +2220,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2507,10 +2507,10 @@ docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
    - + - + diff --git a/debug-common_problems/index.html b/debug-common_problems/index.html index 3c7c07873..7c6429fe4 100644 --- a/debug-common_problems/index.html +++ b/debug-common_problems/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2150,6 +2150,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2215,20 +2229,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2488,10 +2488,10 @@ Docker and iptables-based firewalls sometimes create conflicting rules, so disab
    - + - + diff --git a/debug-logs/index.html b/debug-logs/index.html index 4cc2d6d68..bd500827a 100644 --- a/debug-logs/index.html +++ b/debug-logs/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2291,10 +2291,10 @@
    - + - + diff --git a/debug-mysql_upgrade/index.html b/debug-mysql_upgrade/index.html index c75f8a110..fd0d44560 100644 --- a/debug-mysql_upgrade/index.html +++ b/debug-mysql_upgrade/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2101,6 +2101,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2166,20 +2180,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2347,10 +2347,10 @@ exit
    - + - + diff --git a/debug-reset_pw/index.html b/debug-reset_pw/index.html index 8f0280148..a61bf0b91 100644 --- a/debug-reset_pw/index.html +++ b/debug-reset_pw/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2175,6 +2175,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2240,20 +2254,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2552,10 +2552,10 @@ docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e &qu
    - + - + diff --git a/debug-reset_tls/index.html b/debug-reset_tls/index.html index 0013ef039..2f4ce9224 100644 --- a/debug-reset_tls/index.html +++ b/debug-reset_tls/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2287,10 +2287,10 @@ docker-compose up -d
    - + - + diff --git a/debug-rm_volumes/index.html b/debug-rm_volumes/index.html index 73353586d..12f21ea44 100644 --- a/debug-rm_volumes/index.html +++ b/debug-rm_volumes/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2288,10 +2288,10 @@
    - + - + diff --git a/debug/index.html b/debug/index.html index 44acce1b6..120d4e713 100644 --- a/debug/index.html +++ b/debug/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2285,10 +2285,10 @@
    - + - + diff --git a/firststeps-disable_ipv6/index.html b/firststeps-disable_ipv6/index.html index 03566418b..15688153c 100644 --- a/firststeps-disable_ipv6/index.html +++ b/firststeps-disable_ipv6/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2326,10 +2326,10 @@ inet_protocols = ipv4
    - + - + diff --git a/firststeps-dmarc_reporting/index.html b/firststeps-dmarc_reporting/index.html index 116fee724..86587eb82 100644 --- a/firststeps-dmarc_reporting/index.html +++ b/firststeps-dmarc_reporting/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2129,6 +2129,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2194,20 +2208,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2507,10 +2507,10 @@ docker-compose exec redis-mailcow redis-cli HGETALL "dmarc;example.com;2021
    - + - + diff --git a/firststeps-ip_bindings/index.html b/firststeps-ip_bindings/index.html index 2fc7ef8bc..dcfe3330f 100644 --- a/firststeps-ip_bindings/index.html +++ b/firststeps-ip_bindings/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2108,6 +2108,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2173,20 +2187,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2413,10 +2413,10 @@ services:
    - + - + diff --git a/firststeps-local_mta/index.html b/firststeps-local_mta/index.html index 32dd6721d..fa1f25954 100644 --- a/firststeps-local_mta/index.html +++ b/firststeps-local_mta/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2290,10 +2290,10 @@ Relaying over this interface is necessary (instead of - for example - relaying d
    - + - + diff --git a/firststeps-logging/index.html b/firststeps-logging/index.html index 8ad1e5a14..b5aaa84a2 100644 --- a/firststeps-logging/index.html +++ b/firststeps-logging/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2128,6 +2128,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2193,20 +2207,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2473,10 +2473,10 @@ local3.* /var/log/mailcow.logs
    - + - + diff --git a/firststeps-rp/index.html b/firststeps-rp/index.html index 554f07566..9a1eb5d9c 100644 --- a/firststeps-rp/index.html +++ b/firststeps-rp/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2136,6 +2136,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2201,20 +2215,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2631,10 +2631,10 @@ docker restart ${postfix_c} ${dovecot_c} ${nginx_c}
    - + - + diff --git a/firststeps-rspamd_ui/index.html b/firststeps-rspamd_ui/index.html index 6a7f5bc80..ef369a069 100644 --- a/firststeps-rspamd_ui/index.html +++ b/firststeps-rspamd_ui/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2283,10 +2283,10 @@
    - + - + diff --git a/firststeps-snat/index.html b/firststeps-snat/index.html index ae516bfeb..3d6174cd8 100644 --- a/firststeps-snat/index.html +++ b/firststeps-snat/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2287,10 +2287,10 @@ SNAT6_TO_SOURCE=dead:beef
    - + - + diff --git a/firststeps-ssl/index.html b/firststeps-ssl/index.html index c5a9c5f6a..de2d450da 100644 --- a/firststeps-ssl/index.html +++ b/firststeps-ssl/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2190,6 +2190,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2255,20 +2269,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2626,10 +2626,10 @@ bash helper-scripts/expiry-dates.sh
    - + - + diff --git a/firststeps-sync_jobs_migration/index.html b/firststeps-sync_jobs_migration/index.html index e36897d3f..d555d3f97 100644 --- a/firststeps-sync_jobs_migration/index.html +++ b/firststeps-sync_jobs_migration/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2101,6 +2101,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2166,20 +2180,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2371,10 +2371,10 @@
    - + - + diff --git a/i_u_m_deinstall/index.html b/i_u_m_deinstall/index.html index 7a788a74e..2bc790960 100644 --- a/i_u_m_deinstall/index.html +++ b/i_u_m_deinstall/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2287,10 +2287,10 @@
    - + - + diff --git a/i_u_m_install/index.html b/i_u_m_install/index.html index e5b3c2c9d..951ba941d 100644 --- a/i_u_m_install/index.html +++ b/i_u_m_install/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2362,10 +2362,10 @@ docker-compose up -d
    - + - + diff --git a/i_u_m_migration/index.html b/i_u_m_migration/index.html index 0763f9824..b1c608e82 100644 --- a/i_u_m_migration/index.html +++ b/i_u_m_migration/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2327,10 +2327,10 @@ docker-compose pull
    - + - + diff --git a/i_u_m_update/index.html b/i_u_m_update/index.html index 9eb86a4c8..479c0d65f 100644 --- a/i_u_m_update/index.html +++ b/i_u_m_update/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2142,6 +2142,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2207,20 +2221,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2475,10 +2475,10 @@ docker-compose up -d
    - + - + diff --git a/index.html b/index.html index 6ceb21e57..5688c7cfe 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2144,6 +2144,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2209,20 +2223,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2495,10 +2495,10 @@ Each container represents a single application.

    - + - + diff --git a/model-acl/index.html b/model-acl/index.html index c2e02d57d..93440f004 100644 --- a/model-acl/index.html +++ b/model-acl/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2294,10 +2294,10 @@
    - + - + diff --git a/model-passwd/index.html b/model-passwd/index.html index 77a3eb2dc..fb274e5a6 100644 --- a/model-passwd/index.html +++ b/model-passwd/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2108,6 +2108,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2173,20 +2187,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2396,10 +2396,10 @@ With SOGo disabled, all hashing methods below will be able to be read by mailcow
    - + - + diff --git a/model-sender_rcv/index.html b/model-sender_rcv/index.html index 78679e554..1a9115a82 100644 --- a/model-sender_rcv/index.html +++ b/model-sender_rcv/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2101,6 +2101,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2166,20 +2180,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2374,10 +2374,10 @@ needs to grant you access as described above.

    - + - + diff --git a/prerequisite-dns/index.html b/prerequisite-dns/index.html index bc94db93b..5a8d8c03a 100644 --- a/prerequisite-dns/index.html +++ b/prerequisite-dns/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2170,6 +2170,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2235,20 +2249,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2581,10 +2581,10 @@ Details:
    - + - + diff --git a/prerequisite-system/index.html b/prerequisite-system/index.html index dac940fa5..b68c00836 100644 --- a/prerequisite-system/index.html +++ b/prerequisite-system/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2162,6 +2162,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2227,20 +2241,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2671,10 +2671,10 @@ You may want to
    - + - + diff --git a/restrictions_ip_accss/index.html b/restrictions_ip_accss/index.html index 5c9998571..3c7370b88 100644 --- a/restrictions_ip_accss/index.html +++ b/restrictions_ip_accss/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2063,6 +2063,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2128,20 +2142,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2280,10 +2280,10 @@
    - + - + diff --git a/search/search_index.json b/search/search_index.json index 93d1e1a53..5fcd2c6a9 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"\ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95 \u00b6 Help mailcow \u00b6 Please consider a support contract for a small monthly fee at Servercow EN / Servercow DE to support further development. We support you while you support us . :) If you are super awesome and would like to support without a contract, you can get a SAL license that confirms your awesomeness (a flexible one-time payment) at Servercow EN / Servercow DE . Get support \u00b6 There are two ways to achieve support for your mailcow installation. Commercial support \u00b6 For professional and prioritized commercial support you can sign a basic support subscription at Servercow EN / Servercow DE . For custom inquiries or questions please contact us at info@servercow.de instead. Furthermore we do also provide a fully featured and managed mailcow here . This way we take care about the technical magic underneath and you can enjoy your whole mail experience in a hassle-free way. Community support and chat \u00b6 The other alternative is our free community-support on our various channels below. Please notice, that this support is driven by our awesome community around mailcow. This kind of support is best-effort, voluntary and there is no guarantee for anything. Our mailcow community @ community.mailcow.email Telegram @ t.me/mailcow . Telegram @ t.me/mailcowOfftopic . Telegram desktop clients are available for multiple platforms . You can search the groups history for keywords. For bug tracking, feature requests and code contributions only: GitHub @ mailcow/mailcow-dockerized Demo \u00b6 You can find a demo at demo.mailcow.email , use the following credentials to login: Administrator : admin / moohoo Domain administrator : department / moohoo Mailbox : demo@440044.xyz / moohoo Overview \u00b6 The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: DKIM and ARC support Black- and whitelists per domain and per user Spam score management per-user (reject spam, mark spam, greylist) Allow mailbox users to create temporary spam aliases Prepend mail tags to subject or move mail to sub folder (per-user) Allow mailbox users to toggle incoming and outgoing TLS enforcement Allow users to reset SOGo ActiveSync device caches imapsync to migrate or pull remote mailboxes regularly TFA: Yubikey OTP and U2F USB (Google Chrome and derivatives only), TOTP Add domains, mailboxes, aliases, domain aliases and SOGo resources Add whitelisted hosts to forward mail to mailcow Fail2ban-like integration Quarantine system Antivirus scanning incl. macro scanning in office documents Integrated basic monitoring A lot more... mailcow: dockerized comes with multiple containers linked in one bridged network. Each container represents a single application. ACME ClamAV (optional) Dovecot MariaDB Memcached Netfilter (Fail2ban-like integration by @mkuron ) Nginx Oletools via Olefy PHP Postfix Redis Rspamd SOGo Solr (optional) Unbound A Watchdog to provide basic monitoring Docker volumes to keep dynamic data - take care of them! crypt-vol-1 mysql-socket-vol-1 mysql-vol-1 postfix-vol-1 redis-vol-1 rspamd-vol-1 sogo-userdata-backup-vol-1 sogo-web-vol-1 solr-vol-1 vmail-index-vol-1 vmail-vol-1","title":"Information & Support"},{"location":"#_1","text":"","title":"\ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95"},{"location":"#help-mailcow","text":"Please consider a support contract for a small monthly fee at Servercow EN / Servercow DE to support further development. We support you while you support us . :) If you are super awesome and would like to support without a contract, you can get a SAL license that confirms your awesomeness (a flexible one-time payment) at Servercow EN / Servercow DE .","title":"Help mailcow"},{"location":"#get-support","text":"There are two ways to achieve support for your mailcow installation.","title":"Get support"},{"location":"#commercial-support","text":"For professional and prioritized commercial support you can sign a basic support subscription at Servercow EN / Servercow DE . For custom inquiries or questions please contact us at info@servercow.de instead. Furthermore we do also provide a fully featured and managed mailcow here . This way we take care about the technical magic underneath and you can enjoy your whole mail experience in a hassle-free way.","title":"Commercial support"},{"location":"#community-support-and-chat","text":"The other alternative is our free community-support on our various channels below. Please notice, that this support is driven by our awesome community around mailcow. This kind of support is best-effort, voluntary and there is no guarantee for anything. Our mailcow community @ community.mailcow.email Telegram @ t.me/mailcow . Telegram @ t.me/mailcowOfftopic . Telegram desktop clients are available for multiple platforms . You can search the groups history for keywords. For bug tracking, feature requests and code contributions only: GitHub @ mailcow/mailcow-dockerized","title":"Community support and chat"},{"location":"#demo","text":"You can find a demo at demo.mailcow.email , use the following credentials to login: Administrator : admin / moohoo Domain administrator : department / moohoo Mailbox : demo@440044.xyz / moohoo","title":"Demo"},{"location":"#overview","text":"The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: DKIM and ARC support Black- and whitelists per domain and per user Spam score management per-user (reject spam, mark spam, greylist) Allow mailbox users to create temporary spam aliases Prepend mail tags to subject or move mail to sub folder (per-user) Allow mailbox users to toggle incoming and outgoing TLS enforcement Allow users to reset SOGo ActiveSync device caches imapsync to migrate or pull remote mailboxes regularly TFA: Yubikey OTP and U2F USB (Google Chrome and derivatives only), TOTP Add domains, mailboxes, aliases, domain aliases and SOGo resources Add whitelisted hosts to forward mail to mailcow Fail2ban-like integration Quarantine system Antivirus scanning incl. macro scanning in office documents Integrated basic monitoring A lot more... mailcow: dockerized comes with multiple containers linked in one bridged network. Each container represents a single application. ACME ClamAV (optional) Dovecot MariaDB Memcached Netfilter (Fail2ban-like integration by @mkuron ) Nginx Oletools via Olefy PHP Postfix Redis Rspamd SOGo Solr (optional) Unbound A Watchdog to provide basic monitoring Docker volumes to keep dynamic data - take care of them! crypt-vol-1 mysql-socket-vol-1 mysql-vol-1 postfix-vol-1 redis-vol-1 rspamd-vol-1 sogo-userdata-backup-vol-1 sogo-web-vol-1 solr-vol-1 vmail-index-vol-1 vmail-vol-1","title":"Overview"},{"location":"b_n_r_accidental_deletion/","text":"So you deleted a mailbox and have no backups, he? If you noticed your mistake within a few hours, you can probably recover the users data. SOGo \u00b6 We automatically create daily backups (24h interval starting from running up -d) in /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/ . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the file named after the user you want to restore to __MAILCOW_DIRECTORY__/data/conf/sogo . 1. Copy the backup: cp /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/restoreme@example.org __MAILCOW_DIRECTORY__/data/conf/sogo 2. Run docker-compose exec -u sogo sogo-mailcow sogo-tool restore -F ALL /etc/sogo restoreme@example.org Run sogo-tool without parameters to check for possible restore options. 3. Delete the copied backup by running rm __MAILCOW_DIRECTORY__/data/conf/sogo 4. Restart SOGo and Memcached: docker-compose restart sogo-mailcow memcached-mailcow Mail \u00b6 In case of an accidental deletion of a mailbox, you will be able to recover for (by default) 5 days. This depends on the MAILDIR_GC_TIME parameter in mailcow.conf . A deleted mailbox is copied in its encrypted form to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage . The folder inside _garbage follows the structure [timestamp]_[domain_sanitized][user_sanitized] , for example `1629109708_exampleorgtest in case of test@example.org deleted on 1629109708. To restore make sure you are actually restoring to the same mailcow it was deleted from or you use the same encryption keys in crypt-vol-1 . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the folders from /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage/[timestamp]_[domain_sanitized][user_sanitized] back to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/[domain]/[user] and resync the folder and recalc the quota: docker-compose exec dovecot-mailcow doveadm force-resync -u restoreme@example.net '*' docker-compose exec dovecot-mailcow doveadm quota recalc -u restoreme@example.net","title":"Recover accidentally deleted data"},{"location":"b_n_r_accidental_deletion/#sogo","text":"We automatically create daily backups (24h interval starting from running up -d) in /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/ . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the file named after the user you want to restore to __MAILCOW_DIRECTORY__/data/conf/sogo . 1. Copy the backup: cp /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/restoreme@example.org __MAILCOW_DIRECTORY__/data/conf/sogo 2. Run docker-compose exec -u sogo sogo-mailcow sogo-tool restore -F ALL /etc/sogo restoreme@example.org Run sogo-tool without parameters to check for possible restore options. 3. Delete the copied backup by running rm __MAILCOW_DIRECTORY__/data/conf/sogo 4. Restart SOGo and Memcached: docker-compose restart sogo-mailcow memcached-mailcow","title":"SOGo"},{"location":"b_n_r_accidental_deletion/#mail","text":"In case of an accidental deletion of a mailbox, you will be able to recover for (by default) 5 days. This depends on the MAILDIR_GC_TIME parameter in mailcow.conf . A deleted mailbox is copied in its encrypted form to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage . The folder inside _garbage follows the structure [timestamp]_[domain_sanitized][user_sanitized] , for example `1629109708_exampleorgtest in case of test@example.org deleted on 1629109708. To restore make sure you are actually restoring to the same mailcow it was deleted from or you use the same encryption keys in crypt-vol-1 . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the folders from /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage/[timestamp]_[domain_sanitized][user_sanitized] back to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/[domain]/[user] and resync the folder and recalc the quota: docker-compose exec dovecot-mailcow doveadm force-resync -u restoreme@example.net '*' docker-compose exec dovecot-mailcow doveadm quota recalc -u restoreme@example.net","title":"Mail"},{"location":"b_n_r_backup/","text":"Backup \u00b6 Manual \u00b6 You can use the provided script helper-scripts/backup_and_restore.sh to backup mailcow automatically. Please do not copy this script to another location. To run a backup, write \"backup\" as first parameter and either one or more components to backup as following parameters. You can also use \"all\" as second parameter to backup all components. Append --delete-days n to delete backups older than n days. # Syntax: # ./helper-scripts/backup_and_restore.sh backup (vmail|crypt|redis|rspamd|postfix|mysql|all|--delete-days) # Backup all, delete backups older than 3 days ./helper-scripts/backup_and_restore.sh backup all --delete-days 3 # Backup vmail, crypt and mysql data, delete backups older than 30 days ./helper-scripts/backup_and_restore.sh backup vmail crypt mysql --delete-days 30 # Backup vmail ./helper-scripts/backup_and_restore.sh backup vmail The script will ask you for a backup location. Inside of this location it will create folders in the format \"mailcow_DATE\". You should not rename those folders to not break the restore process. To run a backup unattended, define MAILCOW_BACKUP_LOCATION as environment variable before starting the script: MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all Cronjob \u00b6 You can run the backup script regularly via cronjob. Make sure BACKUP_LOCATION exists: 5 4 * * * cd /opt/mailcow-dockerized/; MAILCOW_BACKUP_LOCATION=/mnt/mailcow_backups /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 Per default cron sends the full result of each backup operation by email. If you want cron to only mail on error (non-zero exit code) you may want to use the following snippet. Pathes need to be modified according to your setup (this script is a user contribution). This following script may be placed in /etc/cron.daily/mailcow-backup - do not forget to mark it as executable via chmod +x : #!/bin/sh # Backup mailcow data # https://mailcow.github.io/mailcow-dockerized-docs/b_n_r_backup/ set -e OUT=\"$(mktemp)\" export MAILCOW_BACKUP_LOCATION=\"/opt/backup\" SCRIPT=\"/opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh\" PARAMETERS=\"backup all\" OPTIONS=\"--delete-days 30\" # run command set +e \"${SCRIPT}\" ${PARAMETERS} ${OPTIONS} 2>&1 > \"$OUT\" RESULT=$? if [ $RESULT -ne 0 ] then echo \"${SCRIPT} ${PARAMETERS} ${OPTIONS} encounters an error:\" echo \"RESULT=$RESULT\" echo \"STDOUT / STDERR:\" cat \"$OUT\" fi Backup strategy with rsync and mailcow backup script \u00b6 Create the destination directory for mailcows helper script: mkdir -p /external_share/backups/backup_script Create cronjobs: 25 1 * * * rsync -aH --delete /opt/mailcow-dockerized /external_share/backups/mailcow-dockerized 40 2 * * * rsync -aH --delete /var/lib/docker/volumes /external_share/backups/var_lib_docker_volumes 5 4 * * * cd /opt/mailcow-dockerized/; BACKUP_LOCATION=/external_share/backups/backup_script /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 # If you want to, use the acl util to backup permissions of some/all folders/files: getfacl -Rn /path On the destination (in this case /external_share/backups ) you may want to have snapshot capabilities (ZFS, Btrfs etc.). Snapshot daily and keep for n days for a consistent backup. Do not rsync to a Samba share, you need to keep the correct permissions! To restore you'd simply need to run rsync the other way round and restart Docker to re-read the volumes. Run docker-compose pull and docker-compose up -d . If you are lucky Redis and MariaDB can automatically fix the inconsistent databases (if they are inconsistent). In case of a corrupted database you'd need to use the helper script to restore the inconsistent elements. If a restore fails, try to extract the backups and copy the files back manually. Keep the file permissions!","title":"Backup"},{"location":"b_n_r_backup/#backup","text":"","title":"Backup"},{"location":"b_n_r_backup/#manual","text":"You can use the provided script helper-scripts/backup_and_restore.sh to backup mailcow automatically. Please do not copy this script to another location. To run a backup, write \"backup\" as first parameter and either one or more components to backup as following parameters. You can also use \"all\" as second parameter to backup all components. Append --delete-days n to delete backups older than n days. # Syntax: # ./helper-scripts/backup_and_restore.sh backup (vmail|crypt|redis|rspamd|postfix|mysql|all|--delete-days) # Backup all, delete backups older than 3 days ./helper-scripts/backup_and_restore.sh backup all --delete-days 3 # Backup vmail, crypt and mysql data, delete backups older than 30 days ./helper-scripts/backup_and_restore.sh backup vmail crypt mysql --delete-days 30 # Backup vmail ./helper-scripts/backup_and_restore.sh backup vmail The script will ask you for a backup location. Inside of this location it will create folders in the format \"mailcow_DATE\". You should not rename those folders to not break the restore process. To run a backup unattended, define MAILCOW_BACKUP_LOCATION as environment variable before starting the script: MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all","title":"Manual"},{"location":"b_n_r_backup/#cronjob","text":"You can run the backup script regularly via cronjob. Make sure BACKUP_LOCATION exists: 5 4 * * * cd /opt/mailcow-dockerized/; MAILCOW_BACKUP_LOCATION=/mnt/mailcow_backups /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 Per default cron sends the full result of each backup operation by email. If you want cron to only mail on error (non-zero exit code) you may want to use the following snippet. Pathes need to be modified according to your setup (this script is a user contribution). This following script may be placed in /etc/cron.daily/mailcow-backup - do not forget to mark it as executable via chmod +x : #!/bin/sh # Backup mailcow data # https://mailcow.github.io/mailcow-dockerized-docs/b_n_r_backup/ set -e OUT=\"$(mktemp)\" export MAILCOW_BACKUP_LOCATION=\"/opt/backup\" SCRIPT=\"/opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh\" PARAMETERS=\"backup all\" OPTIONS=\"--delete-days 30\" # run command set +e \"${SCRIPT}\" ${PARAMETERS} ${OPTIONS} 2>&1 > \"$OUT\" RESULT=$? if [ $RESULT -ne 0 ] then echo \"${SCRIPT} ${PARAMETERS} ${OPTIONS} encounters an error:\" echo \"RESULT=$RESULT\" echo \"STDOUT / STDERR:\" cat \"$OUT\" fi","title":"Cronjob"},{"location":"b_n_r_backup/#backup-strategy-with-rsync-and-mailcow-backup-script","text":"Create the destination directory for mailcows helper script: mkdir -p /external_share/backups/backup_script Create cronjobs: 25 1 * * * rsync -aH --delete /opt/mailcow-dockerized /external_share/backups/mailcow-dockerized 40 2 * * * rsync -aH --delete /var/lib/docker/volumes /external_share/backups/var_lib_docker_volumes 5 4 * * * cd /opt/mailcow-dockerized/; BACKUP_LOCATION=/external_share/backups/backup_script /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 # If you want to, use the acl util to backup permissions of some/all folders/files: getfacl -Rn /path On the destination (in this case /external_share/backups ) you may want to have snapshot capabilities (ZFS, Btrfs etc.). Snapshot daily and keep for n days for a consistent backup. Do not rsync to a Samba share, you need to keep the correct permissions! To restore you'd simply need to run rsync the other way round and restart Docker to re-read the volumes. Run docker-compose pull and docker-compose up -d . If you are lucky Redis and MariaDB can automatically fix the inconsistent databases (if they are inconsistent). In case of a corrupted database you'd need to use the helper script to restore the inconsistent elements. If a restore fails, try to extract the backups and copy the files back manually. Keep the file permissions!","title":"Backup strategy with rsync and mailcow backup script"},{"location":"b_n_r_restore/","text":"Restore \u00b6 Please do not copy this script to another location. To run a restore, start mailcow , use the script with \"restore\" as first parameter. # Syntax: # ./helper-scripts/backup_and_restore.sh restore The script will ask you for a backup location containing the mailcow_DATE folders.","title":"Restore"},{"location":"b_n_r_restore/#restore","text":"Please do not copy this script to another location. To run a restore, start mailcow , use the script with \"restore\" as first parameter. # Syntax: # ./helper-scripts/backup_and_restore.sh restore The script will ask you for a backup location containing the mailcow_DATE folders.","title":"Restore"},{"location":"client/","text":"mailcow supports a variety of email clients, both on desktop computers and on smartphones. Below, you can find a number of configuration guides that explain how to configure your mailcow account. Tip If you access this page by logging into your mailcow server and clicking the \"Show configuration guides for email clients and smartphones\" link, all of the guides will be personalized with your email address and server name. Success Since you accessed this page after logging into your mailcow server, all of the guides have been personalized with your email address and server name. Android Apple iOS / macOS eM Client KDE Kontact / KMail Microsoft Outlook Mozilla Thunderbird Windows Mail Windows Phone Manual configuration","title":"Overview"},{"location":"debug-admin_login_sogo/","text":"This is an experimental feature that allows admins and domain admins to directly log into SOGo as a mailbox user, without knowing the users password. For this, an additional link to SOGo is displayed in the mailbox list. Multiple concurrent admin-logins to different mailboxes are also possible when using this feature. Enabling the feature \u00b6 The feature is disabled by default. It can be enabled in the mailcow.conf by setting: ALLOW_ADMIN_EMAIL_LOGIN=y and recreating the affected containers with docker-compose up -d Drawbacks when enabled \u00b6 Each SOGo page-load and each Active-Sync request will cause an additional execution of an internal PHP script. This might impact load-times of SOGo / EAS. In most cases, this should not be noticeable but should be kept in mind if you face any performance issues. SOGo will not display a logout link for admin-logins, to login normally one has to logout from the mailcow UI so the PHP session is destroyed. Technical details \u00b6 SOGoTrustProxyAuthentication option is set to YES which makes SOGo trust the x-webobjects-remote-user header. Dovecot will receive a random master-password which is valid for all mailboxes when used by the SOGo container. Clicking on the SOGo button in the mailbox list will open sogo-auth.php which checks permissions, sets session variables and redirects to the SOGo mailbox. Each SOGo, CardDAV, CalDAV and EAS http request will cause an additional, nginx internal auth_request call to sogo-auth.php with the following behavior: If a basic_auth header is present, the script will validate the credentials in place of SOGo and provide the following headers: x-webobjects-remote-user , Authorization and x-webobjects-auth-type . If no basic_auth header is present, the script will check for an active mailcow admin session for the requested email user and provide the same headers but with the dovecot master password used in the Authorization header. If both fails the headers will be set empty, which makes SOGo use its standard authentication methods. All of these options / behaviors are disabled if the ALLOW_ADMIN_EMAIL_LOGIN is not enabled in the config.","title":"Admin login to SOGo"},{"location":"debug-admin_login_sogo/#enabling-the-feature","text":"The feature is disabled by default. It can be enabled in the mailcow.conf by setting: ALLOW_ADMIN_EMAIL_LOGIN=y and recreating the affected containers with docker-compose up -d","title":"Enabling the feature"},{"location":"debug-admin_login_sogo/#drawbacks-when-enabled","text":"Each SOGo page-load and each Active-Sync request will cause an additional execution of an internal PHP script. This might impact load-times of SOGo / EAS. In most cases, this should not be noticeable but should be kept in mind if you face any performance issues. SOGo will not display a logout link for admin-logins, to login normally one has to logout from the mailcow UI so the PHP session is destroyed.","title":"Drawbacks when enabled"},{"location":"debug-admin_login_sogo/#technical-details","text":"SOGoTrustProxyAuthentication option is set to YES which makes SOGo trust the x-webobjects-remote-user header. Dovecot will receive a random master-password which is valid for all mailboxes when used by the SOGo container. Clicking on the SOGo button in the mailbox list will open sogo-auth.php which checks permissions, sets session variables and redirects to the SOGo mailbox. Each SOGo, CardDAV, CalDAV and EAS http request will cause an additional, nginx internal auth_request call to sogo-auth.php with the following behavior: If a basic_auth header is present, the script will validate the credentials in place of SOGo and provide the following headers: x-webobjects-remote-user , Authorization and x-webobjects-auth-type . If no basic_auth header is present, the script will check for an active mailcow admin session for the requested email user and provide the same headers but with the dovecot master password used in the Authorization header. If both fails the headers will be set empty, which makes SOGo use its standard authentication methods. All of these options / behaviors are disabled if the ALLOW_ADMIN_EMAIL_LOGIN is not enabled in the config.","title":"Technical details"},{"location":"debug-asan_rspamd/","text":"A quick guide to deeply analyze a malfunctioning Rspamd. docker-compose exec rspamd-mailcow bash if ! grep -qi 'apt-stable-asan' /etc/apt/sources.list.d/rspamd.list; then sed -i 's/apt-stable/apt-stable-asan/i' /etc/apt/sources.list.d/rspamd.list fi apt-get update ; apt-get upgrade rspamd nano /docker-entrypoint.sh # Before \"exec \"$@\"\" add the following lines: export G_SLICE=always-malloc export ASAN_OPTIONS=new_delete_type_mismatch=0:detect_leaks=1:detect_odr_violation=0:log_path=/tmp/rspamd-asan:quarantine_size_mb=2048:malloc_context_size=8:fast_unwind_on_malloc=0 Restart Rspamd: docker-compose restart rspamd-mailcow Your memory consumption will increase by a lot, it will also steadily grow, which is not related to a possible memory leak you are looking for. Leave the container running for a few minutes, hours or days (it should match the time you usually wait for the leak to \"happen\") and restart it: docker-compose restart rspamd-mailcow . Now enter the container by running docker-compose exec rspamd-mailcow bash , change the directory to /tmp and copy the asan Files to your desired location or upload them via termbin.com ( cat /tmp/rspamd-asan.* | nc termbin.com 9999 ).","title":"Advanced: Find memory leaks in Rspamd"},{"location":"debug-attach_service/","text":"Attaching a Container to your Shell \u00b6 To attach a container to your shell you can simply run docker-compose exec $Service_Name /bin/bash Connecting to Services \u00b6 If you want to connect to a service / application directly it is always a good idea to source mailcow.conf to get all relevant variables into your environment. MySQL \u00b6 source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} Redis \u00b6 docker-compose exec redis-mailcow redis-cli Service Descriptions \u00b6 Here is a brief overview of what container / service does what: Service Name Service Descriptions unbound-mailcow Local (DNSSEC) DNS Resolver mysql-mailcow Stores SOGo's and most of mailcow's settings postfix-mailcow Receives and sends mails dovecot-mailcow User logins and sieve filter redis-mailcow Storage back-end for DKIM keys and Rspamd rspamd-mailcow Mail filtering system. Used for av handling, dkim signing, spam handling clamd-mailcow Scans attachments for viruses olefy-mailcow Scans attached office documents for macro-viruses solr-mailcow Provides full-text search in Dovecot sogo-mailcow Webmail client that handles Microsoft ActiveSync and Cal- / CardDav nginx-mailcow Nginx remote proxy that handles all mailcow related HTTP / HTTPS requests acme-mailcow Automates HTTPS (SSL/TLS) certificate deployment memcached-mailcow Internal caching system for mailcow services watchdog-mailcow Allows the monitoring of docker containers / services php-fpm-mailcow Powers the mailcow web UI netfilter-mailcow Fail2Ban like integration","title":"Attach a Container"},{"location":"debug-attach_service/#attaching-a-container-to-your-shell","text":"To attach a container to your shell you can simply run docker-compose exec $Service_Name /bin/bash","title":"Attaching a Container to your Shell"},{"location":"debug-attach_service/#connecting-to-services","text":"If you want to connect to a service / application directly it is always a good idea to source mailcow.conf to get all relevant variables into your environment.","title":"Connecting to Services"},{"location":"debug-attach_service/#mysql","text":"source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}","title":"MySQL"},{"location":"debug-attach_service/#redis","text":"docker-compose exec redis-mailcow redis-cli","title":"Redis"},{"location":"debug-attach_service/#service-descriptions","text":"Here is a brief overview of what container / service does what: Service Name Service Descriptions unbound-mailcow Local (DNSSEC) DNS Resolver mysql-mailcow Stores SOGo's and most of mailcow's settings postfix-mailcow Receives and sends mails dovecot-mailcow User logins and sieve filter redis-mailcow Storage back-end for DKIM keys and Rspamd rspamd-mailcow Mail filtering system. Used for av handling, dkim signing, spam handling clamd-mailcow Scans attachments for viruses olefy-mailcow Scans attached office documents for macro-viruses solr-mailcow Provides full-text search in Dovecot sogo-mailcow Webmail client that handles Microsoft ActiveSync and Cal- / CardDav nginx-mailcow Nginx remote proxy that handles all mailcow related HTTP / HTTPS requests acme-mailcow Automates HTTPS (SSL/TLS) certificate deployment memcached-mailcow Internal caching system for mailcow services watchdog-mailcow Allows the monitoring of docker containers / services php-fpm-mailcow Powers the mailcow web UI netfilter-mailcow Fail2Ban like integration","title":"Service Descriptions"},{"location":"debug-common_problems/","text":"Here we list common problems and possible solutions: Mail loops back to myself \u00b6 Please check in your mailcow UI if you made the domain a backup MX : I can receive but not send mails \u00b6 There are a lot of things that could prevent you from sending mail: Check if your IP address is on any blacklists. You could use dnsbl.info or any other similar service to check for your IP address. There are some consumer ISP routers out there, that block mail ports for non whitelisted domains. Please check if you can reach your server on the ports 465 or 587 : # telnet 74.125.133.27 465 Trying 74.125.133.27... Connected to 74.125.133.27. Escape character is '^]'. My mails are identified as Spam \u00b6 Please read our guide on DNS configuration . docker-compose throws weird errors \u00b6 ... like: ERROR: Invalid interpolation format ... AttributeError: 'NoneType' object has no attribute 'keys' . ERROR: In file './docker-compose.yml' service 'version' doesn't have any configuration options . When you encounter one or similar messages while trying to run mailcow: dockerized please check if you have the latest version of Docker and docker-compose Container XY is unhealthy \u00b6 This error tries to tell you that one of the (health) conditions for a certain container are not met. Therefore it can't be started. This can have several reasons, the most common one is an updated git clone but old docker image or vice versa. A wrong configured firewall could also cause such a failure. The containers need to be able to talk to each other over the network 172.22.1.1/24. It might also be wrongly linked file (i.e. SSL certificate) that prevents a crucial container (nginx) from starting, so always check your logs to get an idea where your problem is coming from. Address already in use \u00b6 If you get an error message like: ERROR: for postfix-mailcow Cannot start service postfix-mailcow: driver failed programming external connectivity on endpoint mailcowdockerized_postfix-mailcow_1: Error starting userland proxy: listen tcp 0.0.0.0:25: bind: address already in use while trying to start / install mailcow: dockerized, make sure you've followed our section on the prerequisites . XYZ can't connect to ... \u00b6 Please check your local firewall! Docker and iptables-based firewalls sometimes create conflicting rules, so disable the firewall on your host to determine whether your connection issues are caused by such conflicts. If they are, you need to manually create appropriate rules in your host firewall to permit the necessary connections. If you experience connection problems from home, please check your ISP router's firewall too, some of them block mail traffic on the SMTP (587) or SMTPS (465) ports. It could also be, that your ISP is blocking the ports for SUBMISSION (25). While Linux users can chose from a variety of tools 1 to check if a port is open, the Windows user has only the PowerShell command Test-NetConnection -ComputerName host -Port port available by default. To enable telnet on a Windows after Vista please check this guide or enter the following command in an terminal with administrator privileges : dism /online /Enable-Feature /FeatureName:TelnetClient Inotify instance limit for user 5000 (UID vmail) exceeded ( see #453 ) \u00b6 Docker containers use the Docker hosts inotify limits. Setting them on your Docker host will pass them to the container. netcat , nmap , openssl , telnet , etc. \u21a9","title":"Common Problems"},{"location":"debug-common_problems/#mail-loops-back-to-myself","text":"Please check in your mailcow UI if you made the domain a backup MX :","title":"Mail loops back to myself"},{"location":"debug-common_problems/#i-can-receive-but-not-send-mails","text":"There are a lot of things that could prevent you from sending mail: Check if your IP address is on any blacklists. You could use dnsbl.info or any other similar service to check for your IP address. There are some consumer ISP routers out there, that block mail ports for non whitelisted domains. Please check if you can reach your server on the ports 465 or 587 : # telnet 74.125.133.27 465 Trying 74.125.133.27... Connected to 74.125.133.27. Escape character is '^]'.","title":"I can receive but not send mails"},{"location":"debug-common_problems/#my-mails-are-identified-as-spam","text":"Please read our guide on DNS configuration .","title":"My mails are identified as Spam"},{"location":"debug-common_problems/#docker-compose-throws-weird-errors","text":"... like: ERROR: Invalid interpolation format ... AttributeError: 'NoneType' object has no attribute 'keys' . ERROR: In file './docker-compose.yml' service 'version' doesn't have any configuration options . When you encounter one or similar messages while trying to run mailcow: dockerized please check if you have the latest version of Docker and docker-compose","title":"docker-compose throws weird errors"},{"location":"debug-common_problems/#container-xy-is-unhealthy","text":"This error tries to tell you that one of the (health) conditions for a certain container are not met. Therefore it can't be started. This can have several reasons, the most common one is an updated git clone but old docker image or vice versa. A wrong configured firewall could also cause such a failure. The containers need to be able to talk to each other over the network 172.22.1.1/24. It might also be wrongly linked file (i.e. SSL certificate) that prevents a crucial container (nginx) from starting, so always check your logs to get an idea where your problem is coming from.","title":"Container XY is unhealthy"},{"location":"debug-common_problems/#address-already-in-use","text":"If you get an error message like: ERROR: for postfix-mailcow Cannot start service postfix-mailcow: driver failed programming external connectivity on endpoint mailcowdockerized_postfix-mailcow_1: Error starting userland proxy: listen tcp 0.0.0.0:25: bind: address already in use while trying to start / install mailcow: dockerized, make sure you've followed our section on the prerequisites .","title":"Address already in use"},{"location":"debug-common_problems/#xyz-cant-connect-to","text":"Please check your local firewall! Docker and iptables-based firewalls sometimes create conflicting rules, so disable the firewall on your host to determine whether your connection issues are caused by such conflicts. If they are, you need to manually create appropriate rules in your host firewall to permit the necessary connections. If you experience connection problems from home, please check your ISP router's firewall too, some of them block mail traffic on the SMTP (587) or SMTPS (465) ports. It could also be, that your ISP is blocking the ports for SUBMISSION (25). While Linux users can chose from a variety of tools 1 to check if a port is open, the Windows user has only the PowerShell command Test-NetConnection -ComputerName host -Port port available by default. To enable telnet on a Windows after Vista please check this guide or enter the following command in an terminal with administrator privileges : dism /online /Enable-Feature /FeatureName:TelnetClient","title":"XYZ can't connect to ..."},{"location":"debug-common_problems/#inotify-instance-limit-for-user-5000-uid-vmail-exceeded-see-453","text":"Docker containers use the Docker hosts inotify limits. Setting them on your Docker host will pass them to the container. netcat , nmap , openssl , telnet , etc. \u21a9","title":"Inotify instance limit for user 5000 (UID vmail) exceeded (see #453)"},{"location":"debug-logs/","text":"Warning This section only applies for Dockers default logging driver (JSON). To view the logs of all mailcow: dockerized related containers, you can use docker-compose logs inside your mailcow-dockerized folder that contains your mailcow.conf . This is usually a bit much, but you could trim the output with --tail=100 to the last 100 lines per container, or add a -f to follow the live output of all your services. To view the logs of a specific service you can use docker-compose logs [options] $service_name Info The available options for the command docker-compose logs are: --no-color : Produce monochrome output. -f : Follow the log output. -t : Show timestamps. --tail=\"all\" : Number of lines to show from the end of the logs for each container.","title":"Logs"},{"location":"debug-mysql_upgrade/","text":"Run a manual mysql_upgrade \u00b6 This step is usually not necessary. docker-compose stop mysql-mailcow watchdog-mailcow docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && bash && exit 0\"' mysql-mailcow As soon as the SQL shell spawned, run mysql_upgrade and exit the container: mysql_upgrade exit","title":"Manual MySQL upgrade"},{"location":"debug-mysql_upgrade/#run-a-manual-mysql_upgrade","text":"This step is usually not necessary. docker-compose stop mysql-mailcow watchdog-mailcow docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && bash && exit 0\"' mysql-mailcow As soon as the SQL shell spawned, run mysql_upgrade and exit the container: mysql_upgrade exit","title":"Run a manual mysql_upgrade"},{"location":"debug-reset_pw/","text":"mailcow Admin Account \u00b6 Resets the mailcow admin account to a random password. Older mailcow: dockerized installations may find the mailcow-reset-admin.sh script in their mailcow root directory (mailcow_path). cd mailcow_path ./helper-scripts/mailcow-reset-admin.sh Reset MySQL Passwords \u00b6 Stop the stack by running docker-compose stop . When the containers came to a stop, run this command: docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && mysql -hlocalhost -uroot && exit 0\"' mysql-mailcow 1. Find database name \u00b6 # source mailcow.conf # docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mailcow_database | <===== | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec) 2. Reset one or more users \u00b6 2.1 Maria DB < 10.4 (older mailcow installations) \u00b6 Both \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both. MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('gotr00t'), password = PASSWORD('gotr00t') WHERE User = 'root'; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('mookuh'), password = PASSWORD('mookuh') WHERE User = 'mailcow' AND Host = '%'; MariaDB [(none)]> FLUSH PRIVILEGES; 2.2 Maria DB >= 10.4 (current mailcows) \u00b6 MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> ALTER USER 'mailcow'@'%' IDENTIFIED BY 'mookuh'; MariaDB [(none)]> ALTER USER 'root'@'%' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> FLUSH PRIVILEGES; Remove Two-Factor Authentication \u00b6 For mailcow WebUI: \u00b6 This works similar to resetting a MySQL password, now we do it from the host without connecting to the MySQL CLI: source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e \"DELETE FROM tfa WHERE username='YOUR_USERNAME';\" For SOGo: \u00b6 docker-compose exec -u sogo sogo-mailcow sogo-tool user-preferences set defaults user@example.com SOGoGoogleAuthenticatorEnabled '{\"SOGoGoogleAuthenticatorEnabled\":0}'","title":"Reset Passwords (incl. SQL)"},{"location":"debug-reset_pw/#mailcow-admin-account","text":"Resets the mailcow admin account to a random password. Older mailcow: dockerized installations may find the mailcow-reset-admin.sh script in their mailcow root directory (mailcow_path). cd mailcow_path ./helper-scripts/mailcow-reset-admin.sh","title":"mailcow Admin Account"},{"location":"debug-reset_pw/#reset-mysql-passwords","text":"Stop the stack by running docker-compose stop . When the containers came to a stop, run this command: docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && mysql -hlocalhost -uroot && exit 0\"' mysql-mailcow","title":"Reset MySQL Passwords"},{"location":"debug-reset_pw/#1-find-database-name","text":"# source mailcow.conf # docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mailcow_database | <===== | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)","title":"1. Find database name"},{"location":"debug-reset_pw/#2-reset-one-or-more-users","text":"","title":"2. Reset one or more users"},{"location":"debug-reset_pw/#21-maria-db-104-older-mailcow-installations","text":"Both \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both. MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('gotr00t'), password = PASSWORD('gotr00t') WHERE User = 'root'; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('mookuh'), password = PASSWORD('mookuh') WHERE User = 'mailcow' AND Host = '%'; MariaDB [(none)]> FLUSH PRIVILEGES;","title":"2.1 Maria DB < 10.4 (older mailcow installations)"},{"location":"debug-reset_pw/#22-maria-db-104-current-mailcows","text":"MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> ALTER USER 'mailcow'@'%' IDENTIFIED BY 'mookuh'; MariaDB [(none)]> ALTER USER 'root'@'%' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> FLUSH PRIVILEGES;","title":"2.2 Maria DB >= 10.4 (current mailcows)"},{"location":"debug-reset_pw/#remove-two-factor-authentication","text":"","title":"Remove Two-Factor Authentication"},{"location":"debug-reset_pw/#for-mailcow-webui","text":"This works similar to resetting a MySQL password, now we do it from the host without connecting to the MySQL CLI: source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e \"DELETE FROM tfa WHERE username='YOUR_USERNAME';\"","title":"For mailcow WebUI:"},{"location":"debug-reset_pw/#for-sogo","text":"docker-compose exec -u sogo sogo-mailcow sogo-tool user-preferences set defaults user@example.com SOGoGoogleAuthenticatorEnabled '{\"SOGoGoogleAuthenticatorEnabled\":0}'","title":"For SOGo:"},{"location":"debug-reset_tls/","text":"In case you encounter problems with your certificate, key or Let's Encrypt account, please try to reset the TLS assets: source mailcow.conf docker-compose down rm -rf data/assets/ssl mkdir data/assets/ssl openssl req -x509 -newkey rsa:4096 -keyout data/assets/ssl-example/key.pem -out data/assets/ssl-example/cert.pem -days 365 -subj \"/C=DE/ST=NRW/L=Willich/O=mailcow/OU=mailcow/CN=${MAILCOW_HOSTNAME}\" -sha256 -nodes cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/ docker-compose up -d This will stop mailcow, source the variables we need, create a self-signed certificate and start mailcow. If you use Let's Encrypt you should be careful as you will create a new account and a new set of certificates. You will run into a ratelimit sooner or later. Please also note that previous TLSA records will be invalid.","title":"Reset TLS certificates"},{"location":"debug-rm_volumes/","text":"You may want to remove a set of persistent data to resolve a conflict or to start over. mailcowdockerized can vary and depends on your compose project name (if it's unchanged, mailcowdockerized is the correct value). If you are unsure about volume names, run docker volume ls for a full list. Delete a single volume: docker volume rm mailcowdockerized_${VOLUME_NAME} Remove volume mysql-vol-1 to remove all MySQL data. Remove volume redis-vol-1 to remove all Redis data. Remove volume vmail-vol-1 to remove all contents of /var/vmail mounted to dovecot-mailcow . Remove volume rspamd-vol-1 to remove all Rspamd data. Remove volume crypt-vol-1 to remove all crypto data. This will render all mails unreadable. Alternatively, running docker-compose down -v will destroy all mailcow: dockerized volumes and delete any related containers and networks.","title":"Remove Persistent Data"},{"location":"debug/","text":"When a problem occurs, then always for a reason! What you want to do in such a case is: Read your logs; follow them to see what the reason for your problem is. Follow the leads given to you in your logfiles and start investigating. Restarting the troubled service or the whole stack to see if the problem persists. Read the documentation of the troubled service and search it's bugtracker for your problem. Search our issues for your problem. Create an issue over at our GitHub repository if you think your problem might be a bug or a missing feature you badly need. But please make sure, that you include all the logs and a full description to your problem. Please do not ask for support on Git. Join our Telegram community or find the official support packages at Servercow .","title":"Introduction"},{"location":"firststeps-disable_ipv6/","text":"This is ONLY recommended if you do not have an IPv6 enabled network on your host! If you really need to, you can disable the usage of IPv6 in the compose file. Additionally, you can also disable the startup of container \"ipv6nat-mailcow\", as it's not needed if you won't use IPv6. Instead of editing docker-compose.yml directly, it is preferable to create an override file for it and implement your changes to the service there. Unfortunately, this right now only seems to work for services, not for network settings. To disable IPv6 on the mailcow network, open docker-compose.yml with your favourite text editor and search for the network section (it's near the bottom of the file). 1. Modify docker-compose.yml Change enable_ipv6: true to enable_ipv6: false : networks: mailcow-network: [...] enable_ipv6: true # <<< set to false [...] 2. Disable ipv6nat-mailcow To disable the ipv6nat-mailcow container as well, go to your mailcow directory and create a new file called \"docker-compose.override.yml\": NOTE: If you already have an override file, of course don't recreate it, but merge the lines below into your existing one accordingly! # cd /opt/mailcow-dockerized # touch docker-compose.override.yml Open the file in your favourite text editor and fill in the following: version: '2.1' services: ipv6nat-mailcow: image: bash:latest restart: \"no\" entrypoint: [\"echo\", \"ipv6nat disabled in compose.override.yml\"] For these changes to be effective, you need to fully stop and then restart the stack, so containers and networks are recreated: docker-compose down docker-compose up -d 3. Disable IPv6 in unbound-mailcow Edit data/conf/unbound/unbound.conf and set do-ip6 to \"no\": server: [...] do-ip6: no [...] Restart Unbound: docker-compose restart unbound-mailcow 4. Disable IPv6 in postfix-mailcow Create data/conf/postfix/extra.cf and set smtp_address_preference to ipv4 : smtp_address_preference = ipv4 inet_protocols = ipv4 Restart Postfix: docker-compose restart postfix-mailcow","title":"Disable IPv6"},{"location":"firststeps-dmarc_reporting/","text":"DMARC Reporting done via Rspamd DMARC Module. Rspamd documentation can be found here: https://rspamd.com/doc/modules/dmarc.html Important: Change example.com , mail.example.com and Example to reflect your setup DMARC reporting requires additional attention, especially over the first few days All receiving domains hosted on mailcow send from one reporting domain. It is recommended to use the parent domain of your MAILCOW_HOSTNAME : If your MAILCOW_HOSTNAME is mail.example.com change the following config to domain = \"example.com\"; Set email equally, e.g. email = \"noreply-dmarc@example.com\"; It is optional but recommended to create an email user noreply-dmarc in mailcow to handle bounces. Enable DMARC reporting \u00b6 Create the file data/conf/rspamd/local.d/dmarc.conf and set the following content: reporting { enabled = true; email = 'noreply-dmarc@example.com'; domain = 'example.com'; org_name = 'Example'; helo = 'rspamd'; smtp = 'postfix'; smtp_port = 25; from_name = 'Example DMARC Report'; msgid_from = 'rspamd.mail.example.com'; max_entries = 2k; keys_expire = 2d; } Create or modify docker-compose.override.yml in the mailcow-dockerized base directory: version: '2.1' services: rspamd-mailcow: environment: - MASTER=${MASTER:-y} labels: ofelia.enabled: \"true\" ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" ofelia.job-exec.rspamd_dmarc_reporting.command: \"/bin/bash -c \\\"[[ $${MASTER} == y ]] && /usr/bin/rspamadm dmarc_report > /var/lib/rspamd/dmarc_reports_last_log 2>&1 || exit 0\\\"\" ofelia-mailcow: depends_on: - rspamd-mailcow Run docker-compose up -d Send a copy reports to yourself \u00b6 To receive a hidden copy of reports generated by Rspamd you can set a bcc_addrs list in the reporting config section of data/conf/rspamd/local.d/dmarc.conf : reporting { enabled = true; email = 'noreply-dmarc@example.com'; bcc_addrs = [\"noreply-dmarc@example.com\",\"parsedmarc@example.com\"]; [...] Rspamd will load changes in real time, so you won't need to restart the container at this point. This can be useful if you... ...want to check that your DMARC reports are sent correctly and authenticated. ...want to analyze your own reports to get statistics, i.e. to use with ParseDMARC or other analytic systems. Troubleshooting \u00b6 Check when the report schedule last ran: docker-compose exec rspamd-mailcow date -r /var/lib/rspamd/dmarc_reports_last_log See the latest report output: docker-compose exec rspamd-mailcow cat /var/lib/rspamd/dmarc_reports_last_log Manually trigger a DMARC report: docker-compose exec rspamd-mailcow rspamadm dmarc_report Validate that Rspamd has recorded data in Redis: docker-compose exec redis-mailcow redis-cli KEYS 'dmarc;*' docker-compose exec redis-mailcow redis-cli HGETALL \"dmarc;example.com;20211231\" Change DMARC reporting frequency \u00b6 In the example above reports are sent once every 24 hours. You may want to change that interval: Edit docker-compose.override.yml and a djust ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" to a desired value. Run docker-compose up -d Run docker-compose restart ofelia-mailcow Disable DMARC Reporting \u00b6 To disable reporting: Set enabled to false in data/conf/rspamd/local.d/dmarc.conf Revert changes done to docker-compose.override.yml Run docker-compose up -d","title":"DMARC Reporting"},{"location":"firststeps-dmarc_reporting/#enable-dmarc-reporting","text":"Create the file data/conf/rspamd/local.d/dmarc.conf and set the following content: reporting { enabled = true; email = 'noreply-dmarc@example.com'; domain = 'example.com'; org_name = 'Example'; helo = 'rspamd'; smtp = 'postfix'; smtp_port = 25; from_name = 'Example DMARC Report'; msgid_from = 'rspamd.mail.example.com'; max_entries = 2k; keys_expire = 2d; } Create or modify docker-compose.override.yml in the mailcow-dockerized base directory: version: '2.1' services: rspamd-mailcow: environment: - MASTER=${MASTER:-y} labels: ofelia.enabled: \"true\" ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" ofelia.job-exec.rspamd_dmarc_reporting.command: \"/bin/bash -c \\\"[[ $${MASTER} == y ]] && /usr/bin/rspamadm dmarc_report > /var/lib/rspamd/dmarc_reports_last_log 2>&1 || exit 0\\\"\" ofelia-mailcow: depends_on: - rspamd-mailcow Run docker-compose up -d","title":"Enable DMARC reporting"},{"location":"firststeps-dmarc_reporting/#send-a-copy-reports-to-yourself","text":"To receive a hidden copy of reports generated by Rspamd you can set a bcc_addrs list in the reporting config section of data/conf/rspamd/local.d/dmarc.conf : reporting { enabled = true; email = 'noreply-dmarc@example.com'; bcc_addrs = [\"noreply-dmarc@example.com\",\"parsedmarc@example.com\"]; [...] Rspamd will load changes in real time, so you won't need to restart the container at this point. This can be useful if you... ...want to check that your DMARC reports are sent correctly and authenticated. ...want to analyze your own reports to get statistics, i.e. to use with ParseDMARC or other analytic systems.","title":"Send a copy reports to yourself"},{"location":"firststeps-dmarc_reporting/#troubleshooting","text":"Check when the report schedule last ran: docker-compose exec rspamd-mailcow date -r /var/lib/rspamd/dmarc_reports_last_log See the latest report output: docker-compose exec rspamd-mailcow cat /var/lib/rspamd/dmarc_reports_last_log Manually trigger a DMARC report: docker-compose exec rspamd-mailcow rspamadm dmarc_report Validate that Rspamd has recorded data in Redis: docker-compose exec redis-mailcow redis-cli KEYS 'dmarc;*' docker-compose exec redis-mailcow redis-cli HGETALL \"dmarc;example.com;20211231\"","title":"Troubleshooting"},{"location":"firststeps-dmarc_reporting/#change-dmarc-reporting-frequency","text":"In the example above reports are sent once every 24 hours. You may want to change that interval: Edit docker-compose.override.yml and a djust ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" to a desired value. Run docker-compose up -d Run docker-compose restart ofelia-mailcow","title":"Change DMARC reporting frequency"},{"location":"firststeps-dmarc_reporting/#disable-dmarc-reporting","text":"To disable reporting: Set enabled to false in data/conf/rspamd/local.d/dmarc.conf Revert changes done to docker-compose.override.yml Run docker-compose up -d","title":"Disable DMARC Reporting"},{"location":"firststeps-ip_bindings/","text":"Warning Changing the binding does not affect source NAT. See SNAT for required steps. IPv4 binding \u00b6 To adjust one or multiple IPv4 bindings, open mailcow.conf and edit one, multiple or all variables as per your needs: # For technical reasons, http bindings are a bit different from other service bindings. # You will find the following variables, separated by a bind address and its port: # Example: HTTP_BIND=1.2.3.4 HTTP_PORT=80 HTTP_BIND= HTTPS_PORT=443 HTTPS_BIND= # Other services are bound by using the following format: # SMTP_PORT=1.2.3.4:25 will bind SMTP to the IP 1.2.3.4 on port 25 # Important! Specifying an IPv4 address will skip all IPv6 bindings since Docker 20.x. # doveadm, SQL as well as Solr are bound to local ports only, please do not change that, unless you know what you are doing. SMTP_PORT=25 SMTPS_PORT=465 SUBMISSION_PORT=587 IMAP_PORT=143 IMAPS_PORT=993 POP_PORT=110 POPS_PORT=995 SIEVE_PORT=4190 DOVEADM_PORT=127.0.0.1:19991 SQL_PORT=127.0.0.1:13306 SOLR_PORT=127.0.0.1:18983 To apply your changes, run docker-compose down followed by docker-compose up -d . IPv6 binding \u00b6 Changing IPv6 bindings is different from IPv4. Again, this has a technical background. A docker-compose.override.yml file will be used instead of editing the docker-compose.yml file directly. This is to maintain updatability, as the docker-compose.yml file gets updated regularly and your changes will most likely be overwritten. Edit to create a file docker-compose.override.yml with the following content. Its content will be merged with the productive docker-compose.yml file. An imaginary IPv6 2a00:dead:beef::abc is given. The first suffix :PORT1 defines the external port, while the second suffix :PORT2 routes to the corresponding port inside the container and must not be changed. version: '2.1' services: dovecot-mailcow: ports: - '2a00:dead:beef::abc:143:143' - '2a00:dead:beef::abc:993:993' - '2a00:dead:beef::abc:110:110' - '2a00:dead:beef::abc:995:995' - '2a00:dead:beef::abc:4190:4190' postfix-mailcow: ports: - '2a00:dead:beef::abc:25:25' - '2a00:dead:beef::abc:465:465' - '2a00:dead:beef::abc:587:587' nginx-mailcow: ports: - '2a00:dead:beef::abc:80:80' - '2a00:dead:beef::abc:443:443' To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IP bindings"},{"location":"firststeps-ip_bindings/#ipv4-binding","text":"To adjust one or multiple IPv4 bindings, open mailcow.conf and edit one, multiple or all variables as per your needs: # For technical reasons, http bindings are a bit different from other service bindings. # You will find the following variables, separated by a bind address and its port: # Example: HTTP_BIND=1.2.3.4 HTTP_PORT=80 HTTP_BIND= HTTPS_PORT=443 HTTPS_BIND= # Other services are bound by using the following format: # SMTP_PORT=1.2.3.4:25 will bind SMTP to the IP 1.2.3.4 on port 25 # Important! Specifying an IPv4 address will skip all IPv6 bindings since Docker 20.x. # doveadm, SQL as well as Solr are bound to local ports only, please do not change that, unless you know what you are doing. SMTP_PORT=25 SMTPS_PORT=465 SUBMISSION_PORT=587 IMAP_PORT=143 IMAPS_PORT=993 POP_PORT=110 POPS_PORT=995 SIEVE_PORT=4190 DOVEADM_PORT=127.0.0.1:19991 SQL_PORT=127.0.0.1:13306 SOLR_PORT=127.0.0.1:18983 To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IPv4 binding"},{"location":"firststeps-ip_bindings/#ipv6-binding","text":"Changing IPv6 bindings is different from IPv4. Again, this has a technical background. A docker-compose.override.yml file will be used instead of editing the docker-compose.yml file directly. This is to maintain updatability, as the docker-compose.yml file gets updated regularly and your changes will most likely be overwritten. Edit to create a file docker-compose.override.yml with the following content. Its content will be merged with the productive docker-compose.yml file. An imaginary IPv6 2a00:dead:beef::abc is given. The first suffix :PORT1 defines the external port, while the second suffix :PORT2 routes to the corresponding port inside the container and must not be changed. version: '2.1' services: dovecot-mailcow: ports: - '2a00:dead:beef::abc:143:143' - '2a00:dead:beef::abc:993:993' - '2a00:dead:beef::abc:110:110' - '2a00:dead:beef::abc:995:995' - '2a00:dead:beef::abc:4190:4190' postfix-mailcow: ports: - '2a00:dead:beef::abc:25:25' - '2a00:dead:beef::abc:465:465' - '2a00:dead:beef::abc:587:587' nginx-mailcow: ports: - '2a00:dead:beef::abc:80:80' - '2a00:dead:beef::abc:443:443' To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IPv6 binding"},{"location":"firststeps-local_mta/","text":"The easiest option would be to disable the listener on port 25/tcp. Postfix users disable the listener by commenting the following line (starting with smtp or 25 ) in /etc/postfix/master.cf : #smtp inet n - - - - smtpd Furthermore, to relay over a dockerized mailcow, you may want to add 172.22.1.1 as relayhost and remove the Docker interface from \"inet_interfaces\": postconf -e 'relayhost = 172.22.1.1' postconf -e \"mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128\" postconf -e \"inet_interfaces = loopback-only\" postconf -e \"relay_transport = relay\" postconf -e \"default_transport = smtp\" Now it is important to not have the same FQDN in myhostname as you use for your dockerized mailcow. Check your local (non-Docker) Postfix' main.cf for myhostname and set it to something different, for example local.my.fqdn.tld . \"172.22.1.1\" is the mailcow created network gateway in Docker. Relaying over this interface is necessary (instead of - for example - relaying directly over ${MAILCOW_HOSTNAME}) to relay over a known internal network. Restart Postfix after applying your changes.","title":"Local MTA on Docker host"},{"location":"firststeps-logging/","text":"Logging in mailcow: dockerized consists of multiple stages, but is, after all, much more flexible and easier to integrate into a logging daemon than before. In Docker the containerized application (PID 1) writes its output to stdout. For real one-application containers this works just fine. Run docker-compose logs --help to learn more. Some containers log or stream to multiple destinations. No container will keep persistent logs in it. Containers are transient items! In the end, every line of logs will reach the Docker daemon - unfiltered. The default logging driver is \"json\" . Filtered logs \u00b6 Some logs are filtered and written to Redis keys but also streamed to a Redis channel. The Redis channel is used to stream logs with failed authentication attempts to be read by netfilter-mailcow. The Redis keys are persistent and will keep 10000 lines of logs for the web UI. This mechanism makes it possible to use whatever Docker logging driver you want to, without losing the ability to read logs from the UI or ban suspicious clients with netfilter-mailcow. Redis keys will only hold logs from applications and filter out system messages (think of cron etc.). Logging drivers \u00b6 Via docker-compose.override.yml \u00b6 Here is the good news: Since Docker has some great logging drivers, you can integrate mailcow: dockerized into your existing logging environment with ease. Create a docker-compose.override.yml and add, for example, this block to use the \"gelf\" logging plugin for postfix-mailcow : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"gelf\" options: gelf-address: \"udp://graylog:12201\" Another example for Syslog : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" dovecot-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" rspamd-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" # For Rsyslog only: # To move local3 input to /var/log/mailcow.log and stop processing, create a file \"/etc/rsyslog.d/docker.conf\": local3.* /var/log/mailcow.logs & ~ # Restart rsyslog afterwards. via daemon.json (globally) \u00b6 If you want to change the logging driver globally , edit Dockers daemon configuration file /etc/docker/daemon.json and restart the Docker service: { ... \"log-driver\": \"gelf\", \"log-opts\": { \"gelf-address\": \"udp://graylog:12201\" } ... } For Syslog: { ... \"log-driver\": \"syslog\", \"log-opts\": { \"syslog-address\": \"udp://1.2.3.4:514\" } ... } Restart the Docker daemon and run docker-compose down && docker-compose up -d to recreate the containers with the new logging driver.","title":"Logging"},{"location":"firststeps-logging/#filtered-logs","text":"Some logs are filtered and written to Redis keys but also streamed to a Redis channel. The Redis channel is used to stream logs with failed authentication attempts to be read by netfilter-mailcow. The Redis keys are persistent and will keep 10000 lines of logs for the web UI. This mechanism makes it possible to use whatever Docker logging driver you want to, without losing the ability to read logs from the UI or ban suspicious clients with netfilter-mailcow. Redis keys will only hold logs from applications and filter out system messages (think of cron etc.).","title":"Filtered logs"},{"location":"firststeps-logging/#logging-drivers","text":"","title":"Logging drivers"},{"location":"firststeps-logging/#via-docker-composeoverrideyml","text":"Here is the good news: Since Docker has some great logging drivers, you can integrate mailcow: dockerized into your existing logging environment with ease. Create a docker-compose.override.yml and add, for example, this block to use the \"gelf\" logging plugin for postfix-mailcow : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"gelf\" options: gelf-address: \"udp://graylog:12201\" Another example for Syslog : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" dovecot-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" rspamd-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" # For Rsyslog only: # To move local3 input to /var/log/mailcow.log and stop processing, create a file \"/etc/rsyslog.d/docker.conf\": local3.* /var/log/mailcow.logs & ~ # Restart rsyslog afterwards.","title":"Via docker-compose.override.yml"},{"location":"firststeps-logging/#via-daemonjson-globally","text":"If you want to change the logging driver globally , edit Dockers daemon configuration file /etc/docker/daemon.json and restart the Docker service: { ... \"log-driver\": \"gelf\", \"log-opts\": { \"gelf-address\": \"udp://graylog:12201\" } ... } For Syslog: { ... \"log-driver\": \"syslog\", \"log-opts\": { \"syslog-address\": \"udp://1.2.3.4:514\" } ... } Restart the Docker daemon and run docker-compose down && docker-compose up -d to recreate the containers with the new logging driver.","title":"via daemon.json (globally)"},{"location":"firststeps-rp/","text":"You don't need to change the Nginx site that comes with mailcow: dockerized. mailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. 1. Make sure you change HTTP_BIND and HTTPS_BIND in mailcow.conf to a local address and set the ports accordingly, for example: HTTP_BIND = 127 .0.0.1 HTTP_PORT = 8080 HTTPS_BIND = 127 .0.0.1 HTTPS_PORT = 8443 This will also change the bindings inside the Nginx container! This is important, if you decide to use a proxy within Docker. IMPORTANT: Do not use port 8081, 9081 or 65510! Recreate affected containers by running docker-compose up -d . Important information, please read them carefully! Info If you plan to use a reverse proxy and want to use another server name that is not MAILCOW_HOSTNAME, you need to read Adding additional server names for mailcow UI at the bottom of this page. Warning Make sure you run generate_config.sh before you enable any site configuration examples below. The script generate_config.sh copies snake-oil certificates to the correct location, so the services will not fail to start due to missing files. Warning If you enable TLS SNI ( ENABLE_TLS_SNI in mailcow.conf), the certificate paths in your reverse proxy must match the correct paths in data/assets/ssl/{hostname}. The certificates will be split into data/assets/ssl/{hostname1,hostname2,etc} and therefore will not work when you copy the examples from below pointing to data/assets/ssl/cert.pem etc. Info Using the site configs below will forward ACME requests to mailcow and let it handle certificates itself. The downside of using mailcow as ACME client behind a reverse proxy is, that you will need to reload your webserver after acme-mailcow changed/renewed/created the certificate. You can either reload your webserver daily or write a script to watch the file for changes. On many servers logrotate will reload the webserver daily anyway. If you want to use a local certbot installation, you will need to change the SSL certificate parameters accordingly. Make sure you run a post-hook script when you decide to use external ACME clients. You will find an example at the bottom of this page. 2. Configure your local webserver as reverse proxy: Apache 2.4 \u00b6 Required modules: a2enmod rewrite proxy proxy_http headers ssl Let's Encrypt will follow our rewrite, certificate requests in mailcow will work fine. Take care of highlighted lines. ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L] ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"http\" ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* # You should proxy to a plain HTTP session to offload SSL processing ProxyPass /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync connectiontimeout=4000 ProxyPassReverse /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"https\" SSLCertificateFile MAILCOW_PATH/data/assets/ssl/cert.pem SSLCertificateKeyFile MAILCOW_PATH/data/assets/ssl/key.pem # If you plan to proxy to a HTTPS host: #SSLProxyEngine On # If you plan to proxy to an untrusted HTTPS host: #SSLProxyVerify none #SSLProxyCheckPeerCN off #SSLProxyCheckPeerName off #SSLProxyCheckPeerExpire off Nginx \u00b6 Let's Encrypt will follow our rewrite, certificate requests will work fine. Take care of highlighted lines. server { listen 80 default_server; listen [::]:80 default_server; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; ssl_certificate MAILCOW_PATH/data/assets/ssl/cert.pem; ssl_certificate_key MAILCOW_PATH/data/assets/ssl/key.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # See https://ssl-config.mozilla.org/#server=nginx for the latest ssl settings recommendations # An example config is given below ssl_protocols TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!SHA1:!kRSA; ssl_prefer_server_ciphers off; location /Microsoft-Server-ActiveSync { proxy_pass http://127.0.0.1:8080/Microsoft-Server-ActiveSync; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 75; proxy_send_timeout 3650; proxy_read_timeout 3650; proxy_buffers 64 256k; client_body_buffer_size 512k; client_max_body_size 0; } location / { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } } HAProxy (community supported) \u00b6 Warning This is an unsupported community contribution. Feel free to provide fixes. Important/Fixme : This example only forwards HTTPS traffic and does not use mailcows built-in ACME client. frontend https-in bind :::443 v4v6 ssl crt mailcow.pem default_backend mailcow backend mailcow option forwardfor http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server mailcow 127.0.0.1:8080 check Traefik v2 (community supported) \u00b6 Warning This is an unsupported community contribution. Feel free to provide fixes. Important : This config only covers the \"reverseproxing\" of the webpannel (nginx-mailcow) using Traefik v2, if you also want to reverseproxy the mail services such as dovecot, postfix... you'll just need to adapt the following config to each container and create an EntryPoint on your traefik.toml or traefik.yml (depending which config you use) for each port. For this section we'll assume you have your Traefik 2 [certificatesresolvers] properly configured on your traefik configuration file, and also using acme, also, the following example uses Lets Encrypt, but feel free to change it to your own cert resolver. You can find a basic Traefik 2 toml config file with all the above implemented which can be used for this example here traefik.toml if you need one, or a hint on how to adapt your config. So, first of all, we are going to disable the acme-mailcow container since we'll use the certs that traefik will provide us. For this we'll have to set SKIP_LETS_ENCRYPT=y on our mailcow.conf , and run docker-compose up -d to apply the changes. Then we'll create a docker-compose.override.yml file in order to override the main docker-compose.yml found in your mailcow root folder. version : '2.1' services : nginx-mailcow : networks : # add Traefik's network web : labels : - traefik.enable=true # Creates a router called \"moo\" for the container, and sets up a rule to link the container to certain rule, # in this case, a Host rule with our MAILCOW_HOSTNAME var. - traefik.http.routers.moo.rule=Host(`${MAILCOW_HOSTNAME}`) # Enables tls over the router we created before. - traefik.http.routers.moo.tls=true # Specifies which kind of cert resolver we'll use, in this case le (Lets Encrypt). - traefik.http.routers.moo.tls.certresolver=le # Creates a service called \"moo\" for the container, and specifies which internal port of the container # should traefik route the incoming data to. - traefik.http.services.moo.loadbalancer.server.port=${HTTP_PORT} # Specifies which entrypoint (external port) should traefik listen to, for this container. # websecure being port 443, check the traefik.toml file liked above. - traefik.http.routers.moo.entrypoints=websecure # Make sure traefik uses the web network, not the mailcowdockerized_mailcow-network - traefik.docker.network=web certdumper : image : humenius/traefik-certs-dumper container_name : traefik_certdumper network_mode : none volumes : # mount the folder which contains Traefik's `acme.json' file # in this case Traefik is started from its own docker-compose in ../traefik - ../traefik/data:/traefik:ro # mount mailcow's SSL folder - ./data/assets/ssl/:/output:rw environment : # only change this, if you're using another domain for mailcow's web frontend compared to the standard config - DOMAIN=${MAILCOW_HOSTNAME} networks : web : external : true Start the new containers with docker-compose up -d . Now, there's only one thing left to do, which is setup the certs so that the mail services can use them as well, since Traefik 2 uses an acme v2 format to save ALL the license from all the domains we have, we'll need to find a way to dump the certs, lucky we have this tiny container which grabs the acme.json file trough a volume, and a variable DOMAIN=example.org , and with these, the container will output the cert.pem and key.pem files, for this we'll simply run the traefik-certs-dumper container binding the /traefik volume to the folder where our acme.json is saved, bind the /output volume to our mailcow data/assets/ssl/ folder, and set up the DOMAIN=example.org variable to the domain we want the certs dumped from. This container will watch over the acme.json file for any changes, and regenerate the cert.pem and key.pem files directly into data/assets/ssl/ being the path binded to the container's /output path. You can use the command line to run it, or use the docker-compose shown here . After we have the certs dumped, we'll have to reload the configs from our postfix and dovecot containers, and check the certs, you can see how here . Aaand that should be it \ud83d\ude0a, you can check if the Traefik router works fine trough Traefik's dashboard / traefik logs / accessing the setted domain trough https, or / and check HTTPS, SMTP and IMAP trough the commands shown on the page linked before. Optional: Post-hook script for non-mailcow ACME clients \u00b6 Using a local certbot (or any other ACME client) requires to restart some containers, you can do this with a post-hook script. Make sure you change the paths accordingly: #!/bin/bash cp /etc/letsencrypt/live/my.domain.tld/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem cp /etc/letsencrypt/live/my.domain.tld/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem postfix_c=$(docker ps -qaf name=postfix-mailcow) dovecot_c=$(docker ps -qaf name=dovecot-mailcow) nginx_c=$(docker ps -qaf name=nginx-mailcow) docker restart ${postfix_c} ${dovecot_c} ${nginx_c} Adding additional server names for mailcow UI \u00b6 If you plan to use a server name that is not MAILCOW_HOSTNAME in your reverse proxy, make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES first. Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond to your reverse proxy with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Reverse Proxy"},{"location":"firststeps-rp/#apache-24","text":"Required modules: a2enmod rewrite proxy proxy_http headers ssl Let's Encrypt will follow our rewrite, certificate requests in mailcow will work fine. Take care of highlighted lines. ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L] ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"http\" ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* # You should proxy to a plain HTTP session to offload SSL processing ProxyPass /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync connectiontimeout=4000 ProxyPassReverse /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"https\" SSLCertificateFile MAILCOW_PATH/data/assets/ssl/cert.pem SSLCertificateKeyFile MAILCOW_PATH/data/assets/ssl/key.pem # If you plan to proxy to a HTTPS host: #SSLProxyEngine On # If you plan to proxy to an untrusted HTTPS host: #SSLProxyVerify none #SSLProxyCheckPeerCN off #SSLProxyCheckPeerName off #SSLProxyCheckPeerExpire off ","title":"Apache 2.4"},{"location":"firststeps-rp/#nginx","text":"Let's Encrypt will follow our rewrite, certificate requests will work fine. Take care of highlighted lines. server { listen 80 default_server; listen [::]:80 default_server; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; ssl_certificate MAILCOW_PATH/data/assets/ssl/cert.pem; ssl_certificate_key MAILCOW_PATH/data/assets/ssl/key.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # See https://ssl-config.mozilla.org/#server=nginx for the latest ssl settings recommendations # An example config is given below ssl_protocols TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!SHA1:!kRSA; ssl_prefer_server_ciphers off; location /Microsoft-Server-ActiveSync { proxy_pass http://127.0.0.1:8080/Microsoft-Server-ActiveSync; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 75; proxy_send_timeout 3650; proxy_read_timeout 3650; proxy_buffers 64 256k; client_body_buffer_size 512k; client_max_body_size 0; } location / { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } }","title":"Nginx"},{"location":"firststeps-rp/#haproxy-community-supported","text":"Warning This is an unsupported community contribution. Feel free to provide fixes. Important/Fixme : This example only forwards HTTPS traffic and does not use mailcows built-in ACME client. frontend https-in bind :::443 v4v6 ssl crt mailcow.pem default_backend mailcow backend mailcow option forwardfor http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server mailcow 127.0.0.1:8080 check","title":"HAProxy (community supported)"},{"location":"firststeps-rp/#traefik-v2-community-supported","text":"Warning This is an unsupported community contribution. Feel free to provide fixes. Important : This config only covers the \"reverseproxing\" of the webpannel (nginx-mailcow) using Traefik v2, if you also want to reverseproxy the mail services such as dovecot, postfix... you'll just need to adapt the following config to each container and create an EntryPoint on your traefik.toml or traefik.yml (depending which config you use) for each port. For this section we'll assume you have your Traefik 2 [certificatesresolvers] properly configured on your traefik configuration file, and also using acme, also, the following example uses Lets Encrypt, but feel free to change it to your own cert resolver. You can find a basic Traefik 2 toml config file with all the above implemented which can be used for this example here traefik.toml if you need one, or a hint on how to adapt your config. So, first of all, we are going to disable the acme-mailcow container since we'll use the certs that traefik will provide us. For this we'll have to set SKIP_LETS_ENCRYPT=y on our mailcow.conf , and run docker-compose up -d to apply the changes. Then we'll create a docker-compose.override.yml file in order to override the main docker-compose.yml found in your mailcow root folder. version : '2.1' services : nginx-mailcow : networks : # add Traefik's network web : labels : - traefik.enable=true # Creates a router called \"moo\" for the container, and sets up a rule to link the container to certain rule, # in this case, a Host rule with our MAILCOW_HOSTNAME var. - traefik.http.routers.moo.rule=Host(`${MAILCOW_HOSTNAME}`) # Enables tls over the router we created before. - traefik.http.routers.moo.tls=true # Specifies which kind of cert resolver we'll use, in this case le (Lets Encrypt). - traefik.http.routers.moo.tls.certresolver=le # Creates a service called \"moo\" for the container, and specifies which internal port of the container # should traefik route the incoming data to. - traefik.http.services.moo.loadbalancer.server.port=${HTTP_PORT} # Specifies which entrypoint (external port) should traefik listen to, for this container. # websecure being port 443, check the traefik.toml file liked above. - traefik.http.routers.moo.entrypoints=websecure # Make sure traefik uses the web network, not the mailcowdockerized_mailcow-network - traefik.docker.network=web certdumper : image : humenius/traefik-certs-dumper container_name : traefik_certdumper network_mode : none volumes : # mount the folder which contains Traefik's `acme.json' file # in this case Traefik is started from its own docker-compose in ../traefik - ../traefik/data:/traefik:ro # mount mailcow's SSL folder - ./data/assets/ssl/:/output:rw environment : # only change this, if you're using another domain for mailcow's web frontend compared to the standard config - DOMAIN=${MAILCOW_HOSTNAME} networks : web : external : true Start the new containers with docker-compose up -d . Now, there's only one thing left to do, which is setup the certs so that the mail services can use them as well, since Traefik 2 uses an acme v2 format to save ALL the license from all the domains we have, we'll need to find a way to dump the certs, lucky we have this tiny container which grabs the acme.json file trough a volume, and a variable DOMAIN=example.org , and with these, the container will output the cert.pem and key.pem files, for this we'll simply run the traefik-certs-dumper container binding the /traefik volume to the folder where our acme.json is saved, bind the /output volume to our mailcow data/assets/ssl/ folder, and set up the DOMAIN=example.org variable to the domain we want the certs dumped from. This container will watch over the acme.json file for any changes, and regenerate the cert.pem and key.pem files directly into data/assets/ssl/ being the path binded to the container's /output path. You can use the command line to run it, or use the docker-compose shown here . After we have the certs dumped, we'll have to reload the configs from our postfix and dovecot containers, and check the certs, you can see how here . Aaand that should be it \ud83d\ude0a, you can check if the Traefik router works fine trough Traefik's dashboard / traefik logs / accessing the setted domain trough https, or / and check HTTPS, SMTP and IMAP trough the commands shown on the page linked before.","title":"Traefik v2 (community supported)"},{"location":"firststeps-rp/#optional-post-hook-script-for-non-mailcow-acme-clients","text":"Using a local certbot (or any other ACME client) requires to restart some containers, you can do this with a post-hook script. Make sure you change the paths accordingly: #!/bin/bash cp /etc/letsencrypt/live/my.domain.tld/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem cp /etc/letsencrypt/live/my.domain.tld/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem postfix_c=$(docker ps -qaf name=postfix-mailcow) dovecot_c=$(docker ps -qaf name=dovecot-mailcow) nginx_c=$(docker ps -qaf name=nginx-mailcow) docker restart ${postfix_c} ${dovecot_c} ${nginx_c}","title":"Optional: Post-hook script for non-mailcow ACME clients"},{"location":"firststeps-rp/#adding-additional-server-names-for-mailcow-ui","text":"If you plan to use a server name that is not MAILCOW_HOSTNAME in your reverse proxy, make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES first. Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond to your reverse proxy with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Adding additional server names for mailcow UI"},{"location":"firststeps-rspamd_ui/","text":"Rspamd is an easy to use spam filtering tool presently installed with mailcow. Go to the mailcow web admin interface Navigate to the Access tab. (Configuration > Administration > Access) Modify the Rspamd UI password Go to https://${MAILCOW_HOSTNAME}/rspamd in a browser and log in! Additional configuration options and documentation can be found here : https://rspamd.com/webui/","title":"Rspamd UI"},{"location":"firststeps-snat/","text":"SNAT is used to change the source address of the packets sent by mailcow. It can be used to change the outgoing IP address on systems with multiple IP addresses. Open mailcow.conf , set either or both of the following parameters: # Use this IPv4 for outgoing connections (SNAT) SNAT_TO_SOURCE=1.2.3.4 # Use this IPv6 for outgoing connections (SNAT) SNAT6_TO_SOURCE=dead:beef Run docker-compose up -d . The values are read by netfilter-mailcow. netfilter-mailcow will make sure, the post-routing rules are on position 1 in the netfilter table. It does automatically delete and re-create them if they are found on another position than 1. Check the output of docker-compose logs --tail=200 netfilter-mailcow to ensure the SNAT settings have been applied.","title":"SNAT"},{"location":"firststeps-ssl/","text":"Let's Encrypt (out-of-the-box) \u00b6 The \"acme-mailcow\" container will try to obtain a LE certificate for ${MAILCOW_HOSTNAME} , autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN . Warning mailcow must be available on port 80 for the acme-client to work. Our reverse proxy example configurations do cover that. You can also use any external ACME client (certbot for example) to obtain certificates, but you will need to make sure, that they are copied to the correct location and a post-hook reloads affected containers. See more in the Reverse Proxy documentation. By default, which means 0 domains are added to mailcow, it will try to obtain a certificate for ${MAILCOW_HOSTNAME} . For each domain you add, it will try to resolve autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN to its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. If it succeeds, a name will be added as SAN to the certificate request. Only names that can be validated, will be added as SAN. For every domain you remove, the certificate will be moved and a new certificate will be requested. It is not possible to keep domains in a certificate, when we are not able validate the challenge for those. If you want to re-run the ACME client, use docker-compose restart acme-mailcow and monitor its logs with docker-compose logs --tail=200 -f acme-mailcow . Additional domain names \u00b6 Edit \"mailcow.conf\" and add a parameter ADDITIONAL_SAN like this: Do not use quotes ( \" ) and do not use spaces between the names! ADDITIONAL_SAN=smtp.*,cert1.example.com,cert2.example.org,whatever.* Each name will be validated against its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. A wildcard name like smtp.* will try to obtain a smtp.DOMAIN_NAME SAN for each domain added to mailcow. Run docker-compose up -d to recreate affected containers automatically. Info Using names other name MAILCOW_HOSTNAME to access the mailcow UI may need further configuration. If you plan to use a server name that is not MAILCOW_HOSTNAME to access the mailcow UI (for example by adding mail.* to ADDITIONAL_SAN make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES . Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply. Force renewal \u00b6 To force a renewal, you need to create a file named force_renew and restart the acme-mailcow container: cd /opt/mailcow-dockerized touch data/assets/ssl/force_renew docker-compose restart acme-mailcow # Now check the logs for a renewal docker-compose logs --tail=200 -f acme-mailcow The file will be deleted automatically. Validation errors and how to skip validation \u00b6 You can skip the IP verification by setting SKIP_IP_CHECK=y in mailcow.conf (no quotes). Be warned that a misconfiguration will get you ratelimited by Let's Encrypt! This is primarily useful for multi-IP setups where the IP check would return the incorrect source IP address. Due to using dynamic IPs for acme-mailcow, source NAT is not consistent over restarts. If you encounter problems with \"HTTP validation\", but your IP address confirmation succeeds, you are most likely using firewalld, ufw or any other firewall, that disallows connections from br-mailcow to your external interface. Both firewalld and ufw disallow this by default. It is often not enough to just stop these firewall services. You'd need to stop mailcow ( docker-compose down ), stop the firewall service, flush the chains and restart Docker. You can also skip this validation method by setting SKIP_HTTP_VERIFICATION=y in \"mailcow.conf\". Be warned that this is discouraged. In most cases, the HTTP verification is skipped to workaround unknown NAT reflection issues, which are not resolved by ignoring this specific network misconfiguration. If you encounter problems generating TLSA records in the DNS overview within mailcow, you are most likely having issues with NAT reflection you should fix. If you changed a SKIP_* parameter, run docker-compose up -d to apply your changes. Disable Let's Encrypt \u00b6 Disable Let's Encrypt completely \u00b6 Set SKIP_LETS_ENCRYPT=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Skip all names but ${MAILCOW_HOSTNAME} \u00b6 Add ONLY_MAILCOW_HOSTNAME=y to \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . The Let's Encrypt subjectAltName limit of 100 domains \u00b6 Let's Encrypt currently has a limit of 100 Domain Names per Certificate . By default, \"acme-mailcow\" will create a single SAN certificate for all validated domains (see the first section and Additional domain names ). This provides best compatibility but means the Let's Encrypt limit exceeds if you add too many domains to a single mailcow installation. To solve this, you can configure ENABLE_SSL_SNI to generate: A main server certificate with MAILCOW_HOSTNAME and all fully qualified domain names in the ADDITIONAL_SAN config One additional certificate for each domain found in the database with autodiscover. , autoconfig. and any other ADDITIONAL_SAN configured in this format (subdomain.*). Limitations: A certificate name ADDITIONAL_SAN=test.example.com will be added as SAN to the main certificate. A separate certificate/key pair will not be generated for this format. Postfix, Dovecot and Nginx will then serve these certificates with SNI. Set ENABLE_SSL_SNI=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Warning Not all clients support SNI, see Dovecot documentation or Wikipedia . You should make sure these clients use the MAILCOW_HOSTNAME for secure connections if you enable this feature. Here is an example: MAILCOW_HOSTNAME=server.email.tld ADDITIONAL_SAN=webmail.email.tld,mail.* Mailcow email domains: \"domain1.tld\" and \"domain2.tld\" The following certificates will be generated: server.email.tld, webmail.email.tld -> this is the default certificate, all clients can connect with these domains mail.domain1.tld, autoconfig.domain1.tld, autodiscover.domain1.tld -> individual certificate for domain1.tld, cannot be used by clients without SNI support mail.domain2.tld, autoconfig.domain2.tld, autodiscover.domain2.tld -> individual certificate for domain2.tld, cannot be used by clients without SNI support How to use your own certificate \u00b6 Make sure you disable mailcows internal LE client (see above). To use your own certificates, just save the combined certificate (containing the certificate and intermediate CA/CA if any) to data/assets/ssl/cert.pem and the corresponding key to data/assets/ssl/key.pem . IMPORTANT: Do not use symbolic links! Make sure you copy the certificates and do not link them to data/assets/ssl . Restart affected services afterwards: docker restart $(docker ps -qaf name=postfix-mailcow) docker restart $(docker ps -qaf name=nginx-mailcow) docker restart $(docker ps -qaf name=dovecot-mailcow) See Post-hook script for non-mailcow ACME clients for a full example script. Test against staging ACME directory \u00b6 Edit mailcow.conf and add LE_STAGING=y . Run docker-compose up -d to activate your changes. Custom directory URL \u00b6 Edit mailcow.conf and add the corresponding directory URL to the new variable DIRECTORY_URL : DIRECTORY_URL=https://acme-custom-v9000.api.letsencrypt.org/directory You cannot use LE_STAGING with DIRECTORY_URL . If both are set, only LE_STAGING is used. Run docker-compose up -d to activate your changes. Check your configuration \u00b6 Run docker-compose logs acme-mailcow to find out why a validation fails. To check if nginx serves the correct certificate, simply use a browser of your choice and check the displayed certificate. To check the certificate served by Postfix, Dovecot and Nginx we will use openssl : # Connect via SMTP (587) echo \"Q\" | openssl s_client -starttls smtp -crlf -connect mx.mailcow.email:587 # Connect via IMAP (143) echo \"Q\" | openssl s_client -starttls imap -showcerts -connect mx.mailcow.email:143 # Connect via HTTPS (443) echo \"Q\" | openssl s_client -connect mx.mailcow.email:443 To validate the expiry dates as returned by openssl against MAILCOW_HOSTNAME, you are able to use our helper script: cd /opt/mailcow-dockerized bash helper-scripts/expiry-dates.sh","title":"Advanced SSL"},{"location":"firststeps-ssl/#lets-encrypt-out-of-the-box","text":"The \"acme-mailcow\" container will try to obtain a LE certificate for ${MAILCOW_HOSTNAME} , autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN . Warning mailcow must be available on port 80 for the acme-client to work. Our reverse proxy example configurations do cover that. You can also use any external ACME client (certbot for example) to obtain certificates, but you will need to make sure, that they are copied to the correct location and a post-hook reloads affected containers. See more in the Reverse Proxy documentation. By default, which means 0 domains are added to mailcow, it will try to obtain a certificate for ${MAILCOW_HOSTNAME} . For each domain you add, it will try to resolve autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN to its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. If it succeeds, a name will be added as SAN to the certificate request. Only names that can be validated, will be added as SAN. For every domain you remove, the certificate will be moved and a new certificate will be requested. It is not possible to keep domains in a certificate, when we are not able validate the challenge for those. If you want to re-run the ACME client, use docker-compose restart acme-mailcow and monitor its logs with docker-compose logs --tail=200 -f acme-mailcow .","title":"Let's Encrypt (out-of-the-box)"},{"location":"firststeps-ssl/#additional-domain-names","text":"Edit \"mailcow.conf\" and add a parameter ADDITIONAL_SAN like this: Do not use quotes ( \" ) and do not use spaces between the names! ADDITIONAL_SAN=smtp.*,cert1.example.com,cert2.example.org,whatever.* Each name will be validated against its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. A wildcard name like smtp.* will try to obtain a smtp.DOMAIN_NAME SAN for each domain added to mailcow. Run docker-compose up -d to recreate affected containers automatically. Info Using names other name MAILCOW_HOSTNAME to access the mailcow UI may need further configuration. If you plan to use a server name that is not MAILCOW_HOSTNAME to access the mailcow UI (for example by adding mail.* to ADDITIONAL_SAN make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES . Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Additional domain names"},{"location":"firststeps-ssl/#force-renewal","text":"To force a renewal, you need to create a file named force_renew and restart the acme-mailcow container: cd /opt/mailcow-dockerized touch data/assets/ssl/force_renew docker-compose restart acme-mailcow # Now check the logs for a renewal docker-compose logs --tail=200 -f acme-mailcow The file will be deleted automatically.","title":"Force renewal"},{"location":"firststeps-ssl/#validation-errors-and-how-to-skip-validation","text":"You can skip the IP verification by setting SKIP_IP_CHECK=y in mailcow.conf (no quotes). Be warned that a misconfiguration will get you ratelimited by Let's Encrypt! This is primarily useful for multi-IP setups where the IP check would return the incorrect source IP address. Due to using dynamic IPs for acme-mailcow, source NAT is not consistent over restarts. If you encounter problems with \"HTTP validation\", but your IP address confirmation succeeds, you are most likely using firewalld, ufw or any other firewall, that disallows connections from br-mailcow to your external interface. Both firewalld and ufw disallow this by default. It is often not enough to just stop these firewall services. You'd need to stop mailcow ( docker-compose down ), stop the firewall service, flush the chains and restart Docker. You can also skip this validation method by setting SKIP_HTTP_VERIFICATION=y in \"mailcow.conf\". Be warned that this is discouraged. In most cases, the HTTP verification is skipped to workaround unknown NAT reflection issues, which are not resolved by ignoring this specific network misconfiguration. If you encounter problems generating TLSA records in the DNS overview within mailcow, you are most likely having issues with NAT reflection you should fix. If you changed a SKIP_* parameter, run docker-compose up -d to apply your changes.","title":"Validation errors and how to skip validation"},{"location":"firststeps-ssl/#disable-lets-encrypt","text":"","title":"Disable Let's Encrypt"},{"location":"firststeps-ssl/#disable-lets-encrypt-completely","text":"Set SKIP_LETS_ENCRYPT=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d .","title":"Disable Let's Encrypt completely"},{"location":"firststeps-ssl/#skip-all-names-but-mailcow_hostname","text":"Add ONLY_MAILCOW_HOSTNAME=y to \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d .","title":"Skip all names but ${MAILCOW_HOSTNAME}"},{"location":"firststeps-ssl/#the-lets-encrypt-subjectaltname-limit-of-100-domains","text":"Let's Encrypt currently has a limit of 100 Domain Names per Certificate . By default, \"acme-mailcow\" will create a single SAN certificate for all validated domains (see the first section and Additional domain names ). This provides best compatibility but means the Let's Encrypt limit exceeds if you add too many domains to a single mailcow installation. To solve this, you can configure ENABLE_SSL_SNI to generate: A main server certificate with MAILCOW_HOSTNAME and all fully qualified domain names in the ADDITIONAL_SAN config One additional certificate for each domain found in the database with autodiscover. , autoconfig. and any other ADDITIONAL_SAN configured in this format (subdomain.*). Limitations: A certificate name ADDITIONAL_SAN=test.example.com will be added as SAN to the main certificate. A separate certificate/key pair will not be generated for this format. Postfix, Dovecot and Nginx will then serve these certificates with SNI. Set ENABLE_SSL_SNI=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Warning Not all clients support SNI, see Dovecot documentation or Wikipedia . You should make sure these clients use the MAILCOW_HOSTNAME for secure connections if you enable this feature. Here is an example: MAILCOW_HOSTNAME=server.email.tld ADDITIONAL_SAN=webmail.email.tld,mail.* Mailcow email domains: \"domain1.tld\" and \"domain2.tld\" The following certificates will be generated: server.email.tld, webmail.email.tld -> this is the default certificate, all clients can connect with these domains mail.domain1.tld, autoconfig.domain1.tld, autodiscover.domain1.tld -> individual certificate for domain1.tld, cannot be used by clients without SNI support mail.domain2.tld, autoconfig.domain2.tld, autodiscover.domain2.tld -> individual certificate for domain2.tld, cannot be used by clients without SNI support","title":"The Let's Encrypt subjectAltName limit of 100 domains"},{"location":"firststeps-ssl/#how-to-use-your-own-certificate","text":"Make sure you disable mailcows internal LE client (see above). To use your own certificates, just save the combined certificate (containing the certificate and intermediate CA/CA if any) to data/assets/ssl/cert.pem and the corresponding key to data/assets/ssl/key.pem . IMPORTANT: Do not use symbolic links! Make sure you copy the certificates and do not link them to data/assets/ssl . Restart affected services afterwards: docker restart $(docker ps -qaf name=postfix-mailcow) docker restart $(docker ps -qaf name=nginx-mailcow) docker restart $(docker ps -qaf name=dovecot-mailcow) See Post-hook script for non-mailcow ACME clients for a full example script.","title":"How to use your own certificate"},{"location":"firststeps-ssl/#test-against-staging-acme-directory","text":"Edit mailcow.conf and add LE_STAGING=y . Run docker-compose up -d to activate your changes.","title":"Test against staging ACME directory"},{"location":"firststeps-ssl/#custom-directory-url","text":"Edit mailcow.conf and add the corresponding directory URL to the new variable DIRECTORY_URL : DIRECTORY_URL=https://acme-custom-v9000.api.letsencrypt.org/directory You cannot use LE_STAGING with DIRECTORY_URL . If both are set, only LE_STAGING is used. Run docker-compose up -d to activate your changes.","title":"Custom directory URL"},{"location":"firststeps-ssl/#check-your-configuration","text":"Run docker-compose logs acme-mailcow to find out why a validation fails. To check if nginx serves the correct certificate, simply use a browser of your choice and check the displayed certificate. To check the certificate served by Postfix, Dovecot and Nginx we will use openssl : # Connect via SMTP (587) echo \"Q\" | openssl s_client -starttls smtp -crlf -connect mx.mailcow.email:587 # Connect via IMAP (143) echo \"Q\" | openssl s_client -starttls imap -showcerts -connect mx.mailcow.email:143 # Connect via HTTPS (443) echo \"Q\" | openssl s_client -connect mx.mailcow.email:443 To validate the expiry dates as returned by openssl against MAILCOW_HOSTNAME, you are able to use our helper script: cd /opt/mailcow-dockerized bash helper-scripts/expiry-dates.sh","title":"Check your configuration"},{"location":"firststeps-sync_jobs_migration/","text":"Sync jobs are used to copy or move existing emails from an external IMAP server or within mailcow's existing mailboxes. Info Depending on your mailbox's ACL you may not have the option to add a sync job. Please contact your domain administrator if so. Setup a Sync Job \u00b6 In the \"Mail Setup\" or \"User Settings\" interface, create a new sync job. If you are an administrator, select the username of the downstream mailcow mailbox in the \"Username\" dropdown. Fill in the \"Host\" and \"Port\" fields with their respective correct values from the upstream IMAP server. In the \"Username\" and \"Password\" fields, supply the correct access credentials from the upstream IMAP server. Select the \"Encryption Method\". If the upstream IMAP server uses port 143, it is likely that the encryption method is TLS and SSL for port 993. Nevertheless, you can use PLAIN authentication, but it is stongly discouraged. For all ther other fields, you can leave them as is or modify them as desired. Make sure to tick \"Active\" and click \"Add\". Info Once Completed, log into the mailbox and check if all emails are imported correctly. If all goes well, all your mails shall end up in your new mailbox. And don't forget to delete or deactivate the sync job after it is used.","title":"Sync job migration"},{"location":"firststeps-sync_jobs_migration/#setup-a-sync-job","text":"In the \"Mail Setup\" or \"User Settings\" interface, create a new sync job. If you are an administrator, select the username of the downstream mailcow mailbox in the \"Username\" dropdown. Fill in the \"Host\" and \"Port\" fields with their respective correct values from the upstream IMAP server. In the \"Username\" and \"Password\" fields, supply the correct access credentials from the upstream IMAP server. Select the \"Encryption Method\". If the upstream IMAP server uses port 143, it is likely that the encryption method is TLS and SSL for port 993. Nevertheless, you can use PLAIN authentication, but it is stongly discouraged. For all ther other fields, you can leave them as is or modify them as desired. Make sure to tick \"Active\" and click \"Add\". Info Once Completed, log into the mailbox and check if all emails are imported correctly. If all goes well, all your mails shall end up in your new mailbox. And don't forget to delete or deactivate the sync job after it is used.","title":"Setup a Sync Job"},{"location":"i_u_m_deinstall/","text":"To remove mailcow: dockerized with all it's volumes, images and containers do: docker-compose down -v --rmi all --remove-orphans Info -v Remove named volumes declared in the volumes section of the Compose file and anonymous volumes attached to containers. --rmi Remove images. Type must be one of: all : Remove all images used by any service. local : Remove only images that don't have a custom tag set by the image field. --remove-orphans Remove containers for services not defined in the compose file. By default docker-compose down only removes currently active containers and networks defined in the docker-compose.yml .","title":"Deinstallation"},{"location":"i_u_m_install/","text":"You need Docker (a version >= 20.10.2 is required) and Docker Compose (a version <= 2.0 is required). 1. Learn how to install Docker and Docker Compose . Quick installation for most operation systems: Docker curl -sSL https://get.docker.com/ | CHANNEL=stable sh # After the installation process is finished, you may need to enable the service and make sure it is started (e.g. CentOS 7) systemctl enable --now docker Docker-Compose Warning mailcow requires the latest version of docker-compose v1. It is highly recommended to use the commands below to install docker-compose . Package managers (e.g. apt , yum ) likely won't give you the correct version. Note: This command downloads docker-compose from the official Docker Github repository and is a safe method. The snippet will determine the latest supported version by mailcow. In almost all cases this is the latest version available (exceptions are broken releases or major changes not yet supported by mailcow). curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Please use the latest Docker engine available and do not use the engine that ships with your distros repository. 1.1. On SELinux enabled systems, e.g. CentOS 7: Check if \"container-selinux\" package is present on your system: rpm -qa | grep container-selinux If the above command returns an empty or no output, you should install it via your package manager. Check if docker has SELinux support enabled: docker info | grep selinux If the above command returns an empty or no output, create or edit /etc/docker/daemon.json and add \"selinux-enabled\": true . Example file content: { \"selinux-enabled\": true } Restart the docker daemon and verify SELinux is now enabled. This step is required to make sure mailcows volumes are properly labeled as declared in the compose file. If you are interested in how this works, you can check out the readme of https://github.com/containers/container-selinux which links to a lot of useful information on that topic. 2. Clone the master branch of the repository, make sure your umask equals 0022. Please clone the repository as root user and also control the stack as root. We will modify attributes - if necessary - while boostrapping the containers automatically and make sure everything is secured. The update.sh script must therefore also be run as root. It might be necessary to change ownership and other attributes of files you will otherwise not have access to. We drop permissions for every exposed application and will not run an exposed service as root! Controlling the Docker daemon as non-root user does not give you additional security. The unprivileged user will spawn the containers as root likewise. The behaviour of the stack is identical. $ su # umask 0022 # <- Verify it is 0022 # cd /opt # git clone https://github.com/mailcow/mailcow-dockerized # cd mailcow-dockerized 3. Generate a configuration file. Use a FQDN ( host.domain.tld ) as hostname when asked. ./generate_config.sh 4. Change configuration if you want or need to. nano mailcow.conf If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080. You may need to stop an existing pre-installed MTA which blocks port 25/tcp. See this chapter to learn how to reconfigure Postfix to run besides mailcow after a successful installation. Some updates modify mailcow.conf and add new parameters. It is hard to keep track of them in the documentation. Please check their description and, if unsure, ask at the known channels for advise. 4.1. Users with a MTU not equal to 1500 (e.g. OpenStack): Whenever you run into trouble and strange phenomena, please check your MTU. Edit docker-compose.yml and change the network settings according to your MTU. Add the new driver_opts parameter like this: networks: mailcow-network: ... driver_opts: com.docker.network.driver.mtu: 1450 ... 4.2. Users without an IPv6 enabled network on their host system: Enable IPv6. Finally. If you do not have an IPv6 enabled network on your host and you don't care for a better internet (thehe), it is recommended to disable IPv6 for the mailcow network to prevent unforeseen issues. 5. Pull the images and run the compose file. The parameter -d will start mailcow: dockerized detached: docker-compose pull docker-compose up -d Done! You can now access https://${MAILCOW_HOSTNAME} with the default credentials admin + password moohoo . Info If you are not using mailcow behind a reverse proxy, you should redirect all HTTP requests to HTTPS . The database will be initialized right after a connection to MySQL can be established. Your data will persist in multiple Docker volumes, that are not deleted when you recreate or delete containers. Run docker volume ls to see a list of all volumes. You can safely run docker-compose down without removing persistent data.","title":"Installation"},{"location":"i_u_m_migration/","text":"Warning This guide assumes you intend to migrate an existing mailcow server (source) over to a brand new, empty server (target). It takes no care about preserving any existing data on your target server and will erase anything within /var/lib/docker/volumes and thus any Docker volumes you may have already set up. Tip Alternatively, you can use the ./helper-scripts/backup_and_restore.sh script to create a full backup on the source machine, then install mailcow on the target machine as usual, copy over your mailcow.conf and use the same script to restore your backup to the target machine. 1. Install Docker and Docker Compose on your new server. Quick installation for most operation systems: Docker curl -sSL https://get.docker.com/ | CHANNEL=stable sh # After the installation process is finished, you may need to enable the service and make sure it is started (e.g. CentOS 7) systemctl enable docker.service docker-compose curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Please use the latest Docker engine available and do not use the engine that ships with your distros repository. 2. Stop Docker and assure Docker has stopped: systemctl stop docker.service systemctl status docker.service 3. Run the following commands on the source machine (take care of adding the trailing slashes in the first path parameter as shown below!) - WARNING: This command will erase anything that may already exist under /var/lib/docker/volumes on the target machine : rsync -aHhP --numeric-ids --delete /opt/mailcow-dockerized/ root@target-machine.example.com:/opt/mailcow-dockerized rsync -aHhP --numeric-ids --delete /var/lib/docker/volumes/ root@target-machine.example.com:/var/lib/docker/volumes 4. Shut down mailcow and stop Docker on the source machine. cd /opt/mailcow-dockerized docker-compose down systemctl stop docker.service 5. Repeat step 3 with the same commands. This will be much quicker than the first time. 6. Switch over to the target machine and start Docker. systemctl start docker.service 7. Now pull the mailcow Docker images on the target machine. cd /opt/mailcow-dockerized docker-compose pull 8. Start the whole mailcow stack and everything should be done! docker-compose up -d 9. Finally, change your DNS settings to point to the target server.","title":"Migration"},{"location":"i_u_m_update/","text":"Automatic update \u00b6 An update script in your mailcow-dockerized directory will take care of updates. But use it with caution! If you think you made a lot of changes to the mailcow code, you should use the manual update guide below. Run the update script: ./update.sh If it needs to, it will ask you how you wish to proceed. Merge errors will be reported. Some minor conflicts will be auto-corrected (in favour for the mailcow: dockerized repository code). Options \u00b6 # Options can be combined # - Check for updates and show changes ./update.sh --check # Do not try to update docker-compose, **make sure to use the latest docker-compose available** ./update.sh --no-update-compose # - Do not start mailcow after applying an update ./update.sh --skip-start # - Force update (unattended, but unsupported, use at own risk) ./update.sh --force # - Run garbage collector to cleanup old image tags and exit ./update.sh --gc # - Update with merge strategy option \"ours\" instead of \"theirs\" # This will **solve conflicts** when merging in favor for your local changes and should be avoided. Local changes will always be kept, unless we changed file XY, too. ./update.sh --ours # - Don't update, but prefetch images and exit ./update.sh --prefetch I forgot what I changed before running update.sh \u00b6 See git log --pretty=oneline | grep -i \"before update\" , you will have an output similar to this: 22cd00b5e28893ef9ddef3c2b5436453cc5223ab Before update on 2020-09-28_19_25_45 dacd4fb9b51e9e1c8a37d84485b92ffaf6c59353 Before update on 2020-08-07_13_31_31 Run git diff 22cd00b5e28893ef9ddef3c2b5436453cc5223ab to see what changed. Can I roll back? \u00b6 Yes. See the topic above, instead of a diff, you run checkout: docker-compose down # Replace commit ID 22cd00b5e28893ef9ddef3c2b5436453cc5223ab by your ID git checkout 22cd00b5e28893ef9ddef3c2b5436453cc5223ab docker-compose pull docker-compose up -d Hooks \u00b6 You can hook into the update mechanism by adding scripts called pre_commit_hook.sh and post_commit_hook.sh to your mailcows root directory. See this for more details. Footnotes \u00b6 There is no release cycle regarding updates.","title":"Update"},{"location":"i_u_m_update/#automatic-update","text":"An update script in your mailcow-dockerized directory will take care of updates. But use it with caution! If you think you made a lot of changes to the mailcow code, you should use the manual update guide below. Run the update script: ./update.sh If it needs to, it will ask you how you wish to proceed. Merge errors will be reported. Some minor conflicts will be auto-corrected (in favour for the mailcow: dockerized repository code).","title":"Automatic update"},{"location":"i_u_m_update/#options","text":"# Options can be combined # - Check for updates and show changes ./update.sh --check # Do not try to update docker-compose, **make sure to use the latest docker-compose available** ./update.sh --no-update-compose # - Do not start mailcow after applying an update ./update.sh --skip-start # - Force update (unattended, but unsupported, use at own risk) ./update.sh --force # - Run garbage collector to cleanup old image tags and exit ./update.sh --gc # - Update with merge strategy option \"ours\" instead of \"theirs\" # This will **solve conflicts** when merging in favor for your local changes and should be avoided. Local changes will always be kept, unless we changed file XY, too. ./update.sh --ours # - Don't update, but prefetch images and exit ./update.sh --prefetch","title":"Options"},{"location":"i_u_m_update/#i-forgot-what-i-changed-before-running-updatesh","text":"See git log --pretty=oneline | grep -i \"before update\" , you will have an output similar to this: 22cd00b5e28893ef9ddef3c2b5436453cc5223ab Before update on 2020-09-28_19_25_45 dacd4fb9b51e9e1c8a37d84485b92ffaf6c59353 Before update on 2020-08-07_13_31_31 Run git diff 22cd00b5e28893ef9ddef3c2b5436453cc5223ab to see what changed.","title":"I forgot what I changed before running update.sh"},{"location":"i_u_m_update/#can-i-roll-back","text":"Yes. See the topic above, instead of a diff, you run checkout: docker-compose down # Replace commit ID 22cd00b5e28893ef9ddef3c2b5436453cc5223ab by your ID git checkout 22cd00b5e28893ef9ddef3c2b5436453cc5223ab docker-compose pull docker-compose up -d","title":"Can I roll back?"},{"location":"i_u_m_update/#hooks","text":"You can hook into the update mechanism by adding scripts called pre_commit_hook.sh and post_commit_hook.sh to your mailcows root directory. See this for more details.","title":"Hooks"},{"location":"i_u_m_update/#footnotes","text":"There is no release cycle regarding updates.","title":"Footnotes"},{"location":"model-acl/","text":"Editing a domain administrator or a mailbox user allows to set restrictions to that account. Important : For overlapping modules like sync jobs, which both domain administrators and mailbox users can be granted access to, the domain administrators permissions are inherited, when logging in as mailbox user. Some examples: 1. A domain administror has not access to sync jobs but can login as mailbox user When logging in as mailbox user, he does not gain access to sync jobs, even if the given mailbox user has access when logging in directly 2. A domain administror has access to sync jobs and can login as mailbox user The mailbox user he tries to login as has not access to sync jobs The domain administrator, now logged in as mailbox user, inherits its permission to the mailbox user and can access sync jobs 3. A domain administrator logs in as mailbox user Every permission, that does not exist in a domain administrators ACL, is automatically granted (example: time-limited alias, TLS policy etc.)","title":"ACL"},{"location":"model-passwd/","text":"Fully supported hashing methods \u00b6 The most current mailcow fully supports the following hashing methods. The default hashing method is written in bold: BLF-CRYPT SSHA SSHA256 SSHA512 The methods above can be used in mailcow.conf as MAILCOW_PASS_SCHEME value. Read-only hashing methods \u00b6 The following methods are supported read only . If you plan to use SOGo (as per default), you need a SOGo compatible hashing method. Please see the note at the bottom of this page how to update the view if necessary. With SOGo disabled, all hashing methods below will be able to be read by mailcow and Dovecot. ARGON2I (SOGo compatible) ARGON2ID (SOGo compatible) CLEAR CLEARTEXT CRYPT (SOGo compatible) DES-CRYPT LDAP-MD5 (SOGo compatible) MD5 (SOGo compatible) MD5-CRYPT (SOGo compatible) PBKDF2 (SOGo compatible) PLAIN (SOGo compatible) PLAIN-MD4 PLAIN-MD5 PLAIN-TRUNC SHA (SOGo compatible) SHA1 (SOGo compatible) SHA256 (SOGo compatible) SHA256-CRYPT (SOGo compatible) SHA512 (SOGo compatible) SHA512-CRYPT (SOGo compatible) SMD5 (SOGo compatible) That means mailcow is able to verify users with a hash like {MD5}1a1dc91c907325c69271ddf0c944bc72 from the database. The value of MAILCOW_PASS_SCHEME will always be used to encrypt new passwords. I changed the password hashes in the \"mailbox\" SQL table and cannot login. A \"view\" needs to be updated. You can trigger this by restarting sogo-mailcow: docker-compose restart sogo-mailcow","title":"Password hashing"},{"location":"model-passwd/#fully-supported-hashing-methods","text":"The most current mailcow fully supports the following hashing methods. The default hashing method is written in bold: BLF-CRYPT SSHA SSHA256 SSHA512 The methods above can be used in mailcow.conf as MAILCOW_PASS_SCHEME value.","title":"Fully supported hashing methods"},{"location":"model-passwd/#read-only-hashing-methods","text":"The following methods are supported read only . If you plan to use SOGo (as per default), you need a SOGo compatible hashing method. Please see the note at the bottom of this page how to update the view if necessary. With SOGo disabled, all hashing methods below will be able to be read by mailcow and Dovecot. ARGON2I (SOGo compatible) ARGON2ID (SOGo compatible) CLEAR CLEARTEXT CRYPT (SOGo compatible) DES-CRYPT LDAP-MD5 (SOGo compatible) MD5 (SOGo compatible) MD5-CRYPT (SOGo compatible) PBKDF2 (SOGo compatible) PLAIN (SOGo compatible) PLAIN-MD4 PLAIN-MD5 PLAIN-TRUNC SHA (SOGo compatible) SHA1 (SOGo compatible) SHA256 (SOGo compatible) SHA256-CRYPT (SOGo compatible) SHA512 (SOGo compatible) SHA512-CRYPT (SOGo compatible) SMD5 (SOGo compatible) That means mailcow is able to verify users with a hash like {MD5}1a1dc91c907325c69271ddf0c944bc72 from the database. The value of MAILCOW_PASS_SCHEME will always be used to encrypt new passwords. I changed the password hashes in the \"mailbox\" SQL table and cannot login. A \"view\" needs to be updated. You can trigger this by restarting sogo-mailcow: docker-compose restart sogo-mailcow","title":"Read-only hashing methods"},{"location":"model-sender_rcv/","text":"When a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address. Mailbox me @example . org is created . example . org is a primary domain . Note : a mailbox cannot be created in an alias domain . me @example . org is only known as me @example . org . me @example . org is allowed to send as me @example . org . We can add an alias domain for example.org: Alias domain alias . com is added and assigned to primary domain example . org . me @example . org is now known as me @example . org and me @alias . com . me @example . org is now allowed to send as me @example . org and me @alias . com . We can add aliases for a mailbox to receive mail for and to send from this new address. It is important to know, that you are not able to receive mail for my-alias@my-alias-domain.tld . You would need to create this particular alias. me @example . org is assigned the alias alias @example . org me @example . org is now known as me @example . org , me @alias . com , alias @example . org me @example . org is NOT known as alias @alias . com . Please note that this does not apply to catch-all aliases: Alias domain alias . com is added and assigned to primary domain example . org me @example . org is assigned the catch - all alias @example . org me @example . org is still just known as me @example . org , which is the only available send - as option Any email send to alias . com will match the catch - all alias for example . org Administrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users (\"delegate\" them). You can choose between mailbox users or completely disable the sender check for domains. SOGo \"mail from\" addresses \u00b6 Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains. If you want to select another existing mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator needs to grant you access as described above.","title":"Sender and receiver model"},{"location":"model-sender_rcv/#sogo-mail-from-addresses","text":"Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains. If you want to select another existing mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator needs to grant you access as described above.","title":"SOGo \"mail from\" addresses"},{"location":"prerequisite-dns/","text":"Below you can find a list of recommended DNS records . While some are mandatory for a mail server (A, MX), others are recommended to build a good reputation score (TXT/SPF) or used for auto-configuration of mail clients (SRV). References \u00b6 A good article covering all relevant topics: \"3 DNS Records Every Email Marketer Must Know\" Another great one, but Zimbra as an example platform: \"Best Practices on Email Protection: SPF, DKIM and DMARC\" An in-depth discussion of SPF, DKIM and DMARC: \"How to eliminate spam and protect your name with DMARC\" A thorough guide on understanding DMARC: \"Demystifying DMARC: A guide to preventing email spoofing\" Reverse DNS of your IP address \u00b6 Make sure that the PTR record of your IP address matches the FQDN of your mailcow host: ${MAILCOW_HOSTNAME} 1 . This record is usually set at the provider you leased the IP address (server) from. The minimal DNS configuration \u00b6 This example shows you a set of records for one domain managed by mailcow. Each domain that is added to mailcow needs at least this set of records to function correctly. # Name Type Value mail IN A 1.2.3.4 autodiscover IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) autoconfig IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) @ IN MX 10 mail.example.org. (your ${MAILCOW_HOSTNAME}) DKIM, SPF and DMARC \u00b6 In the example DNS zone file snippet below, a simple SPF TXT record is used to only allow THIS server (the MX) to send mail for your domain. Every other server is disallowed but able to (\" ~all \"). Please refer to SPF Project for further reading. # Name Type Value @ IN TXT \"v=spf1 mx a -all\" It is highly recommended to create a DKIM TXT record in your mailcow UI and set the corresponding TXT record in your DNS records. Please refer to OpenDKIM for further reading. # Name Type Value dkim._domainkey IN TXT \"v=DKIM1; k=rsa; t=s; s=email; p=...\" The last step in protecting yourself and others is the implementation of a DMARC TXT record, for example by using the DMARC Assistant ( check ). # Name Type Value _dmarc IN TXT \"v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.org\" The advanced DNS configuration \u00b6 SRV records specify the server(s) for a specific protocol on your domain. If you want to explicitly announce a service as not provided, give \".\" as the target address (instead of \"mail.example.org.\"). Please refer to RFC 2782 . # Name Type Priority Weight Port Value _autodiscover._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN TXT \"path=/SOGo/dav/\" _carddavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _carddavs._tcp IN TXT \"path=/SOGo/dav/\" _imap._tcp IN SRV 0 1 143 mail.example.org. (your ${MAILCOW_HOSTNAME}) _imaps._tcp IN SRV 0 1 993 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3._tcp IN SRV 0 1 110 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3s._tcp IN SRV 0 1 995 mail.example.org. (your ${MAILCOW_HOSTNAME}) _sieve._tcp IN SRV 0 1 4190 mail.example.org. (your ${MAILCOW_HOSTNAME}) _smtps._tcp IN SRV 0 1 465 mail.example.org. (your ${MAILCOW_HOSTNAME}) _submission._tcp IN SRV 0 1 587 mail.example.org. (your ${MAILCOW_HOSTNAME}) Testing \u00b6 Here are some tools you can use to verify your DNS configuration: MX Toolbox (DNS, SMTP, RBL) port25.com (DKIM, SPF) Mail-tester (DKIM, DMARC, SPF) DMARC Analyzer (DMARC, SPF) MultiRBL.valli.org (DNSBL, RBL, FCrDNS) Misc \u00b6 Optional DMARC Statistics \u00b6 If you are interested in statistics, you can additionally register with some of the many below DMARC statistic services - or self-host your own. Tip It is worth considering that if you request DMARC statistic reports to your mailcow server and your mailcow server is not configured correctly to receive these reports, you may not get accurate and complete results. Please consider using an alternative email domain for receiving DMARC reports. It is worth mentioning, that the following suggestions are not a comprehensive list of all services and tools available, but only a small few of the many choices. Postmaster Tool parsedmarc (self-hosted) Fraudmarc Postmark Dmarcian Tip These services may provide you with a TXT record you need to insert into your DNS records as the provider specifies. Please ensure you read the provider's documentation from the service you choose as this process may vary. Email test for SPF, DKIM and DMARC: \u00b6 To run a rudimentary email authentication check, send a mail to check-auth at verifier.port25.com and wait for a reply. You will find a report similar to the following: ========================================================== Summary of Results ========================================================== SPF check: pass \"iprev\" check: pass DKIM check: pass DKIM check: pass SpamAssassin check: ham ========================================================== Details: ========================================================== .... The full report will contain more technical details. Fully Qualified Domain Name (FQDN) \u00b6 A Fully Qualified Domain Name ( FQDN ) is the complete (absolute) domain name for a specific computer or host, on the Internet. The FQDN consists of at least three parts divided by a dot: the hostname, the domain name, and the Top Level Domain ( TLD for short). In the example of mx.mailcow.email the hostname would be mx , the domain name mailcow and the TLD email . \u21a9","title":"DNS setup"},{"location":"prerequisite-dns/#references","text":"A good article covering all relevant topics: \"3 DNS Records Every Email Marketer Must Know\" Another great one, but Zimbra as an example platform: \"Best Practices on Email Protection: SPF, DKIM and DMARC\" An in-depth discussion of SPF, DKIM and DMARC: \"How to eliminate spam and protect your name with DMARC\" A thorough guide on understanding DMARC: \"Demystifying DMARC: A guide to preventing email spoofing\"","title":"References"},{"location":"prerequisite-dns/#reverse-dns-of-your-ip-address","text":"Make sure that the PTR record of your IP address matches the FQDN of your mailcow host: ${MAILCOW_HOSTNAME} 1 . This record is usually set at the provider you leased the IP address (server) from.","title":"Reverse DNS of your IP address"},{"location":"prerequisite-dns/#the-minimal-dns-configuration","text":"This example shows you a set of records for one domain managed by mailcow. Each domain that is added to mailcow needs at least this set of records to function correctly. # Name Type Value mail IN A 1.2.3.4 autodiscover IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) autoconfig IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) @ IN MX 10 mail.example.org. (your ${MAILCOW_HOSTNAME})","title":"The minimal DNS configuration"},{"location":"prerequisite-dns/#dkim-spf-and-dmarc","text":"In the example DNS zone file snippet below, a simple SPF TXT record is used to only allow THIS server (the MX) to send mail for your domain. Every other server is disallowed but able to (\" ~all \"). Please refer to SPF Project for further reading. # Name Type Value @ IN TXT \"v=spf1 mx a -all\" It is highly recommended to create a DKIM TXT record in your mailcow UI and set the corresponding TXT record in your DNS records. Please refer to OpenDKIM for further reading. # Name Type Value dkim._domainkey IN TXT \"v=DKIM1; k=rsa; t=s; s=email; p=...\" The last step in protecting yourself and others is the implementation of a DMARC TXT record, for example by using the DMARC Assistant ( check ). # Name Type Value _dmarc IN TXT \"v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.org\"","title":"DKIM, SPF and DMARC"},{"location":"prerequisite-dns/#the-advanced-dns-configuration","text":"SRV records specify the server(s) for a specific protocol on your domain. If you want to explicitly announce a service as not provided, give \".\" as the target address (instead of \"mail.example.org.\"). Please refer to RFC 2782 . # Name Type Priority Weight Port Value _autodiscover._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN TXT \"path=/SOGo/dav/\" _carddavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _carddavs._tcp IN TXT \"path=/SOGo/dav/\" _imap._tcp IN SRV 0 1 143 mail.example.org. (your ${MAILCOW_HOSTNAME}) _imaps._tcp IN SRV 0 1 993 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3._tcp IN SRV 0 1 110 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3s._tcp IN SRV 0 1 995 mail.example.org. (your ${MAILCOW_HOSTNAME}) _sieve._tcp IN SRV 0 1 4190 mail.example.org. (your ${MAILCOW_HOSTNAME}) _smtps._tcp IN SRV 0 1 465 mail.example.org. (your ${MAILCOW_HOSTNAME}) _submission._tcp IN SRV 0 1 587 mail.example.org. (your ${MAILCOW_HOSTNAME})","title":"The advanced DNS configuration"},{"location":"prerequisite-dns/#testing","text":"Here are some tools you can use to verify your DNS configuration: MX Toolbox (DNS, SMTP, RBL) port25.com (DKIM, SPF) Mail-tester (DKIM, DMARC, SPF) DMARC Analyzer (DMARC, SPF) MultiRBL.valli.org (DNSBL, RBL, FCrDNS)","title":"Testing"},{"location":"prerequisite-dns/#misc","text":"","title":"Misc"},{"location":"prerequisite-dns/#optional-dmarc-statistics","text":"If you are interested in statistics, you can additionally register with some of the many below DMARC statistic services - or self-host your own. Tip It is worth considering that if you request DMARC statistic reports to your mailcow server and your mailcow server is not configured correctly to receive these reports, you may not get accurate and complete results. Please consider using an alternative email domain for receiving DMARC reports. It is worth mentioning, that the following suggestions are not a comprehensive list of all services and tools available, but only a small few of the many choices. Postmaster Tool parsedmarc (self-hosted) Fraudmarc Postmark Dmarcian Tip These services may provide you with a TXT record you need to insert into your DNS records as the provider specifies. Please ensure you read the provider's documentation from the service you choose as this process may vary.","title":"Optional DMARC Statistics"},{"location":"prerequisite-dns/#email-test-for-spf-dkim-and-dmarc","text":"To run a rudimentary email authentication check, send a mail to check-auth at verifier.port25.com and wait for a reply. You will find a report similar to the following: ========================================================== Summary of Results ========================================================== SPF check: pass \"iprev\" check: pass DKIM check: pass DKIM check: pass SpamAssassin check: ham ========================================================== Details: ========================================================== .... The full report will contain more technical details.","title":"Email test for SPF, DKIM and DMARC:"},{"location":"prerequisite-dns/#fully-qualified-domain-name-fqdn","text":"A Fully Qualified Domain Name ( FQDN ) is the complete (absolute) domain name for a specific computer or host, on the Internet. The FQDN consists of at least three parts divided by a dot: the hostname, the domain name, and the Top Level Domain ( TLD for short). In the example of mx.mailcow.email the hostname would be mx , the domain name mailcow and the TLD email . \u21a9","title":"Fully Qualified Domain Name (FQDN)"},{"location":"prerequisite-system/","text":"Before you run mailcow: dockerized , there are a few requirements that you should check: Warning Do not try to install mailcow on a Synology/QNAP device (any NAS), OpenVZ, LXC or other container platforms. KVM, ESX, Hyper-V and other full virtualization platforms are supported. Info mailcow: dockerized requires some ports to be open for incoming connections, so make sure that your firewall is not blocking these. Make sure that no other application is interfering with mailcow's configuration, such as another mail service A correct DNS setup is crucial to every good mailserver setup, so please make sure you got at least the basics covered before you begin! Make sure that your system has a correct date and time setup . This is crucial for various components like two factor TOTP authentication. Minimum System Resources \u00b6 OpenVZ, Virtuozzo and LXC are not supported . Please make sure that your system has at least the following resources: Resource mailcow: dockerized CPU 1 GHz RAM Minimum 6 GiB + 1 GiB swap (default config) Disk 20 GiB (without emails) System Type x86_64 We recommend using any distribution listed as supported by Docker CE (check https://docs.docker.com/install/ ). We test on CentOS 7, Debian 9/10 and Ubuntu 18.04/20.04. ClamAV and Solr can be greedy with RAM. You may disable them in mailcow.conf by settings SKIP_CLAMD=y and SKIP_SOLR=y . Info : We are aware that a pure MTA can run on 128 MiB RAM. mailcow is a full-grown and ready-to-use groupware with many extras making life easier. mailcow comes with a webserver, webmailer, ActiveSync (MS), antivirus, antispam, indexing (Solr), document scanner (Oletools), SQL (MariaDB), Cache (Redis), MDA, MTA, various web services etc. A single SOGo worker can acquire ~350 MiB RAM before it gets purged. The more ActiveSync connections you plan to use, the more RAM you will need. A default configuration spawns 20 workers. Usage examples \u00b6 A company with 15 phones (EAS enabled) and about 50 concurrent IMAP connections should plan 16 GiB RAM. 6 GiB RAM + 1 GiB swap are fine for most private installations while 8 GiB RAM are recommended for ~5 to 10 users. We can help to correctly plan your setup as part of our support. Firewall & Ports \u00b6 Please check if any of mailcow's standard ports are open and not in use by other applications: ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' # or: netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' Warning There are several problems with running mailcow on a firewalld/ufw enabled system. You should disable it (if possible) and move your ruleset to the DOCKER-USER chain, which is not cleared by a Docker service restart, instead. See this (blog.donnex.net) or this (unrouted.io) guide for information about how to use iptables-persistent with the DOCKER-USER chain. As mailcow runs dockerized, INPUT rules have no effect on restricting access to mailcow. Use the FORWARD chain instead. If this command returns any results please remove or stop the application running on that port. You may also adjust mailcows ports via the mailcow.conf configuration file. Default Ports \u00b6 If you have a firewall in front of mailcow, please make sure that these ports are open for incoming connections: Service Protocol Port Container Variable Postfix SMTP TCP 25 postfix-mailcow ${SMTP_PORT} Postfix SMTPS TCP 465 postfix-mailcow ${SMTPS_PORT} Postfix Submission TCP 587 postfix-mailcow ${SUBMISSION_PORT} Dovecot IMAP TCP 143 dovecot-mailcow ${IMAP_PORT} Dovecot IMAPS TCP 993 dovecot-mailcow ${IMAPS_PORT} Dovecot POP3 TCP 110 dovecot-mailcow ${POP_PORT} Dovecot POP3S TCP 995 dovecot-mailcow ${POPS_PORT} Dovecot ManageSieve TCP 4190 dovecot-mailcow ${SIEVE_PORT} HTTP(S) TCP 80/443 nginx-mailcow ${HTTP_PORT} / ${HTTPS_PORT} To bind a service to an IP address, you can prepend the IP like this: SMTP_PORT=1.2.3.4:25 Important : You cannot use IP:PORT bindings in HTTP_PORT and HTTPS_PORT. Please use HTTP_PORT=1234 and HTTP_BIND=1.2.3.4 instead. Important for Hetzner firewalls \u00b6 Quoting https://github.com/chermsen via https://github.com/mailcow/mailcow-dockerized/issues/497#issuecomment-469847380 (THANK YOU!): For all who are struggling with the Hetzner firewall: Port 53 unimportant for the firewall configuration in this case. According to the documentation unbound uses the port range 1024-65535 for outgoing requests. Since the Hetzner Robot Firewall is a static firewall (each incoming packet is checked isolated) - the following rules must be applied: For TCP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: tcp TCP flags: ack Action: Accept For UDP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: udp Action: Accept If you want to apply a more restrictive port range you have to change the config of unbound first (after installation): {mailcow-dockerized}/data/conf/unbound/unbound.conf: outgoing-port-avoid: 0-32767 Now the firewall rules can be adjusted as follows: [...] DST Port: 32768-65535 [...] Date and Time \u00b6 To ensure that you have the correct date and time setup on your system, please check the output of timedatectl status : $ timedatectl status Local time: Sat 2017-05-06 02:12:33 CEST Universal time: Sat 2017-05-06 00:12:33 UTC RTC time: Sat 2017-05-06 00:12:32 Time zone: Europe/Berlin (CEST, +0200) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: yes Last DST change: DST began at Sun 2017-03-26 01:59:59 CET Sun 2017-03-26 03:00:00 CEST Next DST change: DST ends (the clock jumps one hour backwards) at Sun 2017-10-29 02:59:59 CEST Sun 2017-10-29 02:00:00 CET The lines NTP enabled: yes and NTP synchronized: yes indicate whether you have NTP enabled and if it's synchronized. To enable NTP you need to run the command timedatectl set-ntp true . You also need to edit your /etc/systemd/timesyncd.conf : # vim /etc/systemd/timesyncd.conf [Time] Servers=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org Hetzner Cloud (and probably others) \u00b6 Check /etc/network/interfaces.d/50-cloud-init.cfg and change the IPv6 interface from eth0:0 to eth0: # Wrong: auto eth0:0 iface eth0:0 inet6 static # Right: auto eth0 iface eth0 inet6 static Reboot or restart the interface. You may want to disable cloud-init network changes. MTU \u00b6 Especially relevant for OpenStack users: Check your MTU and set it accordingly in docker-compose.yml. See 4.1 in our installation docs .","title":"Prepare your system"},{"location":"prerequisite-system/#minimum-system-resources","text":"OpenVZ, Virtuozzo and LXC are not supported . Please make sure that your system has at least the following resources: Resource mailcow: dockerized CPU 1 GHz RAM Minimum 6 GiB + 1 GiB swap (default config) Disk 20 GiB (without emails) System Type x86_64 We recommend using any distribution listed as supported by Docker CE (check https://docs.docker.com/install/ ). We test on CentOS 7, Debian 9/10 and Ubuntu 18.04/20.04. ClamAV and Solr can be greedy with RAM. You may disable them in mailcow.conf by settings SKIP_CLAMD=y and SKIP_SOLR=y . Info : We are aware that a pure MTA can run on 128 MiB RAM. mailcow is a full-grown and ready-to-use groupware with many extras making life easier. mailcow comes with a webserver, webmailer, ActiveSync (MS), antivirus, antispam, indexing (Solr), document scanner (Oletools), SQL (MariaDB), Cache (Redis), MDA, MTA, various web services etc. A single SOGo worker can acquire ~350 MiB RAM before it gets purged. The more ActiveSync connections you plan to use, the more RAM you will need. A default configuration spawns 20 workers.","title":"Minimum System Resources"},{"location":"prerequisite-system/#usage-examples","text":"A company with 15 phones (EAS enabled) and about 50 concurrent IMAP connections should plan 16 GiB RAM. 6 GiB RAM + 1 GiB swap are fine for most private installations while 8 GiB RAM are recommended for ~5 to 10 users. We can help to correctly plan your setup as part of our support.","title":"Usage examples"},{"location":"prerequisite-system/#firewall-ports","text":"Please check if any of mailcow's standard ports are open and not in use by other applications: ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' # or: netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' Warning There are several problems with running mailcow on a firewalld/ufw enabled system. You should disable it (if possible) and move your ruleset to the DOCKER-USER chain, which is not cleared by a Docker service restart, instead. See this (blog.donnex.net) or this (unrouted.io) guide for information about how to use iptables-persistent with the DOCKER-USER chain. As mailcow runs dockerized, INPUT rules have no effect on restricting access to mailcow. Use the FORWARD chain instead. If this command returns any results please remove or stop the application running on that port. You may also adjust mailcows ports via the mailcow.conf configuration file.","title":"Firewall & Ports"},{"location":"prerequisite-system/#default-ports","text":"If you have a firewall in front of mailcow, please make sure that these ports are open for incoming connections: Service Protocol Port Container Variable Postfix SMTP TCP 25 postfix-mailcow ${SMTP_PORT} Postfix SMTPS TCP 465 postfix-mailcow ${SMTPS_PORT} Postfix Submission TCP 587 postfix-mailcow ${SUBMISSION_PORT} Dovecot IMAP TCP 143 dovecot-mailcow ${IMAP_PORT} Dovecot IMAPS TCP 993 dovecot-mailcow ${IMAPS_PORT} Dovecot POP3 TCP 110 dovecot-mailcow ${POP_PORT} Dovecot POP3S TCP 995 dovecot-mailcow ${POPS_PORT} Dovecot ManageSieve TCP 4190 dovecot-mailcow ${SIEVE_PORT} HTTP(S) TCP 80/443 nginx-mailcow ${HTTP_PORT} / ${HTTPS_PORT} To bind a service to an IP address, you can prepend the IP like this: SMTP_PORT=1.2.3.4:25 Important : You cannot use IP:PORT bindings in HTTP_PORT and HTTPS_PORT. Please use HTTP_PORT=1234 and HTTP_BIND=1.2.3.4 instead.","title":"Default Ports"},{"location":"prerequisite-system/#important-for-hetzner-firewalls","text":"Quoting https://github.com/chermsen via https://github.com/mailcow/mailcow-dockerized/issues/497#issuecomment-469847380 (THANK YOU!): For all who are struggling with the Hetzner firewall: Port 53 unimportant for the firewall configuration in this case. According to the documentation unbound uses the port range 1024-65535 for outgoing requests. Since the Hetzner Robot Firewall is a static firewall (each incoming packet is checked isolated) - the following rules must be applied: For TCP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: tcp TCP flags: ack Action: Accept For UDP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: udp Action: Accept If you want to apply a more restrictive port range you have to change the config of unbound first (after installation): {mailcow-dockerized}/data/conf/unbound/unbound.conf: outgoing-port-avoid: 0-32767 Now the firewall rules can be adjusted as follows: [...] DST Port: 32768-65535 [...]","title":"Important for Hetzner firewalls"},{"location":"prerequisite-system/#date-and-time","text":"To ensure that you have the correct date and time setup on your system, please check the output of timedatectl status : $ timedatectl status Local time: Sat 2017-05-06 02:12:33 CEST Universal time: Sat 2017-05-06 00:12:33 UTC RTC time: Sat 2017-05-06 00:12:32 Time zone: Europe/Berlin (CEST, +0200) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: yes Last DST change: DST began at Sun 2017-03-26 01:59:59 CET Sun 2017-03-26 03:00:00 CEST Next DST change: DST ends (the clock jumps one hour backwards) at Sun 2017-10-29 02:59:59 CEST Sun 2017-10-29 02:00:00 CET The lines NTP enabled: yes and NTP synchronized: yes indicate whether you have NTP enabled and if it's synchronized. To enable NTP you need to run the command timedatectl set-ntp true . You also need to edit your /etc/systemd/timesyncd.conf : # vim /etc/systemd/timesyncd.conf [Time] Servers=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org","title":"Date and Time"},{"location":"prerequisite-system/#hetzner-cloud-and-probably-others","text":"Check /etc/network/interfaces.d/50-cloud-init.cfg and change the IPv6 interface from eth0:0 to eth0: # Wrong: auto eth0:0 iface eth0:0 inet6 static # Right: auto eth0 iface eth0 inet6 static Reboot or restart the interface. You may want to disable cloud-init network changes.","title":"Hetzner Cloud (and probably others)"},{"location":"prerequisite-system/#mtu","text":"Especially relevant for OpenStack users: Check your MTU and set it accordingly in docker-compose.yml. See 4.1 in our installation docs .","title":"MTU"},{"location":"restrictions_ip_accss/","text":"WIP Protocol restrictions and IP access \u00b6 Denied access will be shown to the user as failed login attempts. Protocol restrictions in Dovecot \u00b6 Protocol restrictions work by filtering the passdb query for IMAP and POP3 as well as reading the JSON value for %s_access where %s reflects the protocol seen by Dovecot. In the future we may use virtual colums in SQL to add an index on these values. Protocol restrictions in Postfix \u00b6 Filtering SMTP protocol access works by using a check_sasl_map in the smtpd_recipient_restrictions.","title":"Restrictions ip accss"},{"location":"restrictions_ip_accss/#protocol-restrictions-and-ip-access","text":"Denied access will be shown to the user as failed login attempts.","title":"Protocol restrictions and IP access"},{"location":"restrictions_ip_accss/#protocol-restrictions-in-dovecot","text":"Protocol restrictions work by filtering the passdb query for IMAP and POP3 as well as reading the JSON value for %s_access where %s reflects the protocol seen by Dovecot. In the future we may use virtual colums in SQL to add an index on these values.","title":"Protocol restrictions in Dovecot"},{"location":"restrictions_ip_accss/#protocol-restrictions-in-postfix","text":"Filtering SMTP protocol access works by using a check_sasl_map in the smtpd_recipient_restrictions.","title":"Protocol restrictions in Postfix"},{"location":"third_party-borgmatic/","text":"Borgmatic Backup \u00b6 Introduction \u00b6 Borgmatic is a great way to run backups on your Mailcow setup as it securely encrypts your data and is extremely easy to set up. Due to it's deduplication capabilities you can store a great number of backups without wasting large amounts of disk space. This allows you to run backups in very short intervals to ensure minimal data loss when the need arises to recover data from a backup. This document guides you through the process to enable continuous backups for mailcow with borgmatic. The borgmatic functionality is provided by the borgmatic Docker image by b3vis . Check out the README in that repository to find out about the other options (such as push notifications) that are available. This guide only covers the basics. Setting up borgmatic \u00b6 Create or amend docker-compose.override.yml \u00b6 In the mailcow-dockerized root folder create or edit docker-compose.override.yml and insert the following configuration: version : '2.1' services : borgmatic-mailcow : image : b3vis/borgmatic restart : always dns : ${IPV4_NETWORK:-172.22.1}.254 volumes : - vmail-vol-1:/mnt/source/vmail:ro - crypt-vol-1:/mnt/source/crypt:ro - mysql-socket-vol-1:/var/run/mysqld/:z - ./data/conf/borgmatic/etc:/etc/borgmatic.d:Z - ./data/conf/borgmatic/state:/root/.config/borg:Z - ./data/conf/borgmatic/ssh:/root/.ssh:Z environment : - TZ=${TZ} - BORG_PASSPHRASE=YouBetterPutSomethingRealGoodHere networks : mailcow-network : aliases : - borgmatic Ensure that you change the BORG_PASSPHRASE to a secure passphrase of your choosing. For security reasons we mount the maildir as read-only. If you later want to restore data you will need to remove the ro flag prior to restoring the data. This is described in the section on restoring backups. Create data/conf/borgmatic/etc/config.yaml \u00b6 Next, we need to create the borgmatic configuration. source mailcow.conf cat < data/conf/borgmatic/etc/config.yaml location: source_directories: - /mnt/source repositories: - user@rsync.net:mailcow remote_path: borg1 retention: keep_hourly: 24 keep_daily: 7 keep_weekly: 4 keep_monthly: 6 hooks: mysql_databases: - name: ${DBNAME} username: ${DBUSER} password: ${DBPASS} options: --default-character-set=utf8mb4 EOF Creating the file in this way ensures the correct MySQL credentials are pulled in from mailcow.conf . This file is a minimal example for using borgmatic with an account user on the cloud storage provider rsync.net for a repository called mailcow (see repositories setting). It will backup both the maildir and MySQL database, which is all you should need to restore your mailcow setup after an incident. The retention settings will keep one archive for each hour of the past 24 hours, one per day of the week, one per week of the month and one per month of the past half year. Check the borgmatic documentation on how to use other types of repositories or configuration options. If you choose to use a local filesystem as a backup destination make sure to mount it into the container. The container defines a volume called /mnt/borg-repository for this purpose. Note If you do not use rsync.net you can most likely drop the remote_path element from your config. Create a crontab \u00b6 Create a new text file in data/conf/borgmatic/etc/crontab.txt with the following content: 14 * * * * PATH=$PATH:/usr/bin /usr/bin/borgmatic --stats -v 0 2>&1 This file expects crontab syntax. The example shown here will trigger the backup to run every hour at 14 minutes past the hour and log some nice stats at the end. Place SSH keys in folder \u00b6 Place the SSH keys you intend to use for remote repository connections in data/conf/borgmatic/ssh . OpenSSH expects the usual id_rsa , id_ed25519 or similar to be in this directory. Ensure the file is chmod 600 and not world readable or OpenSSH will refuse to use the SSH key. Bring up the container \u00b6 For the next step we need the container to be up and running in a configured state. To do that run: docker-compose up -d Initialize the repository \u00b6 By now your borgmatic container is up and running, but the backups will currently fail due to the repository not being initialized. To initialize the repository run: docker-compose exec borgmatic-mailcow borgmatic init --encryption repokey-blake2 You will be asked you to authenticate the SSH host key of your remote repository server. See if it matches and confirm the prompt by entering yes . The repository will be initialized with the passphrase you set in the BORG_PASSPHRASE environment variable earlier. When using any of the repokey encryption methods the encryption key will be stored in the repository itself and not on the client, so there is no further action required in this regard. If you decide to use a keyfile instead of a repokey make sure you export the key and back it up separately. Check the Exporting Keys section for how to retrieve the key. Restart container \u00b6 Now that we finished configuring and initializing the repository restart the container to ensure it is in a defined state: docker-compose restart borgmatic-mailcow Restoring from a backup \u00b6 Restoring a backup assumes you are starting off with a fresh installation of mailcow, and you currently do not have any custom data in your maildir or your mailcow database. Restore maildir \u00b6 Warning Doing this will overwrite files in your maildir! Do not run this unless you actually intend to recover mail files from a backup. If you use SELinux in Enforcing mode If you are using mailcow on a host with SELinux in Enforcing mode you will have to temporarily disable it during extraction of the archive as the mailcow setup labels the vmail volume as private, belonging to the dovecot container exclusively. SELinux will (rightfully) prevent any other container, such as the borgmatic container, from writing to this volume. Before running a restore you must make the vmail volume writeable in docker-compose.override.yml by removing the ro flag from the volume. Then you can use the following command to restore the maildir from a backup: docker-compose exec borgmatic-mailcow borgmatic extract --path mnt/source --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives ) Restore MySQL \u00b6 Warning Running this command will delete and recreate the mailcow database! Do not run this unless you actually intend to recover the mailcow database from a backup. To restore the MySQL database from the latest archive use this command: docker-compose exec borgmatic-mailcow borgmatic restore --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives ) After restoring \u00b6 After restoring you need to restart mailcow. If you disabled SELinux enforcing mode now would be a good time to re-enable it. To restart mailcow use the follwing command: docker-compose down && docker-compose up -d If you use SELinux this will also trigger the re-labeling of all files in your vmail volume. Be patient, as this may take a while if you have lots of files. Useful commands \u00b6 Manual archiving run (with debugging output) \u00b6 docker-compose exec borgmatic-mailcow borgmatic -v 2 Listing all available archives \u00b6 docker-compose exec borgmatic-mailcow borgmatic list Break lock \u00b6 When borg is interrupted during an archiving run it will leave behind a stale lock that needs to be cleared before any new operations can be performed: docker-compose exec borgmatic-mailcow borg break-lock user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository. Now would be a good time to do a manual archiving run to ensure it can be successfully performed. Exporting keys \u00b6 When using any of the keyfile methods for encryption you MUST take care of backing up the key files yourself. The key files are generated when you initialize the repository. The repokey methods store the key file within the repository, so a manual backup isn't as essential. Note that in either case you also must have the passphrase to decrypt any archives. To fetch the keyfile run: docker-compose exec borgmatic-mailcow borg key export --paper user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository.","title":"Borgmatic Backup"},{"location":"third_party-borgmatic/#borgmatic-backup","text":"","title":"Borgmatic Backup"},{"location":"third_party-borgmatic/#introduction","text":"Borgmatic is a great way to run backups on your Mailcow setup as it securely encrypts your data and is extremely easy to set up. Due to it's deduplication capabilities you can store a great number of backups without wasting large amounts of disk space. This allows you to run backups in very short intervals to ensure minimal data loss when the need arises to recover data from a backup. This document guides you through the process to enable continuous backups for mailcow with borgmatic. The borgmatic functionality is provided by the borgmatic Docker image by b3vis . Check out the README in that repository to find out about the other options (such as push notifications) that are available. This guide only covers the basics.","title":"Introduction"},{"location":"third_party-borgmatic/#setting-up-borgmatic","text":"","title":"Setting up borgmatic"},{"location":"third_party-borgmatic/#create-or-amend-docker-composeoverrideyml","text":"In the mailcow-dockerized root folder create or edit docker-compose.override.yml and insert the following configuration: version : '2.1' services : borgmatic-mailcow : image : b3vis/borgmatic restart : always dns : ${IPV4_NETWORK:-172.22.1}.254 volumes : - vmail-vol-1:/mnt/source/vmail:ro - crypt-vol-1:/mnt/source/crypt:ro - mysql-socket-vol-1:/var/run/mysqld/:z - ./data/conf/borgmatic/etc:/etc/borgmatic.d:Z - ./data/conf/borgmatic/state:/root/.config/borg:Z - ./data/conf/borgmatic/ssh:/root/.ssh:Z environment : - TZ=${TZ} - BORG_PASSPHRASE=YouBetterPutSomethingRealGoodHere networks : mailcow-network : aliases : - borgmatic Ensure that you change the BORG_PASSPHRASE to a secure passphrase of your choosing. For security reasons we mount the maildir as read-only. If you later want to restore data you will need to remove the ro flag prior to restoring the data. This is described in the section on restoring backups.","title":"Create or amend docker-compose.override.yml"},{"location":"third_party-borgmatic/#create-dataconfborgmaticetcconfigyaml","text":"Next, we need to create the borgmatic configuration. source mailcow.conf cat < data/conf/borgmatic/etc/config.yaml location: source_directories: - /mnt/source repositories: - user@rsync.net:mailcow remote_path: borg1 retention: keep_hourly: 24 keep_daily: 7 keep_weekly: 4 keep_monthly: 6 hooks: mysql_databases: - name: ${DBNAME} username: ${DBUSER} password: ${DBPASS} options: --default-character-set=utf8mb4 EOF Creating the file in this way ensures the correct MySQL credentials are pulled in from mailcow.conf . This file is a minimal example for using borgmatic with an account user on the cloud storage provider rsync.net for a repository called mailcow (see repositories setting). It will backup both the maildir and MySQL database, which is all you should need to restore your mailcow setup after an incident. The retention settings will keep one archive for each hour of the past 24 hours, one per day of the week, one per week of the month and one per month of the past half year. Check the borgmatic documentation on how to use other types of repositories or configuration options. If you choose to use a local filesystem as a backup destination make sure to mount it into the container. The container defines a volume called /mnt/borg-repository for this purpose. Note If you do not use rsync.net you can most likely drop the remote_path element from your config.","title":"Create data/conf/borgmatic/etc/config.yaml"},{"location":"third_party-borgmatic/#create-a-crontab","text":"Create a new text file in data/conf/borgmatic/etc/crontab.txt with the following content: 14 * * * * PATH=$PATH:/usr/bin /usr/bin/borgmatic --stats -v 0 2>&1 This file expects crontab syntax. The example shown here will trigger the backup to run every hour at 14 minutes past the hour and log some nice stats at the end.","title":"Create a crontab"},{"location":"third_party-borgmatic/#place-ssh-keys-in-folder","text":"Place the SSH keys you intend to use for remote repository connections in data/conf/borgmatic/ssh . OpenSSH expects the usual id_rsa , id_ed25519 or similar to be in this directory. Ensure the file is chmod 600 and not world readable or OpenSSH will refuse to use the SSH key.","title":"Place SSH keys in folder"},{"location":"third_party-borgmatic/#bring-up-the-container","text":"For the next step we need the container to be up and running in a configured state. To do that run: docker-compose up -d","title":"Bring up the container"},{"location":"third_party-borgmatic/#initialize-the-repository","text":"By now your borgmatic container is up and running, but the backups will currently fail due to the repository not being initialized. To initialize the repository run: docker-compose exec borgmatic-mailcow borgmatic init --encryption repokey-blake2 You will be asked you to authenticate the SSH host key of your remote repository server. See if it matches and confirm the prompt by entering yes . The repository will be initialized with the passphrase you set in the BORG_PASSPHRASE environment variable earlier. When using any of the repokey encryption methods the encryption key will be stored in the repository itself and not on the client, so there is no further action required in this regard. If you decide to use a keyfile instead of a repokey make sure you export the key and back it up separately. Check the Exporting Keys section for how to retrieve the key.","title":"Initialize the repository"},{"location":"third_party-borgmatic/#restart-container","text":"Now that we finished configuring and initializing the repository restart the container to ensure it is in a defined state: docker-compose restart borgmatic-mailcow","title":"Restart container"},{"location":"third_party-borgmatic/#restoring-from-a-backup","text":"Restoring a backup assumes you are starting off with a fresh installation of mailcow, and you currently do not have any custom data in your maildir or your mailcow database.","title":"Restoring from a backup"},{"location":"third_party-borgmatic/#restore-maildir","text":"Warning Doing this will overwrite files in your maildir! Do not run this unless you actually intend to recover mail files from a backup. If you use SELinux in Enforcing mode If you are using mailcow on a host with SELinux in Enforcing mode you will have to temporarily disable it during extraction of the archive as the mailcow setup labels the vmail volume as private, belonging to the dovecot container exclusively. SELinux will (rightfully) prevent any other container, such as the borgmatic container, from writing to this volume. Before running a restore you must make the vmail volume writeable in docker-compose.override.yml by removing the ro flag from the volume. Then you can use the following command to restore the maildir from a backup: docker-compose exec borgmatic-mailcow borgmatic extract --path mnt/source --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives )","title":"Restore maildir"},{"location":"third_party-borgmatic/#restore-mysql","text":"Warning Running this command will delete and recreate the mailcow database! Do not run this unless you actually intend to recover the mailcow database from a backup. To restore the MySQL database from the latest archive use this command: docker-compose exec borgmatic-mailcow borgmatic restore --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives )","title":"Restore MySQL"},{"location":"third_party-borgmatic/#after-restoring","text":"After restoring you need to restart mailcow. If you disabled SELinux enforcing mode now would be a good time to re-enable it. To restart mailcow use the follwing command: docker-compose down && docker-compose up -d If you use SELinux this will also trigger the re-labeling of all files in your vmail volume. Be patient, as this may take a while if you have lots of files.","title":"After restoring"},{"location":"third_party-borgmatic/#useful-commands","text":"","title":"Useful commands"},{"location":"third_party-borgmatic/#manual-archiving-run-with-debugging-output","text":"docker-compose exec borgmatic-mailcow borgmatic -v 2","title":"Manual archiving run (with debugging output)"},{"location":"third_party-borgmatic/#listing-all-available-archives","text":"docker-compose exec borgmatic-mailcow borgmatic list","title":"Listing all available archives"},{"location":"third_party-borgmatic/#break-lock","text":"When borg is interrupted during an archiving run it will leave behind a stale lock that needs to be cleared before any new operations can be performed: docker-compose exec borgmatic-mailcow borg break-lock user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository. Now would be a good time to do a manual archiving run to ensure it can be successfully performed.","title":"Break lock"},{"location":"third_party-borgmatic/#exporting-keys","text":"When using any of the keyfile methods for encryption you MUST take care of backing up the key files yourself. The key files are generated when you initialize the repository. The repokey methods store the key file within the repository, so a manual backup isn't as essential. Note that in either case you also must have the passphrase to decrypt any archives. To fetch the keyfile run: docker-compose exec borgmatic-mailcow borg key export --paper user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository.","title":"Exporting keys"},{"location":"third_party-exchange_onprem/","text":"Using Microsoft Exchange in a hybrid setup is possible with mailcow. With this setup you can add mailboxes on your mailcow and still use Exchange Online Protection . All mailboxes setup in Exchange will receive their mails as usual , while with the hybrid approach additional Mailboxes can be setup in mailcow without any further configuration. This setup becomes very handy if you have enabled the Office 365 security defaults and third party applications can no longer login into your mailboxes by any of the supported methods . Requirements \u00b6 The mx Record of your domain needs to point at the Exchange mail service. Log into your Admin center and look out for the dns settings of your domain to find your personalized gateway domain. It should look like this contoso-com.mail.protection.outlook.com . Contact your domain registrant to get further information on how to change mx record. The domain you want to have additional mailboxes for must be setup as internal relay domain in Exchange. Log in to your Exchange Admin Center Select the mail flow pane and click on accepted domains Select the domain and switch it from authorative to internal relay Set up the mailcow \u00b6 Your mailcow needs to relay all mails to your personalized Exchange Host. It is the same host address we already looked up for the mx Record. Add the domain to your mailcow Add your personalized Exchange Host address as relayhost Add your personalized Exchange Host address as forwarding host to unconditionally accepted all relayed mails from Exchange. (Admin > Configuration & Details > Configuration Dropdown > Forwarding Hosts) Go to the domain settings and select the newly added host on the Sender-dependent transports dropdown. Enable relaying by ticking the Relay this domain , Relay all recipients and the Relay non-existing mailboxes only. checkboxes Info From now on your mailcow will accept all mails relayed from Exchange. The inbound filtering and so the neural learning of your cow will no longer work . Because all mails are routed through Exchange the filtering process is handled there . Set up Connectors in Exchange \u00b6 All mail traffic now goes through Exchange. At this point the Exchange Online Protection already filters all incoming and outgoing mails. Now we need to set up two connectors to relay incoming mails from our Exchange Service to the mailcow and another one to allow mails relayed from the mailcow to our exchange service. You can follow the official guide from Microsoft . Warning For the connector that handles mails from your mailcow to Exchange Microsoft offers two ways of authenticating it. The recommended way is to use a tls certificate configured with a subject name that matches an accepted domain in Exchange. Otherwise you need to choose authentication with the static ip address of your mailcow. Validating \u00b6 The easiest way to validate the hybrid setup is by sending a mail from the internet to a mailbox that only exists on the mailcow and vice versa. Common Issues \u00b6 The connector validation from Exchange to your mailcow failed with 550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipient test@contoso.com not found by SMTP address lookup Possible Solution: Your domain is not set up as internal relay . Exchange therefore cannot find the recipient Mails sent from the mailcow to a mailbox in the internet cannot be sent. Non Delivery Report with error 550 5.7.64 TenantAttribution; Relay Access Denied Possible Solution: The authentication method failed. Make sure the certificate subject matches an accepted domain in Exchange. Try authenticating by static ip instead. Microsoft Guide for the connector setup and additional requirements: https://docs.microsoft.com/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail#prerequisites-for-your-on-premises-email-environment","title":"Exchange Hybrid Setup"},{"location":"third_party-exchange_onprem/#requirements","text":"The mx Record of your domain needs to point at the Exchange mail service. Log into your Admin center and look out for the dns settings of your domain to find your personalized gateway domain. It should look like this contoso-com.mail.protection.outlook.com . Contact your domain registrant to get further information on how to change mx record. The domain you want to have additional mailboxes for must be setup as internal relay domain in Exchange. Log in to your Exchange Admin Center Select the mail flow pane and click on accepted domains Select the domain and switch it from authorative to internal relay","title":"Requirements"},{"location":"third_party-exchange_onprem/#set-up-the-mailcow","text":"Your mailcow needs to relay all mails to your personalized Exchange Host. It is the same host address we already looked up for the mx Record. Add the domain to your mailcow Add your personalized Exchange Host address as relayhost Add your personalized Exchange Host address as forwarding host to unconditionally accepted all relayed mails from Exchange. (Admin > Configuration & Details > Configuration Dropdown > Forwarding Hosts) Go to the domain settings and select the newly added host on the Sender-dependent transports dropdown. Enable relaying by ticking the Relay this domain , Relay all recipients and the Relay non-existing mailboxes only. checkboxes Info From now on your mailcow will accept all mails relayed from Exchange. The inbound filtering and so the neural learning of your cow will no longer work . Because all mails are routed through Exchange the filtering process is handled there .","title":"Set up the mailcow"},{"location":"third_party-exchange_onprem/#set-up-connectors-in-exchange","text":"All mail traffic now goes through Exchange. At this point the Exchange Online Protection already filters all incoming and outgoing mails. Now we need to set up two connectors to relay incoming mails from our Exchange Service to the mailcow and another one to allow mails relayed from the mailcow to our exchange service. You can follow the official guide from Microsoft . Warning For the connector that handles mails from your mailcow to Exchange Microsoft offers two ways of authenticating it. The recommended way is to use a tls certificate configured with a subject name that matches an accepted domain in Exchange. Otherwise you need to choose authentication with the static ip address of your mailcow.","title":"Set up Connectors in Exchange"},{"location":"third_party-exchange_onprem/#validating","text":"The easiest way to validate the hybrid setup is by sending a mail from the internet to a mailbox that only exists on the mailcow and vice versa.","title":"Validating"},{"location":"third_party-exchange_onprem/#common-issues","text":"The connector validation from Exchange to your mailcow failed with 550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipient test@contoso.com not found by SMTP address lookup Possible Solution: Your domain is not set up as internal relay . Exchange therefore cannot find the recipient Mails sent from the mailcow to a mailbox in the internet cannot be sent. Non Delivery Report with error 550 5.7.64 TenantAttribution; Relay Access Denied Possible Solution: The authentication method failed. Make sure the certificate subject matches an accepted domain in Exchange. Try authenticating by static ip instead. Microsoft Guide for the connector setup and additional requirements: https://docs.microsoft.com/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail#prerequisites-for-your-on-premises-email-environment","title":"Common Issues"},{"location":"third_party-gitea/","text":"With Gitea' ability to authenticate over SMTP it is trivial to integrate it with mailcow. Few changes are needed: 1. Open docker-compose.override.yml and add gitea: version: '2.1' services: gitea-mailcow: image: gitea/gitea:1 volumes: - ./data/gitea:/data networks: mailcow-network: aliases: - gitea ports: - \"${GITEA_SSH_PORT:-127.0.0.1:4000}:22\" 2. Create data/conf/nginx/site.gitea.custom , add: location /gitea/ { proxy_pass http://gitea:3000/; } 3. Open mailcow.conf and define the binding you want gitea to use for SSH. Example: GITEA_SSH_PORT=127.0.0.1:4000 5. Run docker-compose up -d to bring up the gitea container and run docker-compose restart nginx-mailcow afterwards. 6. If you forced mailcow to https, execute step 9 and restart gitea with docker-compose restart gitea-mailcow . Go head with step 7 (Remember to use https instead of http, https://mx.example.org/gitea/ 7. Open http://${MAILCOW_HOSTNAME}/gitea/ , for example http://mx.example.org/gitea/ . For database details set mysql as database host. Use the value of DBNAME found in mailcow.conf as database name, DBUSER as database user and DBPASS as database password. 8. Once the installation is complete, login as admin and set \"settings\" -> \"authorization\" -> \"enable SMTP\". SMTP Host should be postfix with port 587 , set Skip TLS Verify as we are using an unlisted SAN (\"postfix\" is most likely not part of your certificate). 9. Create data/gitea/gitea/conf/app.ini and set following values. You can consult gitea cheat sheet for their meaning and other possible values. [server] SSH_LISTEN_PORT = 22 # For GITEA_SSH_PORT=127.0.0.1:4000 in mailcow.conf, set: SSH_DOMAIN = 127.0.0.1 SSH_PORT = 4000 # For MAILCOW_HOSTNAME=mx.example.org in mailcow.conf (and default ports for HTTPS), set: ROOT_URL = https://mx.example.org/gitea/ 10. Restart gitea with docker-compose restart gitea-mailcow . Your users should be able to login with mailcow managed accounts.","title":"Gitea"},{"location":"third_party-gogs/","text":"With Gogs' ability to authenticate over SMTP it is trivial to integrate it with mailcow. Few changes are needed: 1. Open docker-compose.override.yml and add Gogs: version: '2.1' services: gogs-mailcow: image: gogs/gogs volumes: - ./data/gogs:/data networks: mailcow-network: aliases: - gogs ports: - \"${GOGS_SSH_PORT:-127.0.0.1:4000}:22\" 2. Create data/conf/nginx/site.gogs.custom , add: location /gogs/ { proxy_pass http://gogs:3000/; } 3. Open mailcow.conf and define the binding you want Gogs to use for SSH. Example: GOGS_SSH_PORT=127.0.0.1:4000 5. Run docker-compose up -d to bring up the Gogs container and run docker-compose restart nginx-mailcow afterwards. 6. Open http://${MAILCOW_HOSTNAME}/gogs/ , for example http://mx.example.org/gogs/ . For database details set mysql as database host. Use the value of DBNAME found in mailcow.conf as database name, DBUSER as database user and DBPASS as database password. 7. Once the installation is complete, login as admin and set \"settings\" -> \"authorization\" -> \"enable SMTP\". SMTP Host should be postfix with port 587 , set Skip TLS Verify as we are using an unlisted SAN (\"postfix\" is most likely not part of your certificate). 8. Create data/gogs/gogs/conf/app.ini and set following values. You can consult Gogs cheat sheet for their meaning and other possible values. [server] SSH_LISTEN_PORT = 22 # For GOGS_SSH_PORT=127.0.0.1:4000 in mailcow.conf, set: SSH_DOMAIN = 127.0.0.1 SSH_PORT = 4000 # For MAILCOW_HOSTNAME=mx.example.org in mailcow.conf (and default ports for HTTPS), set: ROOT_URL = https://mx.example.org/gogs/ 9. Restart Gogs with docker-compose restart gogs-mailcow . Your users should be able to login with mailcow managed accounts.","title":"Gogs"},{"location":"third_party-mailman3/","text":"Installing Mailcow and Mailman3 based on dockerized versions \u00b6 This guide is a copy from dockerized-mailcow-mailman . Please post issues, questions and improvements in the issue tracker there. Introduction \u00b6 This guide aims to install and configure mailcow-dockerized with docker-mailman and to provide some useful scripts. An essential condition is, to preserve Mailcow and Mailman in their own installations for independent updates. There are some guides and projects on the internet, but they are not up to date and/or incomplete in documentation or configuration. This guide is based on the work of: mailcow-mailman3-dockerized by Shadowghost mailman-mailcow-integration After finishing this guide, mailcow-dockerized and docker-mailman will run and Apache as a reverse proxy will serve the web frontends. The operating system used is an Ubuntu 20.04 LTS . Disclaimer \u00b6 I'm not responsible for any data loss, hardware damage or broken keyboards. This guide comes without any warranty. Make backups before starting, 'coze: No backup no pity! Installation \u00b6 This guide ist based on different steps: DNS setup Install Apache as a reverse proxy Obtain ssl certificates with Let's Encrypt Install Mailcow with Mailman integration Install Mailman \ud83c\udfc3 Run DNS setup \u00b6 Most of the configuration ist covered by Mailcow s DNS setup . After finishing this setup add another subdomain for Mailman , e.g. lists.example.org that points to the same server: # Name Type Value lists IN A 1.2.3.4 lists IN AAAA dead:beef Install Apache as a reverse proxy \u00b6 Install Apache , e.g. with this guide from Digital Ocean : How To Install the Apache Web Server on Ubuntu 20.04 . Activate certain Apache modules (as root or sudo ): a2enmod rewrite proxy proxy_http headers ssl wsgi proxy_uwsgi http2 Maybe you have to install further packages to get these modules. This PPA by Ond\u0159ej Sur\u00fd may help you. vhost configuration \u00b6 Copy the mailcow.conf and the mailman.conf to the Apache conf folder sites-available (e.g. under /etc/apache2/sites-available ). Change in mailcow.conf : - MAILCOW_HOSTNAME to your MAILCOW_HOSTNAME Change in mailman.conf : - MAILMAN_DOMAIN to your Mailman domain (e.g. lists.example.org ) Don't activate the configuration, as the ssl certificates and directories are missing yet. Obtain ssl certificates with Let's Encrypt \u00b6 Check if your DNS config is available over the internet and points to the right IP addresses, e.g. with MXToolBox : https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILMAN_DOMAIN https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILMAN_DOMAIN Install certbot (as root or sudo ): apt install certbot Get the desired certificates (as root or sudo ): certbot certonly -d MAILCOW_HOSTNAME certbot certonly -d MAILMAN_DOMAIN Install Mailcow with Mailman integration \u00b6 install Mailcow \u00b6 Follow the Mailcow installation . Omit step 5 and do not pull and up with docker-compose ! configure Mailcow \u00b6 This is also Step 4 in the official Mailcow installation ( nano mailcow.conf ). So change to your needs and alter the following variables: HTTP_PORT=18080 # don't use 8080 as mailman needs it HTTP_BIND=127.0.0.1 # HTTPS_PORT=18443 # you may use 8443 HTTPS_BIND=127.0.0.1 # SKIP_LETS_ENCRYPT=y # reverse proxy will do the ssl termination SNAT_TO_SOURCE=1.2.3.4 # change this to your ipv4 SNAT6_TO_SOURCE=dead:beef # change this to your global ipv6 add Mailman integration \u00b6 Create the file /opt/mailcow-dockerized/docker-compose.override.yml (e.g. with nano ) and add the following lines: version: '2.1' services: postfix-mailcow: volumes: - /opt/mailman:/opt/mailman networks: - docker-mailman_mailman networks: docker-mailman_mailman: external: true The additional volume is used by Mailman to generate additional config files for Mailcow postfix . The external network is build and used by Mailman . Mailcow needs it to deliver incoming list mails to Mailman . dockerized-mailcow-mailman Create the file /opt/mailcow-dockerized/data/conf/postfix/extra.cf (e.g. with nano ) and add the following lines: # mailman recipient_delimiter = + unknown_local_recipient_reject_code = 550 owner_request_special = no local_recipient_maps = regexp:/opt/mailman/core/var/data/postfix_lmtp, proxy:unix:passwd.byname, $alias_maps virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre, pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf, regexp:/opt/mailman/core/var/data/postfix_domains relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp As we overwrite Mailcow postfix configuration here, this step may break your normal mail transports. Check the original configuration files if anything changed. ssl certificates \u00b6 As we proxying Mailcow , we need to copy the ssl certificates into the Mailcow file structure. This task will do the script renew-ssl.sh for us: copy the file to /opt/mailcow-dockerized change MAILCOW_HOSTNAME to your Mailcow hostname make it executable ( chmod a+x renew-ssl.sh ) do not run it yet, as we first need Mailman You have to create a cronjob , so that new certificates will be copied. Execute as root or sudo : crontab -e To run the script every day at 5am, add: 0 5 * * * /opt/mailcow-dockerized/renew-ssl.sh Install Mailman \u00b6 Basicly follow the instructions at docker-mailman . As they are a lot, here is in a nuthshell what to do: As root or sudo : cd /opt mkdir -p mailman/core mkdir -p mailman/web git clone https://github.com/maxking/docker-mailman cd docker-mailman configure Mailman \u00b6 Create a long key for Hyperkitty , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as HYPERKITTY_KEY. Create a long password for the database, e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this password for a moment as DBPASS. Create a long key for Django , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as DJANGO_KEY. Create the file /opt/docker-mailman/docker-compose.override.yaml and replace HYPERKITTY_KEY , DBPASS and DJANGO_KEY with the generated values: version: '2' services: mailman-core: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - MTA=postfix restart: always networks: - mailman mailman-web: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - SECRET_KEY=DJANGO_KEY - SERVE_FROM_DOMAIN=MAILMAN_DOMAIN # e.g. lists.example.org - MAILMAN_ADMIN_USER=admin # the admin user - MAILMAN_ADMIN_EMAIL=admin@example.org # the admin mail address - UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static restart: always database: environment: - POSTGRES_PASSWORD=DBPASS restart: always At mailman-web fill in correct values for SERVE_FROM_DOMAIN (e.g. lists.example.org ), MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL . You need the admin credentials to log into the web interface ( Pistorius ). For setting the password for the first time use the Forgot password function in the web interface. About other configuration options read Mailman-web and Mailman-core documentation. configure Mailman core and Mailman web \u00b6 Create the file /opt/mailman/core/mailman-extra.cfg with the following content. mailman@example.org should be pointing to a valid mail box or redirection. [mailman] default_language: de site_owner: mailman@example.org Create the file /opt/mailman/web/settings_local.py with the following content. mailman@example.org should be pointing to a valid mail box or redirection. # locale LANGUAGE_CODE = 'de-de' # disable social authentication SOCIALACCOUNT_PROVIDERS = {} # change it DEFAULT_FROM_EMAIL = 'mailman@example.org' DEBUG = False You can change LANGUAGE_CODE and SOCIALACCOUNT_PROVIDERS to your needs. At the moment SOCIALACCOUNT_PROVIDERS has no effect, see issue #2 . \ud83c\udfc3 Run \u00b6 Run (as root or sudo ) a2ensite mailcow.conf a2ensite mailman.conf systemctl restart apache2 cd /opt/docker-mailman docker-compose pull docker-compose up -d cd /opt/mailcow-dockerized/ docker-compose pull ./renew-ssl.sh Wait a few minutes! The containers have to create there databases and config files. This can last up to 1 minute and more. Remarks \u00b6 New lists aren't recognized by postfix instantly \u00b6 When you create a new list and try to immediately send an e-mail, postfix responses with User doesn't exist , because postfix won't deliver it to Mailman yet. The configuration at /opt/mailman/core/var/data/postfix_lmtp is not instantly updated. If you need the list instantly, restart postifx manually: cd /opt/mailcow-dockerized docker-compose restart postfix-mailcow Update \u00b6 Mailcow has it's own update script in `/opt/mailcow-dockerized/update.sh', see the docs . For Mailman just fetch the newest version from the github repository . Backup \u00b6 Mailcow has an own backup script. Read the docs for further informations. Mailman won't state backup instructions in the README.md. In the gitbucket of pgollor is a script that may be helpful. ToDo \u00b6 install script \u00b6 Write a script like in mailman-mailcow-integration/mailman-install.sh as many of the steps are automatable. Ask for all the configuration variables and create passwords and keys. Do a (semi-)automatic installation. Have fun!","title":"Mailman3"},{"location":"third_party-mailman3/#installing-mailcow-and-mailman3-based-on-dockerized-versions","text":"This guide is a copy from dockerized-mailcow-mailman . Please post issues, questions and improvements in the issue tracker there.","title":"Installing Mailcow and Mailman3 based on dockerized versions"},{"location":"third_party-mailman3/#introduction","text":"This guide aims to install and configure mailcow-dockerized with docker-mailman and to provide some useful scripts. An essential condition is, to preserve Mailcow and Mailman in their own installations for independent updates. There are some guides and projects on the internet, but they are not up to date and/or incomplete in documentation or configuration. This guide is based on the work of: mailcow-mailman3-dockerized by Shadowghost mailman-mailcow-integration After finishing this guide, mailcow-dockerized and docker-mailman will run and Apache as a reverse proxy will serve the web frontends. The operating system used is an Ubuntu 20.04 LTS .","title":"Introduction"},{"location":"third_party-mailman3/#disclaimer","text":"I'm not responsible for any data loss, hardware damage or broken keyboards. This guide comes without any warranty. Make backups before starting, 'coze: No backup no pity!","title":"Disclaimer"},{"location":"third_party-mailman3/#installation","text":"This guide ist based on different steps: DNS setup Install Apache as a reverse proxy Obtain ssl certificates with Let's Encrypt Install Mailcow with Mailman integration Install Mailman \ud83c\udfc3 Run","title":"Installation"},{"location":"third_party-mailman3/#dns-setup","text":"Most of the configuration ist covered by Mailcow s DNS setup . After finishing this setup add another subdomain for Mailman , e.g. lists.example.org that points to the same server: # Name Type Value lists IN A 1.2.3.4 lists IN AAAA dead:beef","title":"DNS setup"},{"location":"third_party-mailman3/#install-apache-as-a-reverse-proxy","text":"Install Apache , e.g. with this guide from Digital Ocean : How To Install the Apache Web Server on Ubuntu 20.04 . Activate certain Apache modules (as root or sudo ): a2enmod rewrite proxy proxy_http headers ssl wsgi proxy_uwsgi http2 Maybe you have to install further packages to get these modules. This PPA by Ond\u0159ej Sur\u00fd may help you.","title":"Install Apache as a reverse proxy"},{"location":"third_party-mailman3/#vhost-configuration","text":"Copy the mailcow.conf and the mailman.conf to the Apache conf folder sites-available (e.g. under /etc/apache2/sites-available ). Change in mailcow.conf : - MAILCOW_HOSTNAME to your MAILCOW_HOSTNAME Change in mailman.conf : - MAILMAN_DOMAIN to your Mailman domain (e.g. lists.example.org ) Don't activate the configuration, as the ssl certificates and directories are missing yet.","title":"vhost configuration"},{"location":"third_party-mailman3/#obtain-ssl-certificates-with-lets-encrypt","text":"Check if your DNS config is available over the internet and points to the right IP addresses, e.g. with MXToolBox : https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILMAN_DOMAIN https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILMAN_DOMAIN Install certbot (as root or sudo ): apt install certbot Get the desired certificates (as root or sudo ): certbot certonly -d MAILCOW_HOSTNAME certbot certonly -d MAILMAN_DOMAIN","title":"Obtain ssl certificates with Let's Encrypt"},{"location":"third_party-mailman3/#install-mailcow-with-mailman-integration","text":"","title":"Install Mailcow with Mailman integration"},{"location":"third_party-mailman3/#install-mailcow","text":"Follow the Mailcow installation . Omit step 5 and do not pull and up with docker-compose !","title":"install Mailcow"},{"location":"third_party-mailman3/#configure-mailcow","text":"This is also Step 4 in the official Mailcow installation ( nano mailcow.conf ). So change to your needs and alter the following variables: HTTP_PORT=18080 # don't use 8080 as mailman needs it HTTP_BIND=127.0.0.1 # HTTPS_PORT=18443 # you may use 8443 HTTPS_BIND=127.0.0.1 # SKIP_LETS_ENCRYPT=y # reverse proxy will do the ssl termination SNAT_TO_SOURCE=1.2.3.4 # change this to your ipv4 SNAT6_TO_SOURCE=dead:beef # change this to your global ipv6","title":"configure Mailcow"},{"location":"third_party-mailman3/#add-mailman-integration","text":"Create the file /opt/mailcow-dockerized/docker-compose.override.yml (e.g. with nano ) and add the following lines: version: '2.1' services: postfix-mailcow: volumes: - /opt/mailman:/opt/mailman networks: - docker-mailman_mailman networks: docker-mailman_mailman: external: true The additional volume is used by Mailman to generate additional config files for Mailcow postfix . The external network is build and used by Mailman . Mailcow needs it to deliver incoming list mails to Mailman . dockerized-mailcow-mailman Create the file /opt/mailcow-dockerized/data/conf/postfix/extra.cf (e.g. with nano ) and add the following lines: # mailman recipient_delimiter = + unknown_local_recipient_reject_code = 550 owner_request_special = no local_recipient_maps = regexp:/opt/mailman/core/var/data/postfix_lmtp, proxy:unix:passwd.byname, $alias_maps virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre, pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf, regexp:/opt/mailman/core/var/data/postfix_domains relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp As we overwrite Mailcow postfix configuration here, this step may break your normal mail transports. Check the original configuration files if anything changed.","title":"add Mailman integration"},{"location":"third_party-mailman3/#ssl-certificates","text":"As we proxying Mailcow , we need to copy the ssl certificates into the Mailcow file structure. This task will do the script renew-ssl.sh for us: copy the file to /opt/mailcow-dockerized change MAILCOW_HOSTNAME to your Mailcow hostname make it executable ( chmod a+x renew-ssl.sh ) do not run it yet, as we first need Mailman You have to create a cronjob , so that new certificates will be copied. Execute as root or sudo : crontab -e To run the script every day at 5am, add: 0 5 * * * /opt/mailcow-dockerized/renew-ssl.sh","title":"ssl certificates"},{"location":"third_party-mailman3/#install-mailman","text":"Basicly follow the instructions at docker-mailman . As they are a lot, here is in a nuthshell what to do: As root or sudo : cd /opt mkdir -p mailman/core mkdir -p mailman/web git clone https://github.com/maxking/docker-mailman cd docker-mailman","title":"Install Mailman"},{"location":"third_party-mailman3/#configure-mailman","text":"Create a long key for Hyperkitty , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as HYPERKITTY_KEY. Create a long password for the database, e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this password for a moment as DBPASS. Create a long key for Django , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as DJANGO_KEY. Create the file /opt/docker-mailman/docker-compose.override.yaml and replace HYPERKITTY_KEY , DBPASS and DJANGO_KEY with the generated values: version: '2' services: mailman-core: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - MTA=postfix restart: always networks: - mailman mailman-web: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - SECRET_KEY=DJANGO_KEY - SERVE_FROM_DOMAIN=MAILMAN_DOMAIN # e.g. lists.example.org - MAILMAN_ADMIN_USER=admin # the admin user - MAILMAN_ADMIN_EMAIL=admin@example.org # the admin mail address - UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static restart: always database: environment: - POSTGRES_PASSWORD=DBPASS restart: always At mailman-web fill in correct values for SERVE_FROM_DOMAIN (e.g. lists.example.org ), MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL . You need the admin credentials to log into the web interface ( Pistorius ). For setting the password for the first time use the Forgot password function in the web interface. About other configuration options read Mailman-web and Mailman-core documentation.","title":"configure Mailman"},{"location":"third_party-mailman3/#configure-mailman-core-and-mailman-web","text":"Create the file /opt/mailman/core/mailman-extra.cfg with the following content. mailman@example.org should be pointing to a valid mail box or redirection. [mailman] default_language: de site_owner: mailman@example.org Create the file /opt/mailman/web/settings_local.py with the following content. mailman@example.org should be pointing to a valid mail box or redirection. # locale LANGUAGE_CODE = 'de-de' # disable social authentication SOCIALACCOUNT_PROVIDERS = {} # change it DEFAULT_FROM_EMAIL = 'mailman@example.org' DEBUG = False You can change LANGUAGE_CODE and SOCIALACCOUNT_PROVIDERS to your needs. At the moment SOCIALACCOUNT_PROVIDERS has no effect, see issue #2 .","title":"configure Mailman core and Mailman web"},{"location":"third_party-mailman3/#run","text":"Run (as root or sudo ) a2ensite mailcow.conf a2ensite mailman.conf systemctl restart apache2 cd /opt/docker-mailman docker-compose pull docker-compose up -d cd /opt/mailcow-dockerized/ docker-compose pull ./renew-ssl.sh Wait a few minutes! The containers have to create there databases and config files. This can last up to 1 minute and more.","title":"\ud83c\udfc3 Run"},{"location":"third_party-mailman3/#remarks","text":"","title":"Remarks"},{"location":"third_party-mailman3/#new-lists-arent-recognized-by-postfix-instantly","text":"When you create a new list and try to immediately send an e-mail, postfix responses with User doesn't exist , because postfix won't deliver it to Mailman yet. The configuration at /opt/mailman/core/var/data/postfix_lmtp is not instantly updated. If you need the list instantly, restart postifx manually: cd /opt/mailcow-dockerized docker-compose restart postfix-mailcow","title":"New lists aren't recognized by postfix instantly"},{"location":"third_party-mailman3/#update","text":"Mailcow has it's own update script in `/opt/mailcow-dockerized/update.sh', see the docs . For Mailman just fetch the newest version from the github repository .","title":"Update"},{"location":"third_party-mailman3/#backup","text":"Mailcow has an own backup script. Read the docs for further informations. Mailman won't state backup instructions in the README.md. In the gitbucket of pgollor is a script that may be helpful.","title":"Backup"},{"location":"third_party-mailman3/#todo","text":"","title":"ToDo"},{"location":"third_party-mailman3/#install-script","text":"Write a script like in mailman-mailcow-integration/mailman-install.sh as many of the steps are automatable. Ask for all the configuration variables and create passwords and keys. Do a (semi-)automatic installation. Have fun!","title":"install script"},{"location":"third_party-mailpiler_integration/","text":"This is a simple integration of mailcow aliases and the mailbox name into mailpiler when using IMAP authentication. Disclaimer : This is not officially maintained nor supported by the mailcow project nor its contributors. No warranty or support is being provided, however you're free to open issues on GitHub for filing a bug or provide further ideas. GitHub repo can be found here . Info Support for domain wildcards were implemented in Piler 1.3.10 which was released on 03.01.2021. Prior versions basically do work, but after logging in you won't see emails sent from or to the domain alias. (e.g. when @example.com is an alias for admin@example.com ) The problem to solve \u00b6 mailpiler offers the authentication based on IMAP, for example: $config['ENABLE_IMAP_AUTH'] = 1; $config['IMAP_HOST'] = 'mail.example.com'; $config['IMAP_PORT'] = 993; $config['IMAP_SSL'] = true; So when you log in using patrik@example.com , you will only see delivered emails sent from or to this specific email address. When additional aliases are defined in mailcow, like team@example.com , you won't see emails sent to or from this email address even the fact you're a recipient of mails sent to this alias address. By hooking into the authentication process of mailpiler, we are able to get required data via the mailcow API during login. This fires API requests to the mailcow API (requiring read-only API access) to read out the aliases your email address participates and also the \"Name\" of the mailbox specified to display it on the top-right of mailpiler after login. Permitted email addresses can be seen in the mailpiler settings top-right after logging in. Info This is only pulled once during the authentication process. The authorized aliases and the realname are valid for the whole duration of the user session as mailpiler sets them in the session data. If user is removed from specific alias, this will only take effect after next login. The solution \u00b6 Note: File paths might vary depending on your setup. Requirements \u00b6 A working mailcow instance A working mailpiler instance ( You can find an installation guide here , check supported versions here ) An mailcow API key (read-only works just fine): Configuration & Details - Access - Read-Only Access . Don't forget to allow API access from your mailpiler IP. Warning As mailpiler authenticates against mailcow, our IMAP server, failed logins of users or bots might trigger a block for your mailpiler instance. Therefore you might want to consider whitelisting the IP address of the mailpiler instance within mailcow: Configuration & Details - Configuration - Fail2ban parameters - Whitelisted networks/hosts . Setup \u00b6 Set the custom query function of mailpiler and append this to /usr/local/etc/piler/config-site.php : $config['MAILCOW_API_KEY'] = 'YOUR_READONLY_API_KEY'; $config['MAILCOW_SET_REALNAME'] = true; // when not specified, then default is false $config['CUSTOM_EMAIL_QUERY_FUNCTION'] = 'query_mailcow_for_email_access'; include('auth-mailcow.php'); You can also change the mailcow hostname, if required: $config['MAILCOW_HOST'] = 'mail.domain.tld'; // defaults to $config['IMAP_HOST'] Download the PHP file with the functions from the GitHub repo : curl -o /usr/local/etc/piler/auth-mailcow.php https://raw.githubusercontent.com/patschi/mailpiler-mailcow-integration/master/auth-mailcow.php Done! Make sure to re-login with your IMAP credentials for changes to take effect. If it doesn't work, most likely something's wrong with the API query itself. Consider debugging by sending manual API requests to the API. (Tip: Open https://mail.domain.tld/api on your instance)","title":"Mailpiler Integration"},{"location":"third_party-mailpiler_integration/#the-problem-to-solve","text":"mailpiler offers the authentication based on IMAP, for example: $config['ENABLE_IMAP_AUTH'] = 1; $config['IMAP_HOST'] = 'mail.example.com'; $config['IMAP_PORT'] = 993; $config['IMAP_SSL'] = true; So when you log in using patrik@example.com , you will only see delivered emails sent from or to this specific email address. When additional aliases are defined in mailcow, like team@example.com , you won't see emails sent to or from this email address even the fact you're a recipient of mails sent to this alias address. By hooking into the authentication process of mailpiler, we are able to get required data via the mailcow API during login. This fires API requests to the mailcow API (requiring read-only API access) to read out the aliases your email address participates and also the \"Name\" of the mailbox specified to display it on the top-right of mailpiler after login. Permitted email addresses can be seen in the mailpiler settings top-right after logging in. Info This is only pulled once during the authentication process. The authorized aliases and the realname are valid for the whole duration of the user session as mailpiler sets them in the session data. If user is removed from specific alias, this will only take effect after next login.","title":"The problem to solve"},{"location":"third_party-mailpiler_integration/#the-solution","text":"Note: File paths might vary depending on your setup.","title":"The solution"},{"location":"third_party-mailpiler_integration/#requirements","text":"A working mailcow instance A working mailpiler instance ( You can find an installation guide here , check supported versions here ) An mailcow API key (read-only works just fine): Configuration & Details - Access - Read-Only Access . Don't forget to allow API access from your mailpiler IP. Warning As mailpiler authenticates against mailcow, our IMAP server, failed logins of users or bots might trigger a block for your mailpiler instance. Therefore you might want to consider whitelisting the IP address of the mailpiler instance within mailcow: Configuration & Details - Configuration - Fail2ban parameters - Whitelisted networks/hosts .","title":"Requirements"},{"location":"third_party-mailpiler_integration/#setup","text":"Set the custom query function of mailpiler and append this to /usr/local/etc/piler/config-site.php : $config['MAILCOW_API_KEY'] = 'YOUR_READONLY_API_KEY'; $config['MAILCOW_SET_REALNAME'] = true; // when not specified, then default is false $config['CUSTOM_EMAIL_QUERY_FUNCTION'] = 'query_mailcow_for_email_access'; include('auth-mailcow.php'); You can also change the mailcow hostname, if required: $config['MAILCOW_HOST'] = 'mail.domain.tld'; // defaults to $config['IMAP_HOST'] Download the PHP file with the functions from the GitHub repo : curl -o /usr/local/etc/piler/auth-mailcow.php https://raw.githubusercontent.com/patschi/mailpiler-mailcow-integration/master/auth-mailcow.php Done! Make sure to re-login with your IMAP credentials for changes to take effect. If it doesn't work, most likely something's wrong with the API query itself. Consider debugging by sending manual API requests to the API. (Tip: Open https://mail.domain.tld/api on your instance)","title":"Setup"},{"location":"third_party-nextcloud/","text":"Manage Nextcloud using the helper script \u00b6 Nextcloud can be set up (parameter -i ) and removed (parameter -p ) with the helper script included with mailcow. In order to install Nextcloud simply navigate to your mailcow-dockerized root folder and run the helper script as follows: ./helper-scripts/nextcloud.sh -i In case you have forgotten the password (e.g. for admin) and can't request a new one via the password reset link on the login screen calling the helper script with -r as parameter allows you to set a new password. Only use this option if your Nextcloud isn't configured to use mailcow for authentication as described in the next section. Configure Nextcloud to use mailcow for authentication \u00b6 The following describes how set up authentication via mailcow using the OAuth2 protocol. We will only assume that you have already set up Nextcloud at cloud.example.com and that your mailcow is running at mail.example.com . It does not matter if your Nextcloud is running on a different server, you can still use mailcow for authentication. 1. Log into mailcow as administrator. 2. Scroll down to OAuth2 Apps and click the Add button. Specify the redirect URI as https://cloud.example.com/index.php/apps/sociallogin/custom_oauth2/Mailcow and click Add . Save the client ID and secret for later. Info Some installations, including those setup using the helper script of mailcow, need to remove index.php/ from the URL to get a successful redirect: https://cloud.example.com/apps/sociallogin/custom_oauth2/Mailcow 3. Log into Nextcloud as administrator. 4. Click the button in the top right corner and select Apps . Click the search button in the toolbar, search for the Social Login plugin and click Download and enable next to it. 5. Click the button in the top right corner and select Settings . Scroll down to the Administration section on the left and click Social login . 6. Uncheck the following items: \"Disable auto create new users\" \"Allow users to connect social logins with their accounts\" \"Do not prune not available user groups on login\" \"Automatically create groups if they do not exists\" \"Restrict login for users without mapped groups\" 7. Check the following items: \"Prevent creating an account if the email address exists in another account\" \"Update user profile every login\" \"Disable notify admins about new users\" Click the Save button. 8. Scroll down to Custom OAuth2 and click the + button. 9. Configure the parameters as follows: Internal name: Mailcow Title: Mailcow API Base URL: https://mail.example.com Authorize URL: https://mail.example.com/oauth/authorize Token URL: https://mail.example.com/oauth/token Profile URL: https://mail.example.com/oauth/profile Logout URL: (leave blank) Client ID: (what you obtained in step 1) Client Secret: (what you obtained in step 1) Scope: profile Click the Save button at the very bottom of the page. If you have previously used Nextcloud with mailcow authentication via user_external/IMAP, you need to perform some additional steps to link your existing user accounts with OAuth2. 1. Click the button in the top right corner and select Apps . Scroll down to the External user authentication app and click Remove next to it. 2. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_users (uid, uid_lower) SELECT DISTINCT uid, LOWER(uid) FROM nc_users_external; INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users_external; If you have previously used Nextcloud without mailcow authentication, but with the same usernames as mailcow, you can also link your existing user accounts with OAuth2. 1. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users; Update \u00b6 The Nextcloud instance can be updated easily with the web update mechanism. In the case of larger updates, there may be further changes to be made after the update. After the Nextcloud instance has been checked, problems are shown. This can be e.g. missing indices in the DB or similar. It shows which commands have to be executed, these have to be placed in the php-fpm-mailcow container. As an an example run the following command to add the missing indices. docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c \"php /web/nextcloud/occ db:add-missing-indices\" Debugging & Troubleshooting \u00b6 It may happen that you cannot reach the Nextcloud instance from your network. This may be due to the fact that the entry of your subnet in the array 'trusted_proxies' is missing. You can make changes in the Nextcloud config.php in data/web/nextcloud/config/* . 'trusted_proxies' => array ( 0 => 'fd4d:6169:6c63:6f77::/64', 1 => '172.22.1.0/24', 2 => 'NewSubnet/24', ), After the changes have been made, the nginx container must be restarted. docker-compose restart nginx-mailcow","title":"Nextcloud"},{"location":"third_party-nextcloud/#manage-nextcloud-using-the-helper-script","text":"Nextcloud can be set up (parameter -i ) and removed (parameter -p ) with the helper script included with mailcow. In order to install Nextcloud simply navigate to your mailcow-dockerized root folder and run the helper script as follows: ./helper-scripts/nextcloud.sh -i In case you have forgotten the password (e.g. for admin) and can't request a new one via the password reset link on the login screen calling the helper script with -r as parameter allows you to set a new password. Only use this option if your Nextcloud isn't configured to use mailcow for authentication as described in the next section.","title":"Manage Nextcloud using the helper script"},{"location":"third_party-nextcloud/#configure-nextcloud-to-use-mailcow-for-authentication","text":"The following describes how set up authentication via mailcow using the OAuth2 protocol. We will only assume that you have already set up Nextcloud at cloud.example.com and that your mailcow is running at mail.example.com . It does not matter if your Nextcloud is running on a different server, you can still use mailcow for authentication. 1. Log into mailcow as administrator. 2. Scroll down to OAuth2 Apps and click the Add button. Specify the redirect URI as https://cloud.example.com/index.php/apps/sociallogin/custom_oauth2/Mailcow and click Add . Save the client ID and secret for later. Info Some installations, including those setup using the helper script of mailcow, need to remove index.php/ from the URL to get a successful redirect: https://cloud.example.com/apps/sociallogin/custom_oauth2/Mailcow 3. Log into Nextcloud as administrator. 4. Click the button in the top right corner and select Apps . Click the search button in the toolbar, search for the Social Login plugin and click Download and enable next to it. 5. Click the button in the top right corner and select Settings . Scroll down to the Administration section on the left and click Social login . 6. Uncheck the following items: \"Disable auto create new users\" \"Allow users to connect social logins with their accounts\" \"Do not prune not available user groups on login\" \"Automatically create groups if they do not exists\" \"Restrict login for users without mapped groups\" 7. Check the following items: \"Prevent creating an account if the email address exists in another account\" \"Update user profile every login\" \"Disable notify admins about new users\" Click the Save button. 8. Scroll down to Custom OAuth2 and click the + button. 9. Configure the parameters as follows: Internal name: Mailcow Title: Mailcow API Base URL: https://mail.example.com Authorize URL: https://mail.example.com/oauth/authorize Token URL: https://mail.example.com/oauth/token Profile URL: https://mail.example.com/oauth/profile Logout URL: (leave blank) Client ID: (what you obtained in step 1) Client Secret: (what you obtained in step 1) Scope: profile Click the Save button at the very bottom of the page. If you have previously used Nextcloud with mailcow authentication via user_external/IMAP, you need to perform some additional steps to link your existing user accounts with OAuth2. 1. Click the button in the top right corner and select Apps . Scroll down to the External user authentication app and click Remove next to it. 2. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_users (uid, uid_lower) SELECT DISTINCT uid, LOWER(uid) FROM nc_users_external; INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users_external; If you have previously used Nextcloud without mailcow authentication, but with the same usernames as mailcow, you can also link your existing user accounts with OAuth2. 1. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users;","title":"Configure Nextcloud to use mailcow for authentication"},{"location":"third_party-nextcloud/#update","text":"The Nextcloud instance can be updated easily with the web update mechanism. In the case of larger updates, there may be further changes to be made after the update. After the Nextcloud instance has been checked, problems are shown. This can be e.g. missing indices in the DB or similar. It shows which commands have to be executed, these have to be placed in the php-fpm-mailcow container. As an an example run the following command to add the missing indices. docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c \"php /web/nextcloud/occ db:add-missing-indices\"","title":"Update"},{"location":"third_party-nextcloud/#debugging-troubleshooting","text":"It may happen that you cannot reach the Nextcloud instance from your network. This may be due to the fact that the entry of your subnet in the array 'trusted_proxies' is missing. You can make changes in the Nextcloud config.php in data/web/nextcloud/config/* . 'trusted_proxies' => array ( 0 => 'fd4d:6169:6c63:6f77::/64', 1 => '172.22.1.0/24', 2 => 'NewSubnet/24', ), After the changes have been made, the nginx container must be restarted. docker-compose restart nginx-mailcow","title":"Debugging & Troubleshooting"},{"location":"third_party-portainer/","text":"In order to enable Portainer, the docker-compose.yml and site.conf for Nginx must be modified. 1. Create a new file docker-compose.override.yml in the mailcow-dockerized root folder and insert the following configuration version: '2.1' services: portainer-mailcow: image: portainer/portainer-ce volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data/conf/portainer:/data restart: always dns: - 172.22.1.254 dns_search: mailcow-network networks: mailcow-network: aliases: - portainer 2a. Create data/conf/nginx/portainer.conf : upstream portainer { server portainer-mailcow:9000; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } 2b. Insert a new location to the default mailcow site by creating the file data/conf/nginx/site.portainer.custom : location /portainer/ { proxy_http_version 1.1; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; proxy_set_header Connection \"\"; proxy_buffers 32 4k; proxy_pass http://portainer/; } location /portainer/api/websocket/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_pass http://portainer/api/websocket/; } 3. Apply your changes: docker-compose up -d && docker-compose restart nginx-mailcow Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You\u2019ll then be prompted to specify a new password for the admin account. After specifying your password, you\u2019ll then be able to connect to the Portainer UI.","title":"Portainer"},{"location":"third_party-roundcube/","text":"Download Roundcube 1.5.x to the web htdocs directory and extract it (here rc/ ): # Check for a newer release! cd data/web wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.5-rc/roundcubemail-1.5-rc-complete.tar.gz | tar xfvz - # Change folder name mv roundcubemail-1.5-rc rc # Change permissions chown -R root: rc/ If you need spell check features, create a file data/hooks/phpfpm/aspell.sh with the following content, then chmod +x data/hooks/phpfpm/aspell.sh . This installs a local spell check engine. #!/bin/bash apk update apk add aspell-en # or any other language Create a file data/web/rc/config/config.inc.php with the following content. Change the des_key parameter to a random value. It is used to temporarily store your IMAP password. The \"db_prefix\" is optional but recommended. array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); $config['enable_installer'] = true; $config['smtp_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); $config['db_prefix'] = 'mailcow_rc1'; Point your browser to https://myserver/rc/installer and follow the instructions. Initialize the database and leave the installer. Delete the directory data/web/rc/installer after a successful installation! Configure ManageSieve filtering \u00b6 Open data/web/rc/plugins/managesieve/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['managesieve_port'] = 4190; $config['managesieve_host'] = 'tls://dovecot'; $config['managesieve_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); // Enables separate management interface for vacation responses (out-of-office) // 0 - no separate section (default), // 1 - add Vacation section, // 2 - add Vacation section, but hide Filters section $config['managesieve_vacation'] = 1; Enable change password function in Roundcube \u00b6 Open data/web/rc/config/config.inc.php and enable the password plugin: ... $config['plugins'] = array( 'archive', 'password', ); ... Open data/web/rc/plugins/password/password.php , search for case 'ssha': and add above: case 'ssha256': $salt = rcube_utils::random_bytes(8); $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); $prefix = '{SSHA256}'; break; Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['password_driver'] = 'sql'; $config['password_algorithm'] = 'ssha256'; $config['password_algorithm_prefix'] = '{SSHA256}'; $config['password_query'] = \"UPDATE mailbox SET password = %P WHERE username = %u\"; Integrate CardDAV addressbooks in Roundcube \u00b6 Download the latest release of RCMCardDAV to the Roundcube plugin directory and extract it (here rc/plugins ): cd data/web/rc/plugins wget -O - https://github.com/mstilkerich/rcmcarddav/releases/download/v4.1.2/carddav-v4.1.2.tar.gz | tar xfvz - chown -R root: carddav/ Copy the file config.inc.php.dist to config.inc.php (here in rc/plugins/carddav ) and append the following preset to the end of the file - don't forget to replace mx.example.org with your own hostname: $prefs['SOGo'] = array( 'name' => 'SOGo', 'username' => '%u', 'password' => '%p', 'url' => 'https://mx.example.org/SOGo/dav/%u/', 'carddav_name_only' => true, 'use_categories' => true, 'active' => true, 'readonly' => false, 'refresh_time' => '02:00:00', 'fixed' => array( 'active', 'name', 'username', 'password', 'refresh_time' ), 'hide' => false, ); Please note, that this preset only integrates the default addressbook (the one that's named \"Personal Address Book\" and can't be deleted). Additional addressbooks are currently not automatically detected but can be manually added within the roundecube settings. Enable the plugin by adding carddav to $config['plugins'] in rc/config/config.inc.php . If you want to remove the default addressbooks (stored in the Roundcube database), so that only the CardDAV addressbooks are accessible, append $config['address_book_type'] = ''; to the config file data/web/rc/config/config.inc.php . Optionally, you can add Roundcube's link to the mailcow Apps list. To do this, open or create data/web/inc/vars.local.inc.php and add the following code-block: NOTE: Don't forget to add the 'SOGo', 'link' => '/SOGo/' ), array( 'name' => 'Roundcube', 'link' => '/rc/' ) ); ...","title":"Roundcube"},{"location":"third_party-roundcube/#configure-managesieve-filtering","text":"Open data/web/rc/plugins/managesieve/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['managesieve_port'] = 4190; $config['managesieve_host'] = 'tls://dovecot'; $config['managesieve_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); // Enables separate management interface for vacation responses (out-of-office) // 0 - no separate section (default), // 1 - add Vacation section, // 2 - add Vacation section, but hide Filters section $config['managesieve_vacation'] = 1;","title":"Configure ManageSieve filtering"},{"location":"third_party-roundcube/#enable-change-password-function-in-roundcube","text":"Open data/web/rc/config/config.inc.php and enable the password plugin: ... $config['plugins'] = array( 'archive', 'password', ); ... Open data/web/rc/plugins/password/password.php , search for case 'ssha': and add above: case 'ssha256': $salt = rcube_utils::random_bytes(8); $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); $prefix = '{SSHA256}'; break; Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['password_driver'] = 'sql'; $config['password_algorithm'] = 'ssha256'; $config['password_algorithm_prefix'] = '{SSHA256}'; $config['password_query'] = \"UPDATE mailbox SET password = %P WHERE username = %u\";","title":"Enable change password function in Roundcube"},{"location":"third_party-roundcube/#integrate-carddav-addressbooks-in-roundcube","text":"Download the latest release of RCMCardDAV to the Roundcube plugin directory and extract it (here rc/plugins ): cd data/web/rc/plugins wget -O - https://github.com/mstilkerich/rcmcarddav/releases/download/v4.1.2/carddav-v4.1.2.tar.gz | tar xfvz - chown -R root: carddav/ Copy the file config.inc.php.dist to config.inc.php (here in rc/plugins/carddav ) and append the following preset to the end of the file - don't forget to replace mx.example.org with your own hostname: $prefs['SOGo'] = array( 'name' => 'SOGo', 'username' => '%u', 'password' => '%p', 'url' => 'https://mx.example.org/SOGo/dav/%u/', 'carddav_name_only' => true, 'use_categories' => true, 'active' => true, 'readonly' => false, 'refresh_time' => '02:00:00', 'fixed' => array( 'active', 'name', 'username', 'password', 'refresh_time' ), 'hide' => false, ); Please note, that this preset only integrates the default addressbook (the one that's named \"Personal Address Book\" and can't be deleted). Additional addressbooks are currently not automatically detected but can be manually added within the roundecube settings. Enable the plugin by adding carddav to $config['plugins'] in rc/config/config.inc.php . If you want to remove the default addressbooks (stored in the Roundcube database), so that only the CardDAV addressbooks are accessible, append $config['address_book_type'] = ''; to the config file data/web/rc/config/config.inc.php . Optionally, you can add Roundcube's link to the mailcow Apps list. To do this, open or create data/web/inc/vars.local.inc.php and add the following code-block: NOTE: Don't forget to add the 'SOGo', 'link' => '/SOGo/' ), array( 'name' => 'Roundcube', 'link' => '/rc/' ) ); ...","title":"Integrate CardDAV addressbooks in Roundcube"},{"location":"third_party-thunderbird/","text":"Build the SOGo Connector plugin \u00b6 Install GNU Make, tar, and ZIP if you don't already have them installed. On Debian/Ubuntu, this can be done using apt-get install make tar zip Next, go to data/web inside mailcow-dockerized. Place the file thunderbird-plugins.php into that directory. Create a new directory thunderbird-plugins and place the script build-plugins.sh into it. Finally, execute the script with your hostname as an argument and piping it the names of all domains that mailcow handles. All of this can be done using the following commands: cd data/web curl -LO https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/thunderbird-plugins.php mkdir thunderbird-plugins cd thunderbird-plugins curl -Lo build-plugins.sh https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/build-thunderbird-plugins.sh chmod +x build-plugins.sh echo example.com example.org | ./build-plugins.sh mailcow.example.com Install it in Thunderbird \u00b6 After you have set up your mailcow IMAP account in Thunderbird, download the SOGo Connector plugin for your domain, e.g. https://mailcow.example.com/thunderbird-plugins/sogo-connector-68.0.1-example.com.xpi (see data/web/thunderbird-plugins ), and install it into Thunderbird. All your address books and calendars will be configured automatically.","title":"SOGo Connector for Thunderbird"},{"location":"third_party-thunderbird/#build-the-sogo-connector-plugin","text":"Install GNU Make, tar, and ZIP if you don't already have them installed. On Debian/Ubuntu, this can be done using apt-get install make tar zip Next, go to data/web inside mailcow-dockerized. Place the file thunderbird-plugins.php into that directory. Create a new directory thunderbird-plugins and place the script build-plugins.sh into it. Finally, execute the script with your hostname as an argument and piping it the names of all domains that mailcow handles. All of this can be done using the following commands: cd data/web curl -LO https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/thunderbird-plugins.php mkdir thunderbird-plugins cd thunderbird-plugins curl -Lo build-plugins.sh https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/build-thunderbird-plugins.sh chmod +x build-plugins.sh echo example.com example.org | ./build-plugins.sh mailcow.example.com","title":"Build the SOGo Connector plugin"},{"location":"third_party-thunderbird/#install-it-in-thunderbird","text":"After you have set up your mailcow IMAP account in Thunderbird, download the SOGo Connector plugin for your domain, e.g. https://mailcow.example.com/thunderbird-plugins/sogo-connector-68.0.1-example.com.xpi (see data/web/thunderbird-plugins ), and install it into Thunderbird. All your address books and calendars will be configured automatically.","title":"Install it in Thunderbird"},{"location":"u_e-80_to_443/","text":"Since February the 28th 2017 mailcow does come with port 80 and 443 enabled. Do not use the config below for reverse proxy setups , please see our reverse proxy guide for this, which includes a redirect from HTTP to HTTPS. Open mailcow.conf and set HTTP_BIND= - if not already set. Create a new file data/conf/nginx/redirect.conf and add the following server config to the file: server { root /web; listen 80 default_server; listen [::]:80 default_server; include /etc/nginx/conf.d/server_name.active; if ( $request_uri ~* \"%0A|%0D\" ) { return 403; } location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } location / { return 301 https://$host$uri$is_args$args; } } In case you changed the HTTP_BIND parameter, recreate the container: docker-compose up -d Otherwise restart Nginx: docker-compose restart nginx-mailcow","title":"Redirect HTTP to HTTPS"},{"location":"u_e-autodiscover_config/","text":"You do not need to change or create this file, autodiscover works out of the box . This guide is only meant for customizations to the autodiscover or autoconfig process. Newer Outlook clients (especially those delivered with O365) will not autodiscover mail profiles. Keep in mind, that ActiveSync should NOT be used with a desktop client . Open/create data/web/inc/vars.local.inc.php and add your changes to the configuration array. Changes will be merged with \"$autodiscover_config\" in data/web/inc/vars.inc.php ): 'activesync', // If autodiscoverType => activesync, also use ActiveSync (EAS) for Outlook desktop clients (>= Outlook 2013 on Windows) // Outlook for Mac does not support ActiveSync 'useEASforOutlook' => 'yes', // Please don't use STARTTLS-enabled service ports in the \"port\" variable. // The autodiscover service will always point to SMTPS and IMAPS (TLS-wrapped services). // The autoconfig service will additionally announce the STARTTLS-enabled ports, specified in the \"tlsport\" variable. 'imap' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('IMAPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('IMAP_PORT'))), ), 'pop3' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('POPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('POP_PORT'))), ), 'smtp' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('SMTPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('SUBMISSION_PORT'))), ), 'activesync' => array( 'url' => 'https://'.$mailcow_hostname.($https_port == 443 ? '' : ':'.$https_port).'/Microsoft-Server-ActiveSync', ), 'caldav' => array( 'server' => $mailcow_hostname, 'port' => $https_port, ), 'carddav' => array( 'server' => $mailcow_hostname, 'port' => $https_port, ), ); To always use IMAP and SMTP instead of EAS, set 'autodiscoverType' => 'imap' . Disable ActiveSync for Outlook desktop clients by setting \"useEASforOutlook\" to \"no\".","title":"Autodiscover / Autoconfig"},{"location":"u_e-backup_restore-maildir/","text":"Backup \u00b6 This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: cd /path/to/mailcow-dockerized docker run --rm -i -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar cvfz /backup/backup_vmail.tar.gz /vmail You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. Set the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz Restore \u00b6 cd /path/to/mailcow-dockerized docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar xvfz /backup/backup_vmail.tar.gz","title":"Maildir"},{"location":"u_e-backup_restore-maildir/#backup","text":"This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: cd /path/to/mailcow-dockerized docker run --rm -i -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar cvfz /backup/backup_vmail.tar.gz /vmail You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. Set the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz","title":"Backup"},{"location":"u_e-backup_restore-maildir/#restore","text":"cd /path/to/mailcow-dockerized docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar xvfz /backup/backup_vmail.tar.gz","title":"Restore"},{"location":"u_e-backup_restore-mysql/","text":"Backup \u00b6 cd /path/to/mailcow-dockerized source mailcow.conf DATE=$(date +\"%Y%m%d_%H%M%S\") docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql Restore \u00b6 Warning You should redirect the SQL dump without docker-compose to prevent parsing errors. cd /path/to/mailcow-dockerized source mailcow.conf docker exec -i $(docker-compose ps -q mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql","title":"MySQL"},{"location":"u_e-backup_restore-mysql/#backup","text":"cd /path/to/mailcow-dockerized source mailcow.conf DATE=$(date +\"%Y%m%d_%H%M%S\") docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql","title":"Backup"},{"location":"u_e-backup_restore-mysql/#restore","text":"Warning You should redirect the SQL dump without docker-compose to prevent parsing errors. cd /path/to/mailcow-dockerized source mailcow.conf docker exec -i $(docker-compose ps -q mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql","title":"Restore"},{"location":"u_e-docker-cust_dockerfiles/","text":"You need to copy the override file with corresponding build tags to the mailcow: dockerized root folder (i.e. /opt/mailcow-dockerized ): cp helper-scripts/docker-compose.override.yml.d/BUILD_FLAGS/docker-compose.override.yml docker-compose.override.yml Make your changes in data/Dockerfiles/$service and build the image locally: docker build data/Dockerfiles/service -t mailcow/$service Now auto-recreate modified containers: docker-compose up -d","title":"Customize Dockerfiles"},{"location":"u_e-docker-dc_bash_compl/","text":"To get some sexy bash completion inside your containers simply execute the following: curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose","title":"Docker Compose Bash Completion"},{"location":"u_e-dovecot-any_acl/","text":"On August the 17th, we disabled the possibility to share with \"any\" or \"all authenticated users\" by default. This function can be re-enabled by setting ACL_ANYONE to allow in mailcow.conf: ACL_ANYONE=allow Apply the changes by running docker-compose up -d .","title":"Enable \"any\" ACL settings"},{"location":"u_e-dovecot-catchall_vacation/","text":"The Dovecot parameter sieve_vacation_dont_check_recipient - which was by default set to yes in mailcow configurations pre 21st July - allows for vacation replies even when a mail is sent to non-existent mailboxes like a catch-all addresses. We decided to switch this parameter back to no and allow a user to specify which recipient address triggers a vacation reply. The triggering recipients can also be configured in SOGos autoresponder feature.","title":"Vacation replies for catchall addresses"},{"location":"u_e-dovecot-expunge/","text":"If you want to delete old mails out of the .Junk or .Trash folders or maybe delete all read mails that are older than a certain amount of time you may use dovecot's tool doveadm man doveadm-expunge . The manual way \u00b6 That said, let's dive in: Delete a user's mails inside the junk folder that are read and older than 4 hours docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'Junk' SEEN not SINCE 4h Delete all user's mails in the junk folder that are older than 7 days docker-compose exec dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 7d Delete all mails (of all users) in all folders that are older than 52 weeks (internal date of the mail, not the date it was saved on the system => before instead of savedbefore ). Useful for deleting very old mails on all users and folders (thus especially useful for GDPR-compliance). docker-compose exec dovecot-mailcow doveadm expunge -A mailbox % before 52w Delete mails inside a custom folder inside a user's inbox that are not flagged and older than 2 weeks docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'INBOX/custom-folder' not FLAGGED not SINCE 2w Info For possible time spans or search keys have a look at man doveadm-search-query Job scheduler \u00b6 via the host system cron \u00b6 If you want to automate such a task you can create a cron job on your host that calls a script like the one below: #!/bin/bash # Path to mailcow-dockerized, e.g. /opt/mailcow-dockerized cd /path/to/your/mailcow-dockerized /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 2w /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' SEEN not SINCE 12h [...] To create a cron job you may execute crontab -e and insert something like the following to execute a script: # Execute everyday at 04:00 A.M. 0 4 * * * /path/to/your/expunge_mailboxes.sh via Docker job scheduler \u00b6 To archive this with a docker job scheduler use this docker-compose.override.yml with your mailcow: version: '2.1' services: ofelia: image: mcuadros/ofelia:latest restart: always command: daemon --docker volumes: - /var/run/docker.sock:/var/run/docker.sock:ro network_mode: none dovecot-mailcow: labels: - \"ofelia.enabled=true\" - \"ofelia.job-exec.dovecot-expunge-trash.schedule=0 4 * * *\" - \"ofelia.job-exec.dovecot-expunge-trash.command=doveadm expunge -A mailbox 'Junk' savedbefore 2w\" - \"ofelia.job-exec.dovecot-expunge-trash.tty=false\" The job controller just need access to the docker control socket to be able to emulate the behavior of \"exec\". Then we add a few label to our dovecot-container to activate the job scheduler and tell him in a cron compatible scheduling format when to run. If you struggle with that schedule string you can use crontab guru . This docker-compose.override.yml deletes all mails older then 2 weeks from the \"Junk\" folder every day at 4 am. To see if things ran proper, you can not only see in your mailbox but also check Ofelia's docker log if it looks something like this: common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Started - doveadm expunge -A mailbox 'Junk' savedbefore 2w, common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Finished in \"285.032291ms\", failed: false, skipped: false, error: none, If it failed it will say so and give you the output of the doveadm in the log to make it easy on you to debug. In case you want to add more jobs, ensure you change the \"dovecot-expunge-trash\" part after \"ofelia.job-exec.\" to something else, it defines the name of the job. Syntax of the labels you find at mcuadros/ofelia .","title":"Expunge a Users mails"},{"location":"u_e-dovecot-expunge/#the-manual-way","text":"That said, let's dive in: Delete a user's mails inside the junk folder that are read and older than 4 hours docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'Junk' SEEN not SINCE 4h Delete all user's mails in the junk folder that are older than 7 days docker-compose exec dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 7d Delete all mails (of all users) in all folders that are older than 52 weeks (internal date of the mail, not the date it was saved on the system => before instead of savedbefore ). Useful for deleting very old mails on all users and folders (thus especially useful for GDPR-compliance). docker-compose exec dovecot-mailcow doveadm expunge -A mailbox % before 52w Delete mails inside a custom folder inside a user's inbox that are not flagged and older than 2 weeks docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'INBOX/custom-folder' not FLAGGED not SINCE 2w Info For possible time spans or search keys have a look at man doveadm-search-query","title":"The manual way"},{"location":"u_e-dovecot-expunge/#job-scheduler","text":"","title":"Job scheduler"},{"location":"u_e-dovecot-expunge/#via-the-host-system-cron","text":"If you want to automate such a task you can create a cron job on your host that calls a script like the one below: #!/bin/bash # Path to mailcow-dockerized, e.g. /opt/mailcow-dockerized cd /path/to/your/mailcow-dockerized /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 2w /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' SEEN not SINCE 12h [...] To create a cron job you may execute crontab -e and insert something like the following to execute a script: # Execute everyday at 04:00 A.M. 0 4 * * * /path/to/your/expunge_mailboxes.sh","title":"via the host system cron"},{"location":"u_e-dovecot-expunge/#via-docker-job-scheduler","text":"To archive this with a docker job scheduler use this docker-compose.override.yml with your mailcow: version: '2.1' services: ofelia: image: mcuadros/ofelia:latest restart: always command: daemon --docker volumes: - /var/run/docker.sock:/var/run/docker.sock:ro network_mode: none dovecot-mailcow: labels: - \"ofelia.enabled=true\" - \"ofelia.job-exec.dovecot-expunge-trash.schedule=0 4 * * *\" - \"ofelia.job-exec.dovecot-expunge-trash.command=doveadm expunge -A mailbox 'Junk' savedbefore 2w\" - \"ofelia.job-exec.dovecot-expunge-trash.tty=false\" The job controller just need access to the docker control socket to be able to emulate the behavior of \"exec\". Then we add a few label to our dovecot-container to activate the job scheduler and tell him in a cron compatible scheduling format when to run. If you struggle with that schedule string you can use crontab guru . This docker-compose.override.yml deletes all mails older then 2 weeks from the \"Junk\" folder every day at 4 am. To see if things ran proper, you can not only see in your mailbox but also check Ofelia's docker log if it looks something like this: common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Started - doveadm expunge -A mailbox 'Junk' savedbefore 2w, common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Finished in \"285.032291ms\", failed: false, skipped: false, error: none, If it failed it will say so and give you the output of the doveadm in the log to make it easy on you to debug. In case you want to add more jobs, ensure you change the \"dovecot-expunge-trash\" part after \"ofelia.job-exec.\" to something else, it defines the name of the job. Syntax of the labels you find at mcuadros/ofelia .","title":"via Docker job scheduler"},{"location":"u_e-dovecot-extra_conf/","text":"Create a file data/conf/dovecot/extra.conf - if missing - and add your additional content here. Restart dovecot-mailcow to apply your changes: docker-compose restart dovecot-mailcow","title":"Customize/Expand dovecot.conf"},{"location":"u_e-dovecot-fts/","text":"Solr is used for setups with memory >= 3.5 GiB to provide full-text search in Dovecot. Please be aware that applications like Solr may need maintenance from time to time. Besides that, Solr will eat a lot of RAM, depending on the usage of your server. Please avoid it on machines with less than 3 GB RAM. The default heap size (1024 M) is defined in mailcow.conf. Since we run in Docker and create our containers with the \"restart: always\" flag, a oom situation will at least only trigger a restart of the container. FTS related Dovecot commands \u00b6 # single user docker-compose exec dovecot-mailcow doveadm fts rescan -u user@domain # all users docker-compose exec dovecot-mailcow doveadm fts rescan -A Dovecot Wiki: \"Scan what mails exist in the full text search index and compare those to what actually exist in mailboxes. This removes mails from the index that have already been expunged and makes sure that the next doveadm index will index all the missing mails (if any).\" This does not re-index a mailbox. It basically repairs a given index. If you want to re-index data immediately, you can run the followig command, where '*' can also be a mailbox mask like 'Sent'. You do not need to run these commands, but it will speed things up a bit: # single user docker-compose exec dovecot-mailcow doveadm index -u user@domain '*' # all users, but obviously slower and more dangerous docker-compose exec dovecot-mailcow doveadm index -A '*' This will take some time depending on your machine and Solr can run oom, monitor it! Because re-indexing is very sensible, we did not include it to mailcow UI. You will need to take care of any errors while re-indexing a mailbox. Delete mailbox data \u00b6 mailcow will purge index data of a user when deleting a mailbox.","title":"FTS (Solr)"},{"location":"u_e-dovecot-fts/#fts-related-dovecot-commands","text":"# single user docker-compose exec dovecot-mailcow doveadm fts rescan -u user@domain # all users docker-compose exec dovecot-mailcow doveadm fts rescan -A Dovecot Wiki: \"Scan what mails exist in the full text search index and compare those to what actually exist in mailboxes. This removes mails from the index that have already been expunged and makes sure that the next doveadm index will index all the missing mails (if any).\" This does not re-index a mailbox. It basically repairs a given index. If you want to re-index data immediately, you can run the followig command, where '*' can also be a mailbox mask like 'Sent'. You do not need to run these commands, but it will speed things up a bit: # single user docker-compose exec dovecot-mailcow doveadm index -u user@domain '*' # all users, but obviously slower and more dangerous docker-compose exec dovecot-mailcow doveadm index -A '*' This will take some time depending on your machine and Solr can run oom, monitor it! Because re-indexing is very sensible, we did not include it to mailcow UI. You will need to take care of any errors while re-indexing a mailbox.","title":"FTS related Dovecot commands"},{"location":"u_e-dovecot-fts/#delete-mailbox-data","text":"mailcow will purge index data of a user when deleting a mailbox.","title":"Delete mailbox data"},{"location":"u_e-dovecot-idle_interval/","text":"Changing the IMAP IDLE interval \u00b6 What is the IDLE interval? \u00b6 Per default, Dovecot sends a \"I'm still here\" notification to every client that has an open connection with Dovecot to get mails as quickly as possible without manually polling it (IMAP PUSH). This notification is controlled by the setting imap_idle_notify_interval , which defaults to 2 minutes. A short interval results in the client getting a lot of messages for this connection, which is bad for mobile devices, because every time the device receives this message, the mailing app has to wake up. This can result in unnecessary battery drain. Edit the value \u00b6 Change configuration \u00b6 Create a new file data/conf/dovecot/extra.conf (or edit it if it already exists). Insert the setting followed by the new value. For example, to set the interval to 5 minutes you could type: imap_idle_notify_interval = 5 mins 29 minutes is the maximum value allowed by the corresponding RFC . Warning This isn't a default setting in mailcow because we don't know how this setting changes the behavior of other clients. Be careful if you change this and monitor different behavior. Reload Dovecot \u00b6 Now reload Dovecot: docker-compose exec dovecot-mailcow dovecot reload Info You can check the value of this setting with docker-compose exec dovecot-mailcow dovecot -a | grep \"imap_idle_notify_interval\" If you didn't change it, it should be at 2m. If you did change it, you should see your new value.","title":"IMAP IDLE interval"},{"location":"u_e-dovecot-idle_interval/#changing-the-imap-idle-interval","text":"","title":"Changing the IMAP IDLE interval"},{"location":"u_e-dovecot-idle_interval/#what-is-the-idle-interval","text":"Per default, Dovecot sends a \"I'm still here\" notification to every client that has an open connection with Dovecot to get mails as quickly as possible without manually polling it (IMAP PUSH). This notification is controlled by the setting imap_idle_notify_interval , which defaults to 2 minutes. A short interval results in the client getting a lot of messages for this connection, which is bad for mobile devices, because every time the device receives this message, the mailing app has to wake up. This can result in unnecessary battery drain.","title":"What is the IDLE interval?"},{"location":"u_e-dovecot-idle_interval/#edit-the-value","text":"","title":"Edit the value"},{"location":"u_e-dovecot-idle_interval/#change-configuration","text":"Create a new file data/conf/dovecot/extra.conf (or edit it if it already exists). Insert the setting followed by the new value. For example, to set the interval to 5 minutes you could type: imap_idle_notify_interval = 5 mins 29 minutes is the maximum value allowed by the corresponding RFC . Warning This isn't a default setting in mailcow because we don't know how this setting changes the behavior of other clients. Be careful if you change this and monitor different behavior.","title":"Change configuration"},{"location":"u_e-dovecot-idle_interval/#reload-dovecot","text":"Now reload Dovecot: docker-compose exec dovecot-mailcow dovecot reload Info You can check the value of this setting with docker-compose exec dovecot-mailcow dovecot -a | grep \"imap_idle_notify_interval\" If you didn't change it, it should be at 2m. If you did change it, you should see your new value.","title":"Reload Dovecot"},{"location":"u_e-dovecot-mail-crypt/","text":"Mails are stored compressed (lz4) and encrypted. The key pair can be found in crypt-vol-1. If you want to decode/encode existing maildir files, you can use the following script at your own risk: Enter Dovecot by running docker-compose exec dovecot-mailcow /bin/bash in the mailcow-dockerized location. # Decrypt /var/vmail find /var/vmail/ -type f -regextype egrep -regex '.*S=.*W=.*' | while read -r file; do if [[ $(head -c7 \"$file\") == \"CRYPTED\" ]]; then doveadm fs get compress lz4:0:crypt:private_key_path=/mail_crypt/ecprivkey.pem:public_key_path=/mail_crypt/ecpubkey.pem:posix:prefix=/ \\ \"$file\" > \"/tmp/$(basename \"$file\")\" if [[ -s \"/tmp/$(basename \"$file\")\" ]]; then chmod 600 \"/tmp/$(basename \"$file\")\" chown 5000:5000 \"/tmp/$(basename \"$file\")\" mv \"/tmp/$(basename \"$file\")\" \"$file\" else rm \"/tmp/$(basename \"$file\")\" fi fi done # Encrypt /var/vmail find /var/vmail/ -type f -regextype egrep -regex '.*S=.*W=.*' | while read -r file; do if [[ $(head -c7 \"$file\") != \"CRYPTED\" ]]; then doveadm fs put crypt private_key_path=/mail_crypt/ecprivkey.pem:public_key_path=/mail_crypt/ecpubkey.pem:posix:prefix=/ \\ \"$file\" \"$file\" chmod 600 \"$file\" chown 5000:5000 \"$file\" fi done","title":"Mail crypt"},{"location":"u_e-dovecot-more/","text":"Here is just an unsorted list of useful doveadm commands that could be useful. doveadm quota \u00b6 The quota get and quota recalc 1 commands are used to display or recalculate the current user's quota usage. The reported values are in kilobytes . To list the current quota status for a user / mailbox, do: doveadm quota get -u 'mailbox@example.org' To list the quota storage value for all users, do: doveadm quota get -A |grep \"STORAGE\" Recalculate a single user's quota usage: doveadm quota recalc -u 'mailbox@example.org' doveadm search \u00b6 The doveadm search 2 command is used to find messages matching your query. It can return the username, mailbox-GUID / -UID and message-GUIDs / -UIDs. To view the number of messages, by user, in their .Trash folder: doveadm search -A mailbox 'Trash' | awk '{print $1}' | sort | uniq -c Show all messages in a user's inbox older then 90 days: doveadm search -u 'mailbox@example.org' mailbox 'INBOX' savedbefore 90d Show all messages in any folder that are older then 30 days for mailbox@example.org : doveadm search -u 'mailbox@example.org' mailbox \"*\" savedbefore 30d https://wiki.dovecot.org/Tools/Doveadm/Quota \u21a9 https://wiki.dovecot.org/Tools/Doveadm/Search \u21a9","title":"More Examples with DOVEADM"},{"location":"u_e-dovecot-more/#doveadm-quota","text":"The quota get and quota recalc 1 commands are used to display or recalculate the current user's quota usage. The reported values are in kilobytes . To list the current quota status for a user / mailbox, do: doveadm quota get -u 'mailbox@example.org' To list the quota storage value for all users, do: doveadm quota get -A |grep \"STORAGE\" Recalculate a single user's quota usage: doveadm quota recalc -u 'mailbox@example.org'","title":"doveadm quota"},{"location":"u_e-dovecot-more/#doveadm-search","text":"The doveadm search 2 command is used to find messages matching your query. It can return the username, mailbox-GUID / -UID and message-GUIDs / -UIDs. To view the number of messages, by user, in their .Trash folder: doveadm search -A mailbox 'Trash' | awk '{print $1}' | sort | uniq -c Show all messages in a user's inbox older then 90 days: doveadm search -u 'mailbox@example.org' mailbox 'INBOX' savedbefore 90d Show all messages in any folder that are older then 30 days for mailbox@example.org : doveadm search -u 'mailbox@example.org' mailbox \"*\" savedbefore 30d https://wiki.dovecot.org/Tools/Doveadm/Quota \u21a9 https://wiki.dovecot.org/Tools/Doveadm/Search \u21a9","title":"doveadm search"},{"location":"u_e-dovecot-public_folder/","text":"Create a new public namespace \"Public\" and a mailbox \"Develcow\" inside that namespace: Edit or create data/conf/dovecot/extra.conf , add: namespace { type = public separator = / prefix = Public/ location = maildir:/var/vmail/public:INDEXPVT=~/public subscriptions = yes mailbox \"Develcow\" { auto = subscribe } } :INDEXPVT=~/public can be omitted if per-user seen flags are not wanted. The new mailbox in the public namespace will be auto-subscribed by users. To allow all authenticated users access full to that new mailbox (not the whole namespace), run: docker-compose exec dovecot-mailcow doveadm acl set -A \"Public/Develcow\" \"authenticated\" lookup read write write-seen write-deleted insert post delete expunge create Adjust the command to your needs if you like to assign more granular rights per user (use -u user@domain instead of -A for example). Allow authenticated users access to the whole public namespace \u00b6 To allow all authenticated users access full access to the whole public namespace and its subfolders, create a new dovecot-acl file in the namespace root directory: Open/edit/create /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/public/dovecot-acl (adjust the path accordingly) to create the global ACL file with the following content: authenticated kxeilprwts kxeilprwts equals to lookup read write write-seen write-deleted insert post delete expunge create . You can use doveadm acl set -u user@domain \"Public/Develcow\" user=user@domain lookup read to limit access for a single user. You may also turn it around to limit access for all users to \"lr\" and grant only some users full access. See Dovecot ACL for further information about ACL.","title":"Public folders"},{"location":"u_e-dovecot-public_folder/#allow-authenticated-users-access-to-the-whole-public-namespace","text":"To allow all authenticated users access full access to the whole public namespace and its subfolders, create a new dovecot-acl file in the namespace root directory: Open/edit/create /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/public/dovecot-acl (adjust the path accordingly) to create the global ACL file with the following content: authenticated kxeilprwts kxeilprwts equals to lookup read write write-seen write-deleted insert post delete expunge create . You can use doveadm acl set -u user@domain \"Public/Develcow\" user=user@domain lookup read to limit access for a single user. You may also turn it around to limit access for all users to \"lr\" and grant only some users full access. See Dovecot ACL for further information about ACL.","title":"Allow authenticated users access to the whole public namespace"},{"location":"u_e-dovecot-static_master/","text":"Random master usernames and passwords are automatically created on every restart of dovecot-mailcow. That's recommended and should not be changed. If you need the user to be static anyway, please specify two variables in mailcow.conf . Both parameters must not be empty! DOVECOT_MASTER_USER=mymasteruser DOVECOT_MASTER_PASS=mysecretpass Run docker-compose up -d to apply your changes. The static master username will be expanded to DOVECOT_MASTER_USER@mailcow.local . To login as test@example.org this would equal to test@example.org*mymasteruser@mailcow.local with the specified password above. A login to SOGo is not possible with this username. A click-to-login function for SOGo is available for admins as described here No master user is required.","title":"Static master user"},{"location":"u_e-dovecot-vmail-volume/","text":"The \"new\" way \u00b6 WARNING : Newer Docker versions seem to complain about existing volumes. You can fix this temporarily by removing the existing volume and start mailcow with the override file. But it seems to be problematic after a reboot (needs to be confirmed). An easy, dirty, yet stable workaround is to stop mailcow ( docker-compose down ), remove /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data and create a new link to your remote filesystem location, for example: mv /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data_backup ln -s /mnt/volume-xy/vmail_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data Start mailcow afterwards. The \"old\" way \u00b6 If you want to use another folder for the vmail-volume, you can create a docker-compose.override.yml file and add the following content: version: '2.1' volumes: vmail-vol-1: driver_opts: type: none device: /data/mailcow/vmail o: bind Moving an existing vmail folder: \u00b6 Locate the current vmail folder by its \"Mountpoint\" attribute: docker volume inspect mailcowdockerized_vmail-vol-1 [ { \"CreatedAt\": \"2019-06-16T22:08:34+02:00\", \"Driver\": \"local\", \"Labels\": { \"com.docker.compose.project\": \"mailcowdockerized\", \"com.docker.compose.version\": \"1.23.2\", \"com.docker.compose.volume\": \"vmail-vol-1\" }, \"Mountpoint\": \"/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data\", \"Name\": \"mailcowdockerized_vmail-vol-1\", \"Options\": null, \"Scope\": \"local\" } ] Copy the content of the Mountpoint folder to the new location (e.g. /data/mailcow/vmail ) using cp -a , rsync -a or a similar non strcuture breaking copy command Stop mailcow by executing docker-compose down from within your mailcow root folder (e.g. /opt/mailcow-dockerized ) Create the file docker-compose.override.yml , edit the device path accordingly Delete the current vmail folder: docker volume rm mailcowdockerized_vmail-vol-1 Start mailcow by executing docker-compose up -d from within your mailcow root folder (e.g. /opt/mailcow-dockerized )","title":"Move Maildir (vmail)"},{"location":"u_e-dovecot-vmail-volume/#the-new-way","text":"WARNING : Newer Docker versions seem to complain about existing volumes. You can fix this temporarily by removing the existing volume and start mailcow with the override file. But it seems to be problematic after a reboot (needs to be confirmed). An easy, dirty, yet stable workaround is to stop mailcow ( docker-compose down ), remove /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data and create a new link to your remote filesystem location, for example: mv /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data_backup ln -s /mnt/volume-xy/vmail_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data Start mailcow afterwards.","title":"The \"new\" way"},{"location":"u_e-dovecot-vmail-volume/#the-old-way","text":"If you want to use another folder for the vmail-volume, you can create a docker-compose.override.yml file and add the following content: version: '2.1' volumes: vmail-vol-1: driver_opts: type: none device: /data/mailcow/vmail o: bind","title":"The \"old\" way"},{"location":"u_e-dovecot-vmail-volume/#moving-an-existing-vmail-folder","text":"Locate the current vmail folder by its \"Mountpoint\" attribute: docker volume inspect mailcowdockerized_vmail-vol-1 [ { \"CreatedAt\": \"2019-06-16T22:08:34+02:00\", \"Driver\": \"local\", \"Labels\": { \"com.docker.compose.project\": \"mailcowdockerized\", \"com.docker.compose.version\": \"1.23.2\", \"com.docker.compose.volume\": \"vmail-vol-1\" }, \"Mountpoint\": \"/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data\", \"Name\": \"mailcowdockerized_vmail-vol-1\", \"Options\": null, \"Scope\": \"local\" } ] Copy the content of the Mountpoint folder to the new location (e.g. /data/mailcow/vmail ) using cp -a , rsync -a or a similar non strcuture breaking copy command Stop mailcow by executing docker-compose down from within your mailcow root folder (e.g. /opt/mailcow-dockerized ) Create the file docker-compose.override.yml , edit the device path accordingly Delete the current vmail folder: docker volume rm mailcowdockerized_vmail-vol-1 Start mailcow by executing docker-compose up -d from within your mailcow root folder (e.g. /opt/mailcow-dockerized )","title":"Moving an existing vmail folder:"},{"location":"u_e-fido2/","text":"How is UV handled in mailcow? \u00b6 The UV flag (as in \"user verification\") enforces WebAuthn to verify the user before it allows access to the key (think of a PIN). We don't enforce UV to allow logins via iOS and NFC (YubiKey). Login and key processing \u00b6 mailcow uses client-side key processing . We ask the authenticator (i.e. YubiKey) to save the registration in its memory. A user does not need to enter a username. The available credentials - if any - will be shown to the user when selecting the \"key login\" via mailcow UI login. When calling the login process, the authenticator is not given any credential IDs. This will force it to lookup credentials in its own memory. Who can use WebAuthn to login to mailcow? \u00b6 As of today, only administrators and domain administrators are able to setup WebAuthn/FIDO2.","title":"WebAuthn / FIDO2"},{"location":"u_e-fido2/#how-is-uv-handled-in-mailcow","text":"The UV flag (as in \"user verification\") enforces WebAuthn to verify the user before it allows access to the key (think of a PIN). We don't enforce UV to allow logins via iOS and NFC (YubiKey).","title":"How is UV handled in mailcow?"},{"location":"u_e-fido2/#login-and-key-processing","text":"mailcow uses client-side key processing . We ask the authenticator (i.e. YubiKey) to save the registration in its memory. A user does not need to enter a username. The available credentials - if any - will be shown to the user when selecting the \"key login\" via mailcow UI login. When calling the login process, the authenticator is not given any credential IDs. This will force it to lookup credentials in its own memory.","title":"Login and key processing"},{"location":"u_e-fido2/#who-can-use-webauthn-to-login-to-mailcow","text":"As of today, only administrators and domain administrators are able to setup WebAuthn/FIDO2.","title":"Who can use WebAuthn to login to mailcow?"},{"location":"u_e-mailcow_ui-bl_wl/","text":"To add or edit an entry to your domain wide filter table, login to your mailcow UI as (domain) administrator. Info Be aware that a user may override this setting by setting his own black- and whitelist! There is also a global filter table in /admin to configure a server-wide filter for multiple Regex maps (Todo: Screenshots).","title":"Blacklist / Whitelist"},{"location":"u_e-mailcow_ui-config/","text":"Several configuration parameters of the mailcow UI can be changed by creating a file data/web/inc/vars.local.inc.php which overrides defaults settings found in data/web/inc/vars.inc.php . The local configuration file is persistent over updates of mailcow. Try not to change values inside data/web/inc/vars.inc.php , but use them as template for the local override. mailcow UI configuration parameters can be used to... ...change the default language 1 ...change the default bootstrap theme ...set a password complexity regex ...enable DKIM private key visibility ...set a pagination trigger size ...set default mailbox attributes ...change session lifetimes ...create fixed app menus (which cannot be changed in mailcow UI) ...set a default \"To\" field for relayhost tests ...set a timeout for Docker API requests ...toggle IP anonymization To change SOGos default language, you will need to edit data/conf/sogo/sogo.conf and replace \"English\" by your preferred language. \u21a9","title":"Configuration"},{"location":"u_e-mailcow_ui-css/","text":"For custom overrides of specific elements via CSS, use data/web/css/build/0081-custom-mailcow.css . The file is excluded from tracking and persists over updates.","title":"CSS overrides"},{"location":"u_e-mailcow_ui-pushover/","text":"Info Pushover makes it easy to get real-time notifications on your Android, iPhone, iPad, and Desktop You can use Pushover to get a push notification on every mail you receive for each mailbox where you enabled this feature. 1. As admin open your mailbox' settings and scroll down to the Pushover settings 2. Register yourself on Pushover 3. Put your 'User Key' in the 'User/Group Key' field in your mailbox settings 4. Create an Applications to get the API Token/Key which you also need to put in your mailbox settings 5. Optional you can edit the notification title/text and define certain sender email addresses where a push notification is triggered 6. Save everything and then you can verify your credentials If everything is done you can test sending a mail and you will receive a push message on your phone","title":"Pushover"},{"location":"u_e-mailcow_ui-spamalias/","text":"These temporary email aliases are mostly used for places where we need to provide an email address but don't want future correspondence with. They are also called spam alias. To create, delete or extend a temporary email aliases you need to login to mailcow's UI as a mailbox user and navigate to the tab Temporary email aliases :","title":"Temporary email aliases"},{"location":"u_e-mailcow_ui-spamfilter/","text":"A mailbox user may adjust the spam filter and black- / whitelist settings for his mailbox individually by navigating to the Spam filter tab in the users mailcow UI. Info For global adjustments on your spam filter please check our section on Rspamd . For a domain wide black- and whitelist please check our guide on Black / Whitelist","title":"Spamfilter"},{"location":"u_e-mailcow_ui-tagging/","text":"Mailbox users can tag their mail address like in me+facebook@example.org . They can control the tag handling in the users mailcow UI panel. Tagging is also known as 'sub-addressing' (RFC 5233) or 'plus addressing' Available Actions \u00b6 1. Move this message to a sub folder \"facebook\" (will be created lower case if not existing) 2. Prepend the tag to the subject: \"[facebook] Subject\" Please note: Uppercase tags are converted to lowercase except for the first letter. If you want to keep the tag as it is, please apply the following diff and restart mailcow: diff --git a/data/conf/dovecot/global_sieve_after b/data/conf/dovecot/global_sieve_after index e047136e..933c4137 100644 --- a/data/conf/dovecot/global_sieve_after +++ b/data/conf/dovecot/global_sieve_after @@ -15,7 +15,7 @@ if allof ( envelope :detail :matches \"to\" \"*\", header :contains \"X-Moo-Tag\" \"YES\" ) { - set :lower :upperfirst \"tag\" \"${1}\"; + set \"tag\" \"${1}\"; if mailboxexists \"INBOX/${1}\" { fileinto \"INBOX/${1}\"; } else {","title":"Tagging"},{"location":"u_e-mailcow_ui-tagging/#available-actions","text":"1. Move this message to a sub folder \"facebook\" (will be created lower case if not existing) 2. Prepend the tag to the subject: \"[facebook] Subject\" Please note: Uppercase tags are converted to lowercase except for the first letter. If you want to keep the tag as it is, please apply the following diff and restart mailcow: diff --git a/data/conf/dovecot/global_sieve_after b/data/conf/dovecot/global_sieve_after index e047136e..933c4137 100644 --- a/data/conf/dovecot/global_sieve_after +++ b/data/conf/dovecot/global_sieve_after @@ -15,7 +15,7 @@ if allof ( envelope :detail :matches \"to\" \"*\", header :contains \"X-Moo-Tag\" \"YES\" ) { - set :lower :upperfirst \"tag\" \"${1}\"; + set \"tag\" \"${1}\"; if mailboxexists \"INBOX/${1}\" { fileinto \"INBOX/${1}\"; } else {","title":"Available Actions"},{"location":"u_e-mailcow_ui-tfa/","text":"So far three methods for Two-Factor Authentication are implemented: U2F, Yubi OTP, and TOTP For U2F to work, you need an encrypted connection to the server (HTTPS) as well as a FIDO security key. Both U2F and Yubi OTP work well with the fantastic Yubikey . While Yubi OTP needs an active internet connection and an API ID + key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS. U2F and Yubi OTP support multiple keys per user. As the third TFA method mailcow uses TOTP: time-based one-time passwords. Those passwords can be generated with apps like \"Google Authenticator\" after initially scanning a QR code or entering the given secret manually. As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in. The key used to login will be displayed in green, while other keys remain grey. Information on how to remove 2FA can be found here . Yubi OTP \u00b6 The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret. U2F \u00b6 To use U2F, the browser must support this standard. The following desktop browsers support this authentication type: Edge (>=79) Firefox (>=47, enabled by default since version 67) Chrome (>=41) Safari (>=13) Opera (40, >=42, not 41) The following mobile browsers support this authentication type: Safari on iOS (>=13.3) Firefox on Android (>=68) Sources: caniuse.com , blog.mozilla.org U2F works without an internet connection. TOTP \u00b6 The best known TFA method mostly used with a smartphone.","title":"Two-Factor Authentication"},{"location":"u_e-mailcow_ui-tfa/#yubi-otp","text":"The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.","title":"Yubi OTP"},{"location":"u_e-mailcow_ui-tfa/#u2f","text":"To use U2F, the browser must support this standard. The following desktop browsers support this authentication type: Edge (>=79) Firefox (>=47, enabled by default since version 67) Chrome (>=41) Safari (>=13) Opera (40, >=42, not 41) The following mobile browsers support this authentication type: Safari on iOS (>=13.3) Firefox on Android (>=68) Sources: caniuse.com , blog.mozilla.org U2F works without an internet connection.","title":"U2F"},{"location":"u_e-mailcow_ui-tfa/#totp","text":"The best known TFA method mostly used with a smartphone.","title":"TOTP"},{"location":"u_e-nginx/","text":"SSL \u00b6 Please see Advanced SSL and explicitly check ADDITIONAL_SERVER_NAMES for SSL configuration. Please do not add ADDITIONAL_SERVER_NAMES when you plan to use a different web root. New site \u00b6 To create persistent (over updates) sites hosted by mailcow: dockerized, a new site configuration must be placed inside data/conf/nginx/ : A good template to begin with: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; # Location: data/web root /web; # Location: data/web/mysite.com #root /web/mysite.com include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name mysite.example.org; server_tokens off; # This allows acme to be validated even with a different web root location ^~ /.well-known/acme-challenge/ { default_type \"text/plain\"; rewrite /.well-known/acme-challenge/(.*) /$1 break; root /web/.well-known/acme-challenge/; } if ($scheme = http) { return 301 https://$server_name$request_uri; } } New site with proxy to a remote location \u00b6 Another example with a reverse proxy configuration: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name example.domain.tld; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } if ($scheme = http) { return 301 https://$host$request_uri; } location / { proxy_pass http://service:3000/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } } Config expansion in mailcows Nginx \u00b6 The filename used for a new site is not important, as long as the filename carries a .conf extension. It is also possible to extend the configuration of the default file site.conf file: nano data/conf/nginx/site.my_content.custom This filename does not need to have a \".conf\" extension but follows the pattern site.*.custom , where * is a custom name. If PHP is to be included in a custom site, please use the PHP-FPM listener on phpfpm:9002 or create a new listener in data/conf/phpfpm/php-fpm.d/pools.conf . Restart Nginx (and PHP-FPM, if a new listener was created): docker-compose restart nginx-mailcow docker-compose restart php-fpm-mailcow","title":"Custom sites"},{"location":"u_e-nginx/#ssl","text":"Please see Advanced SSL and explicitly check ADDITIONAL_SERVER_NAMES for SSL configuration. Please do not add ADDITIONAL_SERVER_NAMES when you plan to use a different web root.","title":"SSL"},{"location":"u_e-nginx/#new-site","text":"To create persistent (over updates) sites hosted by mailcow: dockerized, a new site configuration must be placed inside data/conf/nginx/ : A good template to begin with: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; # Location: data/web root /web; # Location: data/web/mysite.com #root /web/mysite.com include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name mysite.example.org; server_tokens off; # This allows acme to be validated even with a different web root location ^~ /.well-known/acme-challenge/ { default_type \"text/plain\"; rewrite /.well-known/acme-challenge/(.*) /$1 break; root /web/.well-known/acme-challenge/; } if ($scheme = http) { return 301 https://$server_name$request_uri; } }","title":"New site"},{"location":"u_e-nginx/#new-site-with-proxy-to-a-remote-location","text":"Another example with a reverse proxy configuration: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name example.domain.tld; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } if ($scheme = http) { return 301 https://$host$request_uri; } location / { proxy_pass http://service:3000/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } }","title":"New site with proxy to a remote location"},{"location":"u_e-nginx/#config-expansion-in-mailcows-nginx","text":"The filename used for a new site is not important, as long as the filename carries a .conf extension. It is also possible to extend the configuration of the default file site.conf file: nano data/conf/nginx/site.my_content.custom This filename does not need to have a \".conf\" extension but follows the pattern site.*.custom , where * is a custom name. If PHP is to be included in a custom site, please use the PHP-FPM listener on phpfpm:9002 or create a new listener in data/conf/phpfpm/php-fpm.d/pools.conf . Restart Nginx (and PHP-FPM, if a new listener was created): docker-compose restart nginx-mailcow docker-compose restart php-fpm-mailcow","title":"Config expansion in mailcows Nginx"},{"location":"u_e-postfix-attachment_size/","text":"Open data/conf/postfix/extra.cf and set the message_size_limit accordingly in bytes. See main.cf for the default value. Restart Postfix: docker-compose restart postfix-mailcow","title":"Max. message size (attachment size)"},{"location":"u_e-postfix-custom_transport/","text":"For transport maps other than those to be configured in mailcow UI, please use data/conf/postfix/custom_transport.pcre to prevent existing maps or settings from being overwritten by updates. In most cases using this file is not necessary. Please make sure mailcow UI is not able to route your desired traffic properly before using that file. The file needs valid PCRE content and can break Postfix, if configured incorrectly.","title":"Custom transport maps"},{"location":"u_e-postfix-disable_sender_verification/","text":"New guide \u00b6 Edit a mailbox and select \"Allow to send as *\". For historical reasons we kept the old and deprecated guide below: Deprecated guide (DO NOT USE ON NEWER MAILCOWS!) \u00b6 This option is not best-practice and should only be implemented when there is no other option available to achieve whatever you are trying to do. Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail. user-to-allow-everything@example.com OK Open data/conf/postfix/main.cf and find smtpd_sender_restrictions . Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this: smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...] Run postmap on check_sasl_access: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access Restart the Postfix container.","title":"Disable Sender Addresses Verification"},{"location":"u_e-postfix-disable_sender_verification/#new-guide","text":"Edit a mailbox and select \"Allow to send as *\". For historical reasons we kept the old and deprecated guide below:","title":"New guide"},{"location":"u_e-postfix-disable_sender_verification/#deprecated-guide-do-not-use-on-newer-mailcows","text":"This option is not best-practice and should only be implemented when there is no other option available to achieve whatever you are trying to do. Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail. user-to-allow-everything@example.com OK Open data/conf/postfix/main.cf and find smtpd_sender_restrictions . Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this: smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...] Run postmap on check_sasl_access: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access Restart the Postfix container.","title":"Deprecated guide (DO NOT USE ON NEWER MAILCOWS!)"},{"location":"u_e-postfix-extra_cf/","text":"Please create a new file data/conf/postfix/extra.cf for overrides or additional content to main.cf . Postfix will complain about duplicate values once after starting postfix-mailcow, this is intended. Syslog-ng was configured to hide those warnings while Postfix is running, to not spam the log files with unnecessary information every time a service is used. Restart postfix-mailcow to apply your changes: docker-compose restart postfix-mailcow","title":"Customize/Expand main.cf"},{"location":"u_e-postfix-pflogsumm/","text":"To use pflogsumm with the default logging driver, we need to query postfix-mailcow via docker logs and pipe the output to pflogsumm: docker logs --since 24h $(docker ps -qf name=postfix-mailcow) | pflogsumm The above log output is limited to the past 24 hours. It's also possible to create a daily pflogsumm report via cron. Create the file /etc/cron.d/pflogsumm with the following content: SHELL=/bin/bash 59 23 * * * root docker logs --since 24h $(docker ps -qf name=postfix-mailcow) | /usr/sbin/pflogsumm -d today | mail -s \"Postfix Report of $(date)\" postmaster@example.net Based on the last 24h postfix logs, this example sends every day at 23:59:00 a pflogsumm report to postmaster@example.net .","title":"Statistics with pflogsumm"},{"location":"u_e-postfix-postscreen_whitelist/","text":"IPs can be removed from Postscreen and therefore also from RBL checks in data/conf/postfix/custom_postscreen_whitelist.cidr . Postscreen does multiple checks to identify malicious senders. In most cases you want to whitelist an IP to exclude it from blacklist lookups. The format of the file is as follows: CIDR ACTION Where CIDR is a single IP address or IP range in CIDR notation, and action is either \"permit\" or \"reject\". Example: # Rules are evaluated in the order as specified. # Blacklist 192.168.* except 192.168.0.1. 192.168.0.1 permit 192.168.0.0/16 reject The file is reloaded on the fly, postfix restart is not required.","title":"Whitelist IP in Postscreen"},{"location":"u_e-postfix-relayhost/","text":"As of September 12, 2018 you can setup relayhosts as admin by using the mailcow UI. This is useful if you want to relay outgoing emails for a specific domain to a third-party spam filter or a service like Mailgun or Sendgrid. This is also known as a smarthost . Add a new relayhost \u00b6 Go to the Routing tab of the Configuration and Details section of the admin UI. Here you will see a list of relayhosts currently setup. Scroll to the Add sender-dependent transport section. Under Host , add the host you want to relay to. Example: if you want to use Mailgun to send emails instead of your server IP, enter smtp.mailgun.org If the relay host requires a username and password to authenticate, enter them in the respective fields. Keep in mind the credentials will be stored in plain text. Test a relayhost \u00b6 To test that connectivity to the host works, click on Test from the list of relayhosts and enter a From: address. Then, run the test. You will then see the results of the SMTP transmission. If all went well, you should see SERVER -> CLIENT: 250 2.0.0 Ok: queued as A093B401D4 as one of the last lines. If not, review the error provided and resolve it. Note: Some hosts, especially those who do not require authentication, will deny connections from servers that have not been added to their system beforehand. Make sure you read the documentation of the relayhost to make sure you've added your domain and/or the server IP to their system. Tip: You can change the default test To: address the test uses from null@mailcow.email to any email address you choose by modifying the $RELAY_TO variable on the vars.inc.php file under /opt/mailcow-dockerized/data/web/inc This way you can check that the relay worked by checking the destination mailbox. Set the relayhost for a domain \u00b6 Go to the Domains tab of the Mail setup section of the admin UI. Edit the desired domain. Select the newly added host on the Sender-dependent transports dropdown and save changes. Send an email from a mailbox on that domain and you should see postfix handing the message over to the relayhost in the logs.","title":"Relayhosts"},{"location":"u_e-postfix-relayhost/#add-a-new-relayhost","text":"Go to the Routing tab of the Configuration and Details section of the admin UI. Here you will see a list of relayhosts currently setup. Scroll to the Add sender-dependent transport section. Under Host , add the host you want to relay to. Example: if you want to use Mailgun to send emails instead of your server IP, enter smtp.mailgun.org If the relay host requires a username and password to authenticate, enter them in the respective fields. Keep in mind the credentials will be stored in plain text.","title":"Add a new relayhost"},{"location":"u_e-postfix-relayhost/#test-a-relayhost","text":"To test that connectivity to the host works, click on Test from the list of relayhosts and enter a From: address. Then, run the test. You will then see the results of the SMTP transmission. If all went well, you should see SERVER -> CLIENT: 250 2.0.0 Ok: queued as A093B401D4 as one of the last lines. If not, review the error provided and resolve it. Note: Some hosts, especially those who do not require authentication, will deny connections from servers that have not been added to their system beforehand. Make sure you read the documentation of the relayhost to make sure you've added your domain and/or the server IP to their system. Tip: You can change the default test To: address the test uses from null@mailcow.email to any email address you choose by modifying the $RELAY_TO variable on the vars.inc.php file under /opt/mailcow-dockerized/data/web/inc This way you can check that the relay worked by checking the destination mailbox.","title":"Test a relayhost"},{"location":"u_e-postfix-relayhost/#set-the-relayhost-for-a-domain","text":"Go to the Domains tab of the Mail setup section of the admin UI. Edit the desired domain. Select the newly added host on the Sender-dependent transports dropdown and save changes. Send an email from a mailbox on that domain and you should see postfix handing the message over to the relayhost in the logs.","title":"Set the relayhost for a domain"},{"location":"u_e-postfix-trust_networks/","text":"By default mailcow considers all networks as untrusted excluding its own IPV4_NETWORK and IPV6_NETWORK scopes. Though it is reasonable in most cases, there may be circumstances that you need to loosen this restriction. By default mailcow uses mynetworks_style = subnet to determine internal subnets and leaves mynetworks unconfigured. If you decide to set mynetworks , Postfix ignores the mynetworks_style setting. This means you have to add the IPV4_NETWORK and IPV6_NETWORK scopes as well as loopback subnets manually! Unauthenticated relaying \u00b6 Warning Incorrect setup of mynetworks will allow your server to be used as an open relay. If abused, this will affect your ability to send emails and can take some time to be resolved. IPv4 hosts/subnets \u00b6 To add the subnet 192.168.2.0/24 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 192.168.2.0/24 Run docker-compose restart postfix-mailcow to apply your new settings. IPv6 hosts/subnets \u00b6 Adding IPv6 hosts is done the same as IPv4, however the subnet needs to be placed in brackets [] with the netmask appended. To add the subnet 2001:db8::/32 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 [2001:db8::]/32 Run docker-compose restart postfix-mailcow to apply your new settings. Info More information about mynetworks can be found in the Postfix documentation .","title":"Add trusted networks"},{"location":"u_e-postfix-trust_networks/#unauthenticated-relaying","text":"Warning Incorrect setup of mynetworks will allow your server to be used as an open relay. If abused, this will affect your ability to send emails and can take some time to be resolved.","title":"Unauthenticated relaying"},{"location":"u_e-postfix-trust_networks/#ipv4-hostssubnets","text":"To add the subnet 192.168.2.0/24 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 192.168.2.0/24 Run docker-compose restart postfix-mailcow to apply your new settings.","title":"IPv4 hosts/subnets"},{"location":"u_e-postfix-trust_networks/#ipv6-hostssubnets","text":"Adding IPv6 hosts is done the same as IPv4, however the subnet needs to be placed in brackets [] with the netmask appended. To add the subnet 2001:db8::/32 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 [2001:db8::]/32 Run docker-compose restart postfix-mailcow to apply your new settings. Info More information about mynetworks can be found in the Postfix documentation .","title":"IPv6 hosts/subnets"},{"location":"u_e-redis/","text":"Redis is used as a key-value store for rspamd's and (some of) mailcow's settings and data. If you are unfamiliar with redis please read the introduction to redis and maybe visit this wonderful guide on how to use it. Client \u00b6 To connect to the redis cli execute: docker-compose exec redis-mailcow redis-cli Debugging \u00b6 Here are some useful commands for the redis-cli for debugging: MONITOR \u00b6 Listens for all requests received by the server in real time: # docker-compose exec redis-mailcow redis-cli 127.0.0.1:6379> monitor OK 1494077286.401963 [0 172.22.1.253:41228] \"SMEMBERS\" \"BAYES_SPAM_keys\" 1494077288.292970 [0 172.22.1.253:41229] \"SMEMBERS\" \"BAYES_SPAM_keys\" [...] KEYS \u00b6 Get all keys matching your pattern: KEYS * PING \u00b6 Test a connection: 127.0.0.1:6379> PING PONG If you want to know more, here is a cheat sheet .","title":"Redis"},{"location":"u_e-redis/#client","text":"To connect to the redis cli execute: docker-compose exec redis-mailcow redis-cli","title":"Client"},{"location":"u_e-redis/#debugging","text":"Here are some useful commands for the redis-cli for debugging:","title":"Debugging"},{"location":"u_e-redis/#monitor","text":"Listens for all requests received by the server in real time: # docker-compose exec redis-mailcow redis-cli 127.0.0.1:6379> monitor OK 1494077286.401963 [0 172.22.1.253:41228] \"SMEMBERS\" \"BAYES_SPAM_keys\" 1494077288.292970 [0 172.22.1.253:41229] \"SMEMBERS\" \"BAYES_SPAM_keys\" [...]","title":"MONITOR"},{"location":"u_e-redis/#keys","text":"Get all keys matching your pattern: KEYS *","title":"KEYS"},{"location":"u_e-redis/#ping","text":"Test a connection: 127.0.0.1:6379> PING PONG If you want to know more, here is a cheat sheet .","title":"PING"},{"location":"u_e-reeanble-weak-protocols/","text":"On February the 12th 2020 we disabled the deprecated protocols TLS 1.0 and 1.1 in Dovecot (POP3, POP3S, IMAP, IMAPS) and Postfix (SMTPS, SUBMISSION). Unauthenticated mail via SMTP on port 25/tcp does still accept >= TLS 1.0 . It is better to accept a weak encryption than none at all. How to re-enable weak protocols? Edit data/conf/postfix/extra.cf : submission_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 Edit data/conf/dovecot/extra.conf : ssl_min_protocol = TLSv1 Restart the affected services: docker-compose restart postfix-mailcow dovecot-mailcow Hint: You can enable TLS 1.2 in Windows 7.","title":"Re-enable TLS 1.0 and TLS 1.1"},{"location":"u_e-rspamd/","text":"Rspamd is used for AV handling, DKIM signing and SPAM handling. It's a powerful and fast filter system. For a more in-depth documentation on Rspamd please visit its own documentation . Learn Spam & Ham \u00b6 Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash. This is achieved by using the Sieve plugin \"sieve_imapsieve\" and parser scripts. Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning ). We configured the plugin to keep a sane ratio between spam and ham learns. The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM . Besides bayes, a local fuzzy storage is used to learn recurring patterns in text or images that indicate ham or spam. You can also use Rspamd's web UI to learn ham and / or spam or to adjust certain settings of Rspamd. Learn Spam or Ham from existing directory \u00b6 You can use a one-liner to learn mail in plain-text (uncompressed) format: # Ham for file in /my/folder/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_ham < $file ; done # Spam for file in /my/folder/.Junk/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_spam < $file ; done Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example: for file in /data/old_mail/.Junk/cur/* ; do rspamc learn_spam < zcat $file ; done Reset learned data (Bayes, Neural) \u00b6 You need to delete keys in Redis to reset learned data, so create a copy of your Redis database now: Backup database # It is better to stop Redis before you copy the file. cp /var/lib/docker/volumes/mailcowdockerized_redis-vol-1/_data/dump.rdb /root/ Reset Bayes data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern BAYES_* | xargs redis-cli del' docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern RS* | xargs redis-cli del' Reset Neural data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern rn_* | xargs redis-cli del' Reset Fuzzy data # We need to enter the redis-cli first: docker-compose exec redis-mailcow redis-cli # In redis-cli: 127 .0.0.1:6379> EVAL \"for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end\" 0 fuzzy* Info If redis-cli complains about... (error) ERR wrong number of arguments for 'del' command ...the key pattern was not found and thus no data is available to delete - it is fine. CLI tools \u00b6 docker-compose exec rspamd-mailcow rspamc --help docker-compose exec rspamd-mailcow rspamadm --help Disable Greylisting \u00b6 Only messages with a higher score will be considered to be greylisted (soft rejected). It is bad practice to disable greylisting. You can disable greylisting server-wide by editing: {mailcow-dir}/data/conf/rspamd/local.d/greylist.conf Add the line: enabled = false ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Spam filter thresholds (global) \u00b6 Each user is able to change their spam rating individually . To define a new server-wide limit, edit data/conf/rspamd/local.d/actions.conf : reject = 15 ; add_header = 8 ; greylist = 7 ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Existing settings of users will not be overwritten! To reset custom defined thresholds, run: source mailcow.conf docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel';\" # or: # docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel' and object = 'only-this-mailbox@example.org';\" Custom reject messages \u00b6 The default spam reject message can be changed by adding a new file data/conf/rspamd/override.d/worker-proxy.custom.inc with the following content: reject_message = \"My custom reject message\"; Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . While the above works for rejected mails with a high spam score, prefilter reject actions will ignore this setting. For these maps, the multimap module in Rspamd needs to be adjusted: Find prefilet reject symbol for which you want change message, to do it run: grep -R \"SYMBOL_YOU_WANT_TO_ADJUST\" /opt/mailcow-dockerized/data/conf/rspamd/ Add your custom message as new line: GLOBAL_RCPT_BL { type = \"rcpt\"; map = \"${LOCAL_CONFDIR}/custom/global_rcpt_blacklist.map\"; regexp = true; prefilter = true; action = \"reject\"; message = \"Sending mail to this recipient is prohibited by postmaster@your.domain\"; } Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . Whitelist specific ClamAV signatures \u00b6 You may find that legitimate (clean) mail is being blocked by ClamAV (Rspamd will flag the mail with VIRUS_FOUND ). For instance, interactive PDF form attachments are blocked by default because the embedded Javascript code may be used for nefarious purposes. Confirm by looking at the clamd logs, e.g.: docker-compose logs clamd-mailcow | grep \"FOUND\" This line confirms that such was identified: clamd-mailcow_1 | Sat Sep 28 07:43:24 2019 -> instream(local): PUA.Pdf.Trojan.EmbeddedJavaScript-1(e887d2ac324ce90750768b86b63d0749:363325) FOUND To whitelist this particular signature (and enable sending this type of file attached), add it to the ClamAV signature whitelist file: echo 'PUA.Pdf.Trojan.EmbeddedJavaScript-1' >> data/conf/clamav/whitelist.ign2 Then restart the clamd-mailcow service container in the mailcow UI or using docker-compose: docker-compose restart clamd-mailcow Cleanup cached ClamAV results in Redis: # docker-compose exec redis-mailcow /bin/sh /data # redis-cli KEYS rs_cl* | xargs redis-cli DEL /data # exit Discard instead of reject \u00b6 If you want to silently drop a message, create or edit the file data/conf/rspamd/override.d/worker-proxy.custom.inc and add the following content: discard_on_reject = true; Restart Rspamd: docker-compose restart rspamd-mailcow Wipe all ratelimit keys \u00b6 If you don't want to use the UI and instead wipe all keys in the Redis database, you can use redis-cli for that task: docker-compose exec redis-mailcow sh # Unlink (available in Redis >=4.) will delete in the backgronud redis-cli --scan --pattern RL* | xargs redis-cli unlink Restart Rspamd: docker-compose exec redis-mailcow sh Trigger a resend of quarantine notifications \u00b6 Should be used for debugging only! docker-compose exec dovecot-mailcow bash mysql -umailcow -p$DBPASS mailcow -e \"update quarantine set notified = 0;\" redis-cli -h redis DEL Q_LAST_NOTIFIED quarantine_notify.py Increase history retention \u00b6 By default Rspamd keeps 1000 elements in the history. The history is stored compressed. It is recommended not to use a disproportionate high value here, try something along 5000 or 10000 and see how your server handles it: Edit data/conf/rspamd/local.d/history_redis.conf : nrows = 1000; # change this value Restart Rspamd afterwards: docker-compose restart rspamd-mailcow","title":"Rspamd"},{"location":"u_e-rspamd/#learn-spam-ham","text":"Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash. This is achieved by using the Sieve plugin \"sieve_imapsieve\" and parser scripts. Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning ). We configured the plugin to keep a sane ratio between spam and ham learns. The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM . Besides bayes, a local fuzzy storage is used to learn recurring patterns in text or images that indicate ham or spam. You can also use Rspamd's web UI to learn ham and / or spam or to adjust certain settings of Rspamd.","title":"Learn Spam & Ham"},{"location":"u_e-rspamd/#learn-spam-or-ham-from-existing-directory","text":"You can use a one-liner to learn mail in plain-text (uncompressed) format: # Ham for file in /my/folder/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_ham < $file ; done # Spam for file in /my/folder/.Junk/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_spam < $file ; done Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example: for file in /data/old_mail/.Junk/cur/* ; do rspamc learn_spam < zcat $file ; done","title":"Learn Spam or Ham from existing directory"},{"location":"u_e-rspamd/#reset-learned-data-bayes-neural","text":"You need to delete keys in Redis to reset learned data, so create a copy of your Redis database now: Backup database # It is better to stop Redis before you copy the file. cp /var/lib/docker/volumes/mailcowdockerized_redis-vol-1/_data/dump.rdb /root/ Reset Bayes data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern BAYES_* | xargs redis-cli del' docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern RS* | xargs redis-cli del' Reset Neural data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern rn_* | xargs redis-cli del' Reset Fuzzy data # We need to enter the redis-cli first: docker-compose exec redis-mailcow redis-cli # In redis-cli: 127 .0.0.1:6379> EVAL \"for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end\" 0 fuzzy* Info If redis-cli complains about... (error) ERR wrong number of arguments for 'del' command ...the key pattern was not found and thus no data is available to delete - it is fine.","title":"Reset learned data (Bayes, Neural)"},{"location":"u_e-rspamd/#cli-tools","text":"docker-compose exec rspamd-mailcow rspamc --help docker-compose exec rspamd-mailcow rspamadm --help","title":"CLI tools"},{"location":"u_e-rspamd/#disable-greylisting","text":"Only messages with a higher score will be considered to be greylisted (soft rejected). It is bad practice to disable greylisting. You can disable greylisting server-wide by editing: {mailcow-dir}/data/conf/rspamd/local.d/greylist.conf Add the line: enabled = false ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow","title":"Disable Greylisting"},{"location":"u_e-rspamd/#spam-filter-thresholds-global","text":"Each user is able to change their spam rating individually . To define a new server-wide limit, edit data/conf/rspamd/local.d/actions.conf : reject = 15 ; add_header = 8 ; greylist = 7 ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Existing settings of users will not be overwritten! To reset custom defined thresholds, run: source mailcow.conf docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel';\" # or: # docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel' and object = 'only-this-mailbox@example.org';\"","title":"Spam filter thresholds (global)"},{"location":"u_e-rspamd/#custom-reject-messages","text":"The default spam reject message can be changed by adding a new file data/conf/rspamd/override.d/worker-proxy.custom.inc with the following content: reject_message = \"My custom reject message\"; Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . While the above works for rejected mails with a high spam score, prefilter reject actions will ignore this setting. For these maps, the multimap module in Rspamd needs to be adjusted: Find prefilet reject symbol for which you want change message, to do it run: grep -R \"SYMBOL_YOU_WANT_TO_ADJUST\" /opt/mailcow-dockerized/data/conf/rspamd/ Add your custom message as new line: GLOBAL_RCPT_BL { type = \"rcpt\"; map = \"${LOCAL_CONFDIR}/custom/global_rcpt_blacklist.map\"; regexp = true; prefilter = true; action = \"reject\"; message = \"Sending mail to this recipient is prohibited by postmaster@your.domain\"; } Save the file and restart Rspamd: docker-compose restart rspamd-mailcow .","title":"Custom reject messages"},{"location":"u_e-rspamd/#whitelist-specific-clamav-signatures","text":"You may find that legitimate (clean) mail is being blocked by ClamAV (Rspamd will flag the mail with VIRUS_FOUND ). For instance, interactive PDF form attachments are blocked by default because the embedded Javascript code may be used for nefarious purposes. Confirm by looking at the clamd logs, e.g.: docker-compose logs clamd-mailcow | grep \"FOUND\" This line confirms that such was identified: clamd-mailcow_1 | Sat Sep 28 07:43:24 2019 -> instream(local): PUA.Pdf.Trojan.EmbeddedJavaScript-1(e887d2ac324ce90750768b86b63d0749:363325) FOUND To whitelist this particular signature (and enable sending this type of file attached), add it to the ClamAV signature whitelist file: echo 'PUA.Pdf.Trojan.EmbeddedJavaScript-1' >> data/conf/clamav/whitelist.ign2 Then restart the clamd-mailcow service container in the mailcow UI or using docker-compose: docker-compose restart clamd-mailcow Cleanup cached ClamAV results in Redis: # docker-compose exec redis-mailcow /bin/sh /data # redis-cli KEYS rs_cl* | xargs redis-cli DEL /data # exit","title":"Whitelist specific ClamAV signatures"},{"location":"u_e-rspamd/#discard-instead-of-reject","text":"If you want to silently drop a message, create or edit the file data/conf/rspamd/override.d/worker-proxy.custom.inc and add the following content: discard_on_reject = true; Restart Rspamd: docker-compose restart rspamd-mailcow","title":"Discard instead of reject"},{"location":"u_e-rspamd/#wipe-all-ratelimit-keys","text":"If you don't want to use the UI and instead wipe all keys in the Redis database, you can use redis-cli for that task: docker-compose exec redis-mailcow sh # Unlink (available in Redis >=4.) will delete in the backgronud redis-cli --scan --pattern RL* | xargs redis-cli unlink Restart Rspamd: docker-compose exec redis-mailcow sh","title":"Wipe all ratelimit keys"},{"location":"u_e-rspamd/#trigger-a-resend-of-quarantine-notifications","text":"Should be used for debugging only! docker-compose exec dovecot-mailcow bash mysql -umailcow -p$DBPASS mailcow -e \"update quarantine set notified = 0;\" redis-cli -h redis DEL Q_LAST_NOTIFIED quarantine_notify.py","title":"Trigger a resend of quarantine notifications"},{"location":"u_e-rspamd/#increase-history-retention","text":"By default Rspamd keeps 1000 elements in the history. The history is stored compressed. It is recommended not to use a disproportionate high value here, try something along 5000 or 10000 and see how your server handles it: Edit data/conf/rspamd/local.d/history_redis.conf : nrows = 1000; # change this value Restart Rspamd afterwards: docker-compose restart rspamd-mailcow","title":"Increase history retention"},{"location":"u_e-sogo/","text":"SOGo is used for accessing your mails via a webbrowser, adding and sharing your contacts or calendars. For a more in-depth documentation on SOGo please visit its own documentation . Apply custom SOGo theme \u00b6 mailcow builds after 28 January 2021 can change SOGo's theme by editing data/conf/sogo/custom-theme.js . Please check the AngularJS Material intro and documentation as well as the material style guideline to learn how this works. You can use the provided custom-theme.js as an example starting point by removing the comments. After you modified data/conf/sogo/custom-theme.js and made changes to your new SOGo theme you need to edit data/conf/sogo/sogo.conf and append/set SOGoUIxDebugEnabled = YES; restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Reset to SOGo default theme \u00b6 Checkout data/conf/sogo/custom-theme.js by executing git fetch ; git checkout origin/master data/conf/sogo/custom-theme.js data/conf/sogo/custom-theme.js Find in data/conf/sogo/custom-theme.js : // Apply new palettes to the default theme, remap some of the hues $mdThemingProvider.theme('default') .primaryPalette('green-cow', { 'default': '400', // background color of top toolbars 'hue-1': '400', 'hue-2': '600', // background color of sidebar toolbar 'hue-3': 'A700' }) .accentPalette('green', { 'default': '600', // background color of fab buttons and login screen 'hue-1': '300', // background color of center list toolbar 'hue-2': '300', // highlight color for selected mail and current day calendar 'hue-3': 'A700' }) .backgroundPalette('frost-grey'); and replace with: $mdThemingProvider.theme('default'); Change favicon \u00b6 mailcow builds after 31 January 2021 can change SOGo's favicon by replacing data/conf/sogo/custom-favicon.ico for SOGo and data/web/favicon.png for mailcow UI. Note : You can use .png favicons for SOGo by renaming them to custom-favicon.ico . For both SOGo and mailcow UI favicons you need use one of the standard dimensions: 16x16, 32x32, 64x64, 128x128 and 256x256. After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Change logo \u00b6 mailcow builds after 21 December 2018 can change SOGo's logo by replacing or creating (if missing) data/conf/sogo/sogo-full.svg . After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Connect domains \u00b6 Domains are usually isolated from eachother. You can change that by modifying data/conf/sogo/sogo.conf : Search... // SOGoDomainsVisibility = ( // (domain1.tld, domain5.tld), // (domain3.tld, domain2.tld) // ); ...and replace it by - for example: SOGoDomainsVisibility = ( (example.org, example.com, example.net) ); Restart SOGo: docker-compose restart sogo-mailcow Disable password changing \u00b6 Edit data/conf/sogo/sogo.conf and change SOGoPasswordChangeEnabled to NO . Please do not add a new parameter. Run docker-compose restart memcached-mailcow sogo-mailcow to activate the changes.","title":"SOGo"},{"location":"u_e-sogo/#apply-custom-sogo-theme","text":"mailcow builds after 28 January 2021 can change SOGo's theme by editing data/conf/sogo/custom-theme.js . Please check the AngularJS Material intro and documentation as well as the material style guideline to learn how this works. You can use the provided custom-theme.js as an example starting point by removing the comments. After you modified data/conf/sogo/custom-theme.js and made changes to your new SOGo theme you need to edit data/conf/sogo/sogo.conf and append/set SOGoUIxDebugEnabled = YES; restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Apply custom SOGo theme"},{"location":"u_e-sogo/#reset-to-sogo-default-theme","text":"Checkout data/conf/sogo/custom-theme.js by executing git fetch ; git checkout origin/master data/conf/sogo/custom-theme.js data/conf/sogo/custom-theme.js Find in data/conf/sogo/custom-theme.js : // Apply new palettes to the default theme, remap some of the hues $mdThemingProvider.theme('default') .primaryPalette('green-cow', { 'default': '400', // background color of top toolbars 'hue-1': '400', 'hue-2': '600', // background color of sidebar toolbar 'hue-3': 'A700' }) .accentPalette('green', { 'default': '600', // background color of fab buttons and login screen 'hue-1': '300', // background color of center list toolbar 'hue-2': '300', // highlight color for selected mail and current day calendar 'hue-3': 'A700' }) .backgroundPalette('frost-grey'); and replace with: $mdThemingProvider.theme('default');","title":"Reset to SOGo default theme"},{"location":"u_e-sogo/#change-favicon","text":"mailcow builds after 31 January 2021 can change SOGo's favicon by replacing data/conf/sogo/custom-favicon.ico for SOGo and data/web/favicon.png for mailcow UI. Note : You can use .png favicons for SOGo by renaming them to custom-favicon.ico . For both SOGo and mailcow UI favicons you need use one of the standard dimensions: 16x16, 32x32, 64x64, 128x128 and 256x256. After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Change favicon"},{"location":"u_e-sogo/#change-logo","text":"mailcow builds after 21 December 2018 can change SOGo's logo by replacing or creating (if missing) data/conf/sogo/sogo-full.svg . After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Change logo"},{"location":"u_e-sogo/#connect-domains","text":"Domains are usually isolated from eachother. You can change that by modifying data/conf/sogo/sogo.conf : Search... // SOGoDomainsVisibility = ( // (domain1.tld, domain5.tld), // (domain3.tld, domain2.tld) // ); ...and replace it by - for example: SOGoDomainsVisibility = ( (example.org, example.com, example.net) ); Restart SOGo: docker-compose restart sogo-mailcow","title":"Connect domains"},{"location":"u_e-sogo/#disable-password-changing","text":"Edit data/conf/sogo/sogo.conf and change SOGoPasswordChangeEnabled to NO . Please do not add a new parameter. Run docker-compose restart memcached-mailcow sogo-mailcow to activate the changes.","title":"Disable password changing"},{"location":"u_e-unbound-fwd/","text":"If you want or have to use an external DNS service, you can either set a forwarder in Unbound or copy an override file to define external DNS servers: !!! warning Please do not use a public resolver like we did in the example above. Many - if not all - blacklist lookups will fail with public resolvers. Important : Only DNSSEC validating DNS services will work. Method A, Unbound \u00b6 Edit data/conf/unbound/unbound.conf and append the following parameters: forward-zone: name: \".\" forward-addr: 8.8.8.8 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE forward-addr: 8.8.4.4 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE Restart Unbound: docker-compose restart unbound-mailcow Method B, Override file \u00b6 cd /opt/mailcow-dockerized cp helper-scripts/docker-compose.override.yml.d/EXTERNAL_DNS/docker-compose.override.yml . Edit docker-compose.override.yml and adjust the IP. Run docker-compose down ; docker-compose up -d .","title":"Using an external DNS service"},{"location":"u_e-unbound-fwd/#method-a-unbound","text":"Edit data/conf/unbound/unbound.conf and append the following parameters: forward-zone: name: \".\" forward-addr: 8.8.8.8 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE forward-addr: 8.8.4.4 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE Restart Unbound: docker-compose restart unbound-mailcow","title":"Method A, Unbound"},{"location":"u_e-unbound-fwd/#method-b-override-file","text":"cd /opt/mailcow-dockerized cp helper-scripts/docker-compose.override.yml.d/EXTERNAL_DNS/docker-compose.override.yml . Edit docker-compose.override.yml and adjust the IP. Run docker-compose down ; docker-compose up -d .","title":"Method B, Override file"},{"location":"u_e-update-hooks/","text":"It is possible to add pre- and post-update-hooks to the update.sh script that upgrades your whole mailcow installation. To do so, just add the corresponding bash script into your mailcows root directory: pre_update_hook.sh for commands that should run before the update post_uddate_hook.sh for commands that should run after the update is completed Keep in mind that pre_update_hook.sh runs every time you call update.sh and post_update_hook.sh will only run if the update was successful and the script doesn't have to be re-run. The scripts will be run by bash, an interpreter (e.g. #!/bin/bash ) as well as an execute permission flag (\"+x\") are not required.","title":"Run scripts before and after updates"},{"location":"u_e-webmail-site/","text":"IMPORTANT : This guide only applies to non SNI enabled configurations. The certificate path needs to be adjusted if SNI is enabled. Something like ssl_certificate,key /etc/ssl/mail/webmail.example.org/cert.pem,key.pem; will do. But : The certificate should be acquired first and only after the certificate exists a site config should be created. Nginx will fail to start if it cannot find the certificate and key. To create a subdomain webmail.example.org and redirect it to SOGo, you need to create a new Nginx site. Take care of \"CHANGE_TO_MAILCOW_HOSTNAME\"! nano data/conf/nginx/webmail.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name webmail.example.org; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } location / { return 301 https://CHANGE_TO_MAILCOW_HOSTNAME/SOGo; } } Save and restart Nginx: docker-compose restart nginx-mailcow . Now open mailcow.conf and find ADDITIONAL_SAN . Add webmail.example.org to this array, don't use quotes! ADDITIONAL_SAN=webmail.example.org Run docker-compose up -d . See \"acme-mailcow\" and \"nginx-mailcow\" logs if anything fails.","title":"Create subdomain webmail.example.org"},{"location":"u_e-why_unbound/","text":"For DNS blacklist lookups and DNSSEC. Most systems use either a public or a local caching DNS resolver. That's a very bad idea when it comes to filter spam using DNS-based black hole lists (DNSBL) or similar technics. Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service. Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.","title":"Why unbound?"},{"location":"client/client-android/","text":"Open the Email app. If this is your first email account, tap Add Account ; if not, tap More and Settings and then Add account . Select Microsoft Exchange ActiveSync . Enter your email address ( ) and password. Tap Sign in .","title":"Android"},{"location":"client/client-apple/","text":"Method 1 via Mobileconfig \u00b6 Email, contacts and calendars can be configured automatically on Apple devices by installing a profile. To download a profile you must login to the mailcow UI first. Method 1.1: IMAP, SMTP and Cal/CardDAV \u00b6 This method configures IMAP, CardDAV and CalDAV. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password three times when prompted. Method 1.2: IMAP, SMTP (no DAV) \u00b6 This method configures IMAP and SMTP only. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php?only_email mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password when prompted. Method 2 (Exchange ActiveSync emulation) \u00b6 On iOS, Exchange ActiveSync is also supported as an alternative to the procedure above. It has the advantage of supporting push email (i.e. you are immediately notified of incoming messages), but has some limitations, e.g. it does not support more than three email addresses per contact in your address book. Follow the steps below if you decide to use Exchange instead. Open the Settings app, tap Mail , tap Accounts , tap Add Acccount , select Exchange . Enter your email address ( ) and tap Next . Enter your password, tap Next again. Finally, tap Save .","title":"Apple macOS / iOS"},{"location":"client/client-apple/#method-1-via-mobileconfig","text":"Email, contacts and calendars can be configured automatically on Apple devices by installing a profile. To download a profile you must login to the mailcow UI first.","title":"Method 1 via Mobileconfig"},{"location":"client/client-apple/#method-11-imap-smtp-and-calcarddav","text":"This method configures IMAP, CardDAV and CalDAV. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password three times when prompted.","title":"Method 1.1: IMAP, SMTP and Cal/CardDAV"},{"location":"client/client-apple/#method-12-imap-smtp-no-dav","text":"This method configures IMAP and SMTP only. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php?only_email mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password when prompted.","title":"Method 1.2: IMAP, SMTP (no DAV)"},{"location":"client/client-apple/#method-2-exchange-activesync-emulation","text":"On iOS, Exchange ActiveSync is also supported as an alternative to the procedure above. It has the advantage of supporting push email (i.e. you are immediately notified of incoming messages), but has some limitations, e.g. it does not support more than three email addresses per contact in your address book. Follow the steps below if you decide to use Exchange instead. Open the Settings app, tap Mail , tap Accounts , tap Add Acccount , select Exchange . Enter your email address ( ) and tap Next . Enter your password, tap Next again. Finally, tap Save .","title":"Method 2 (Exchange ActiveSync emulation)"},{"location":"client/client-emclient/","text":"Launch eM Client. If this is the first time you launched eM Client, it asks you to set up your account. Proceed to step 4. Go to Menu at the top, select Tools and Accounts . Enter your email address ( ) and click Start Now . Enter your password and click Continue . Enter your name ( ) and click Next . Click Finish .","title":"eM Client"},{"location":"client/client-kontact/","text":"Launch Kontact. If this is the first time you launched Kontact or KMail, it asks you to set up your account. Proceed to step 4. Go to Mail in the sidebar. Go to the Tools menu and select Account Wizard . Enter your name ( ) , email address ( ) and your password. Click Next . Click Create Account . If prompted, re-enter your password and click OK . Close the window by clicking Finish . Go to Calendar in the sidebar. Go to the Settings menu and select Configure KOrganizer . Go to the Calendars tab and click the Add button. Choose DAV groupware resource and click OK . Enter your email address ( ) and your password. Click Next . Select ScalableOGo from the dropdown menu and click Next . Enter your mailcow hostname into the Host field and click Next . Click Test Connection and then Finish . Finally, click OK twice. Once you have set up Kontact, you can also use KMail, KOrganizer and KAddressBook individually.","title":"KDE Kontact"},{"location":"client/client-manual/","text":"These instructions are valid for unchanged port bindings only! Email \u00b6 Service Encryption Host Port IMAP STARTTLS mailcow hostname 143 IMAPS SSL mailcow hostname 993 POP3 STARTTLS mailcow hostname 110 POP3S SSL mailcow hostname 995 SMTP STARTTLS mailcow hostname 587 SMTPS SSL mailcow hostname 465 Please use \"plain\" as authentication mechanisms. Contrary to the assumption no passwords will be transferred plain text, as no authentication is allowed to take place without TLS. Contacts and calendars \u00b6 SOGos default calendar (CalDAV) and contacts (CardDAV) URLs: CalDAV - https://mail.example.com/SOGo/dav/user@example.com/Calendar/personal/ CardDAV - https://mail.example.com/SOGo/dav/user@example.com/Contacts/personal/ Some applications may require you to use https://mail.example.com/SOGo/dav/ or the full path to your calendar, which can be found and copied from within SOGo.","title":"Manual configuration"},{"location":"client/client-manual/#email","text":"Service Encryption Host Port IMAP STARTTLS mailcow hostname 143 IMAPS SSL mailcow hostname 993 POP3 STARTTLS mailcow hostname 110 POP3S SSL mailcow hostname 995 SMTP STARTTLS mailcow hostname 587 SMTPS SSL mailcow hostname 465 Please use \"plain\" as authentication mechanisms. Contrary to the assumption no passwords will be transferred plain text, as no authentication is allowed to take place without TLS.","title":"Email"},{"location":"client/client-manual/#contacts-and-calendars","text":"SOGos default calendar (CalDAV) and contacts (CardDAV) URLs: CalDAV - https://mail.example.com/SOGo/dav/user@example.com/Calendar/personal/ CardDAV - https://mail.example.com/SOGo/dav/user@example.com/Contacts/personal/ Some applications may require you to use https://mail.example.com/SOGo/dav/ or the full path to your calendar, which can be found and copied from within SOGo.","title":"Contacts and calendars"},{"location":"client/client-outlook/","text":"Outlook 2016 or higher from Office 365 on Windows \u00b6 This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Outlook 2016 has an issue with autodiscover . Only Outlook from Office 365 is affected. If you installed Outlook from another source, please follow the guide for Outlook 2013 or higher. For EAS you must use the old assistant by launching C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\OLCFG.EXE . If this application opens, you can go to step 4 of the guide for Outlook 2013 below. If it does not open, you can completely disable the new account creation wizard and follow the guide for Outlook 2013 below. Outlook 2013 or higher on Windows \u00b6 This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 4. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . When prompted, enter your password again, check Remember my credentials and click OK . Click the Allow button. Click Finish . Outlook 2007 or 2010 on Windows \u00b6 Outlook 2007 or higher on Windows \u00b6 Download and install Outlook CalDav Synchronizer . Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 5. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . Click Finish . Go to the CalDav Synchronizer ribbon and click Synchronization Profiles . Click the second button at top ( Add multiple profiles ), select Sogo , click Ok . Click the Get IMAP/POP3 account settings button. Click Discover resources and assign to Outlook folders . In the Select Resource window that pops up, select your main calendar (usually Personal Calendar ), click the ... button, assign it to Calendar , and click OK . Go to the Address Books and Tasks tabs and repeat repeat the process accordingly. Do not assign multiple calendars, address books or task lists! Close all windows with the OK buttons. Outlook 2011 or higher on macOS \u00b6 The Mac version of Outlook does not synchronize calendars and contacts and therefore is not supported.","title":"Microsoft Outlook"},{"location":"client/client-outlook/#outlook-2016-or-higher-from-office-365-on-windows","text":"This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Outlook 2016 has an issue with autodiscover . Only Outlook from Office 365 is affected. If you installed Outlook from another source, please follow the guide for Outlook 2013 or higher. For EAS you must use the old assistant by launching C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\OLCFG.EXE . If this application opens, you can go to step 4 of the guide for Outlook 2013 below. If it does not open, you can completely disable the new account creation wizard and follow the guide for Outlook 2013 below.","title":"Outlook 2016 or higher from Office 365 on Windows"},{"location":"client/client-outlook/#outlook-2013-or-higher-on-windows","text":"This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 4. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . When prompted, enter your password again, check Remember my credentials and click OK . Click the Allow button. Click Finish .","title":"Outlook 2013 or higher on Windows"},{"location":"client/client-outlook/#outlook-2007-or-2010-on-windows","text":"","title":"Outlook 2007 or 2010 on Windows"},{"location":"client/client-outlook/#outlook-2007-or-higher-on-windows","text":"Download and install Outlook CalDav Synchronizer . Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 5. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . Click Finish . Go to the CalDav Synchronizer ribbon and click Synchronization Profiles . Click the second button at top ( Add multiple profiles ), select Sogo , click Ok . Click the Get IMAP/POP3 account settings button. Click Discover resources and assign to Outlook folders . In the Select Resource window that pops up, select your main calendar (usually Personal Calendar ), click the ... button, assign it to Calendar , and click OK . Go to the Address Books and Tasks tabs and repeat repeat the process accordingly. Do not assign multiple calendars, address books or task lists! Close all windows with the OK buttons.","title":"Outlook 2007 or higher on Windows"},{"location":"client/client-outlook/#outlook-2011-or-higher-on-macos","text":"The Mac version of Outlook does not synchronize calendars and contacts and therefore is not supported.","title":"Outlook 2011 or higher on macOS"},{"location":"client/client-thunderbird/","text":"Launch Thunderbird. If this is the first time you launched Thunderbird, it asks you whether you would like a new email address. Click Skip this and use my existing email and proceed to step 4. Go to the Tools menu and select Account Settings . Click the Account Actions dropdown menu at the bottom left and select Add Mail Account . Enter your name ( ) , email address ( ) and your password. Make sure the Remember password checkbox is selected and click Continue . Once the configuration has been automatically detected, click Done . If you already had other accounts configured in Thunderbird, select the new one ( ) on the left, click the Account Actions dropdown and select Set as Default . Close the account settings window with the OK button. In your web browser, download SOGo Connector SOGo Connector . Back in Thunderbird, go to the Tools menu and select Add-ons . Click Extensions on the left and ensure that the Lightning add-on is already installed. It is installed by default in the Windows and macOS versions of Thunderbird, but if you are running Linux and installed Thunderbird through your distribution's package manager, Lightning might be available as a separate package (e.g. xul-ext-lightning on Ubuntu). Click Extensions on the left, click the little gear icon at the top and select Install Add-on From File . Select the file you downloaded in step 9, click Open and, after waiting for a few seconds, Install Now . Click the Restart Now button at the top that appears. Thunderbird briefly shows a message that it is updating extensions, then restarts automatically once more. When you are prompted to authenticate for , enter your email address and password, check Use Password Manager and click OK . Automatic configuration of calendars and address books in Thunderbird is not currently supported. You can ask your server administrator to enable SOGo Connector if you need it. Automatic configuration of calendars and address books (from step 9 onward) in Thunderbird is only supported if your server administrator has enabled SOGo Connector . Different method of connecting Cal-/CardDAV in Thunderbird with automatic detection of address books and calendars \u00b6 Instead of using SOGo Connector you can use a combination of https://addons.thunderbird.net/de/thunderbird/addon/tbsync/ and https://addons.thunderbird.net/de/thunderbird/addon/dav-4-tbsync/ To add your Cal-/CardDAV accounts go to Tools and find TbSync You can add new accounts via the CalDAV & CardDAV provider: Choose \"Automatic Configuration\". Use your mail address as account and username. Use your mail password as DAV password. The server URL is your MAILCOW_HOSTNAME (specifying any protocol is not necessary, just enter the full domain). Now tick the checkbox for \"Enable and synchronize this account\" in the synchronization status tab: Several available resources should appear in the same window area now. Tick all checkboxes of the resources (address books and calendars) that you want to sync. Choose a synchronization period (in minutes) in the same window area before clicking on \"Synchronize now\". If you leave the sync at \"0\" it will only sync manually so choose at least 30 minutes for periodic synchronization. If you want to manually synchronize you can find this option under \"Account actions\" - the dropdown-menu where you added the Cal-/CardDAV account (step 2).","title":"Mozilla Thunderbird"},{"location":"client/client-thunderbird/#different-method-of-connecting-cal-carddav-in-thunderbird-with-automatic-detection-of-address-books-and-calendars","text":"Instead of using SOGo Connector you can use a combination of https://addons.thunderbird.net/de/thunderbird/addon/tbsync/ and https://addons.thunderbird.net/de/thunderbird/addon/dav-4-tbsync/ To add your Cal-/CardDAV accounts go to Tools and find TbSync You can add new accounts via the CalDAV & CardDAV provider: Choose \"Automatic Configuration\". Use your mail address as account and username. Use your mail password as DAV password. The server URL is your MAILCOW_HOSTNAME (specifying any protocol is not necessary, just enter the full domain). Now tick the checkbox for \"Enable and synchronize this account\" in the synchronization status tab: Several available resources should appear in the same window area now. Tick all checkboxes of the resources (address books and calendars) that you want to sync. Choose a synchronization period (in minutes) in the same window area before clicking on \"Synchronize now\". If you leave the sync at \"0\" it will only sync manually so choose at least 30 minutes for periodic synchronization. If you want to manually synchronize you can find this option under \"Account actions\" - the dropdown-menu where you added the Cal-/CardDAV account (step 2).","title":"Different method of connecting Cal-/CardDAV in Thunderbird with automatic detection of address books and calendars"},{"location":"client/client-windows/","text":"Windows 8 and higher support email, contacts and calendar via Exchange ActiveSync. Open the Mail app. If you have not previously used Mail, you can click Add Account in the main window. Proceed to step 4. Click Accounts in the sidebar on the left, then click Add Account on the far right. Select Exchange . Enter your email address ( ) and click Next . Enter your password and click Log in . Once you have set up the Mail app, you can also use the People and Calendar apps.","title":"Windows Mail"},{"location":"client/client-windowsphone/","text":"Open the Settings app. Select email + accounts and tap add an account . Tap Exchange . Enter your email address ( ) and your password. Tap Sign in . Tap done .","title":"Windows Phone"}]} \ No newline at end of file +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"\ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95 \u00b6 Help mailcow \u00b6 Please consider a support contract for a small monthly fee at Servercow EN / Servercow DE to support further development. We support you while you support us . :) If you are super awesome and would like to support without a contract, you can get a SAL license that confirms your awesomeness (a flexible one-time payment) at Servercow EN / Servercow DE . Get support \u00b6 There are two ways to achieve support for your mailcow installation. Commercial support \u00b6 For professional and prioritized commercial support you can sign a basic support subscription at Servercow EN / Servercow DE . For custom inquiries or questions please contact us at info@servercow.de instead. Furthermore we do also provide a fully featured and managed mailcow here . This way we take care about the technical magic underneath and you can enjoy your whole mail experience in a hassle-free way. Community support and chat \u00b6 The other alternative is our free community-support on our various channels below. Please notice, that this support is driven by our awesome community around mailcow. This kind of support is best-effort, voluntary and there is no guarantee for anything. Our mailcow community @ community.mailcow.email Telegram @ t.me/mailcow . Telegram @ t.me/mailcowOfftopic . Telegram desktop clients are available for multiple platforms . You can search the groups history for keywords. For bug tracking, feature requests and code contributions only: GitHub @ mailcow/mailcow-dockerized Demo \u00b6 You can find a demo at demo.mailcow.email , use the following credentials to login: Administrator : admin / moohoo Domain administrator : department / moohoo Mailbox : demo@440044.xyz / moohoo Overview \u00b6 The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: DKIM and ARC support Black- and whitelists per domain and per user Spam score management per-user (reject spam, mark spam, greylist) Allow mailbox users to create temporary spam aliases Prepend mail tags to subject or move mail to sub folder (per-user) Allow mailbox users to toggle incoming and outgoing TLS enforcement Allow users to reset SOGo ActiveSync device caches imapsync to migrate or pull remote mailboxes regularly TFA: Yubikey OTP and U2F USB (Google Chrome and derivatives only), TOTP Add domains, mailboxes, aliases, domain aliases and SOGo resources Add whitelisted hosts to forward mail to mailcow Fail2ban-like integration Quarantine system Antivirus scanning incl. macro scanning in office documents Integrated basic monitoring A lot more... mailcow: dockerized comes with multiple containers linked in one bridged network. Each container represents a single application. ACME ClamAV (optional) Dovecot MariaDB Memcached Netfilter (Fail2ban-like integration by @mkuron ) Nginx Oletools via Olefy PHP Postfix Redis Rspamd SOGo Solr (optional) Unbound A Watchdog to provide basic monitoring Docker volumes to keep dynamic data - take care of them! crypt-vol-1 mysql-socket-vol-1 mysql-vol-1 postfix-vol-1 redis-vol-1 rspamd-vol-1 sogo-userdata-backup-vol-1 sogo-web-vol-1 solr-vol-1 vmail-index-vol-1 vmail-vol-1","title":"Information & Support"},{"location":"#_1","text":"","title":"\ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95"},{"location":"#help-mailcow","text":"Please consider a support contract for a small monthly fee at Servercow EN / Servercow DE to support further development. We support you while you support us . :) If you are super awesome and would like to support without a contract, you can get a SAL license that confirms your awesomeness (a flexible one-time payment) at Servercow EN / Servercow DE .","title":"Help mailcow"},{"location":"#get-support","text":"There are two ways to achieve support for your mailcow installation.","title":"Get support"},{"location":"#commercial-support","text":"For professional and prioritized commercial support you can sign a basic support subscription at Servercow EN / Servercow DE . For custom inquiries or questions please contact us at info@servercow.de instead. Furthermore we do also provide a fully featured and managed mailcow here . This way we take care about the technical magic underneath and you can enjoy your whole mail experience in a hassle-free way.","title":"Commercial support"},{"location":"#community-support-and-chat","text":"The other alternative is our free community-support on our various channels below. Please notice, that this support is driven by our awesome community around mailcow. This kind of support is best-effort, voluntary and there is no guarantee for anything. Our mailcow community @ community.mailcow.email Telegram @ t.me/mailcow . Telegram @ t.me/mailcowOfftopic . Telegram desktop clients are available for multiple platforms . You can search the groups history for keywords. For bug tracking, feature requests and code contributions only: GitHub @ mailcow/mailcow-dockerized","title":"Community support and chat"},{"location":"#demo","text":"You can find a demo at demo.mailcow.email , use the following credentials to login: Administrator : admin / moohoo Domain administrator : department / moohoo Mailbox : demo@440044.xyz / moohoo","title":"Demo"},{"location":"#overview","text":"The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: DKIM and ARC support Black- and whitelists per domain and per user Spam score management per-user (reject spam, mark spam, greylist) Allow mailbox users to create temporary spam aliases Prepend mail tags to subject or move mail to sub folder (per-user) Allow mailbox users to toggle incoming and outgoing TLS enforcement Allow users to reset SOGo ActiveSync device caches imapsync to migrate or pull remote mailboxes regularly TFA: Yubikey OTP and U2F USB (Google Chrome and derivatives only), TOTP Add domains, mailboxes, aliases, domain aliases and SOGo resources Add whitelisted hosts to forward mail to mailcow Fail2ban-like integration Quarantine system Antivirus scanning incl. macro scanning in office documents Integrated basic monitoring A lot more... mailcow: dockerized comes with multiple containers linked in one bridged network. Each container represents a single application. ACME ClamAV (optional) Dovecot MariaDB Memcached Netfilter (Fail2ban-like integration by @mkuron ) Nginx Oletools via Olefy PHP Postfix Redis Rspamd SOGo Solr (optional) Unbound A Watchdog to provide basic monitoring Docker volumes to keep dynamic data - take care of them! crypt-vol-1 mysql-socket-vol-1 mysql-vol-1 postfix-vol-1 redis-vol-1 rspamd-vol-1 sogo-userdata-backup-vol-1 sogo-web-vol-1 solr-vol-1 vmail-index-vol-1 vmail-vol-1","title":"Overview"},{"location":"b_n_r_accidental_deletion/","text":"So you deleted a mailbox and have no backups, he? If you noticed your mistake within a few hours, you can probably recover the users data. SOGo \u00b6 We automatically create daily backups (24h interval starting from running up -d) in /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/ . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the file named after the user you want to restore to __MAILCOW_DIRECTORY__/data/conf/sogo . 1. Copy the backup: cp /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/restoreme@example.org __MAILCOW_DIRECTORY__/data/conf/sogo 2. Run docker-compose exec -u sogo sogo-mailcow sogo-tool restore -F ALL /etc/sogo restoreme@example.org Run sogo-tool without parameters to check for possible restore options. 3. Delete the copied backup by running rm __MAILCOW_DIRECTORY__/data/conf/sogo 4. Restart SOGo and Memcached: docker-compose restart sogo-mailcow memcached-mailcow Mail \u00b6 In case of an accidental deletion of a mailbox, you will be able to recover for (by default) 5 days. This depends on the MAILDIR_GC_TIME parameter in mailcow.conf . A deleted mailbox is copied in its encrypted form to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage . The folder inside _garbage follows the structure [timestamp]_[domain_sanitized][user_sanitized] , for example `1629109708_exampleorgtest in case of test@example.org deleted on 1629109708. To restore make sure you are actually restoring to the same mailcow it was deleted from or you use the same encryption keys in crypt-vol-1 . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the folders from /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage/[timestamp]_[domain_sanitized][user_sanitized] back to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/[domain]/[user] and resync the folder and recalc the quota: docker-compose exec dovecot-mailcow doveadm force-resync -u restoreme@example.net '*' docker-compose exec dovecot-mailcow doveadm quota recalc -u restoreme@example.net","title":"Recover accidentally deleted data"},{"location":"b_n_r_accidental_deletion/#sogo","text":"We automatically create daily backups (24h interval starting from running up -d) in /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/ . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the file named after the user you want to restore to __MAILCOW_DIRECTORY__/data/conf/sogo . 1. Copy the backup: cp /var/lib/docker/volumes/mailcowdockerized_sogo-userdata-backup-vol-1/_data/restoreme@example.org __MAILCOW_DIRECTORY__/data/conf/sogo 2. Run docker-compose exec -u sogo sogo-mailcow sogo-tool restore -F ALL /etc/sogo restoreme@example.org Run sogo-tool without parameters to check for possible restore options. 3. Delete the copied backup by running rm __MAILCOW_DIRECTORY__/data/conf/sogo 4. Restart SOGo and Memcached: docker-compose restart sogo-mailcow memcached-mailcow","title":"SOGo"},{"location":"b_n_r_accidental_deletion/#mail","text":"In case of an accidental deletion of a mailbox, you will be able to recover for (by default) 5 days. This depends on the MAILDIR_GC_TIME parameter in mailcow.conf . A deleted mailbox is copied in its encrypted form to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage . The folder inside _garbage follows the structure [timestamp]_[domain_sanitized][user_sanitized] , for example `1629109708_exampleorgtest in case of test@example.org deleted on 1629109708. To restore make sure you are actually restoring to the same mailcow it was deleted from or you use the same encryption keys in crypt-vol-1 . Make sure the user you want to restore exists in your mailcow . Re-create them if they are missing. Copy the folders from /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/_garbage/[timestamp]_[domain_sanitized][user_sanitized] back to /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/[domain]/[user] and resync the folder and recalc the quota: docker-compose exec dovecot-mailcow doveadm force-resync -u restoreme@example.net '*' docker-compose exec dovecot-mailcow doveadm quota recalc -u restoreme@example.net","title":"Mail"},{"location":"b_n_r_backup/","text":"Backup \u00b6 Manual \u00b6 You can use the provided script helper-scripts/backup_and_restore.sh to backup mailcow automatically. Please do not copy this script to another location. To run a backup, write \"backup\" as first parameter and either one or more components to backup as following parameters. You can also use \"all\" as second parameter to backup all components. Append --delete-days n to delete backups older than n days. # Syntax: # ./helper-scripts/backup_and_restore.sh backup (vmail|crypt|redis|rspamd|postfix|mysql|all|--delete-days) # Backup all, delete backups older than 3 days ./helper-scripts/backup_and_restore.sh backup all --delete-days 3 # Backup vmail, crypt and mysql data, delete backups older than 30 days ./helper-scripts/backup_and_restore.sh backup vmail crypt mysql --delete-days 30 # Backup vmail ./helper-scripts/backup_and_restore.sh backup vmail The script will ask you for a backup location. Inside of this location it will create folders in the format \"mailcow_DATE\". You should not rename those folders to not break the restore process. To run a backup unattended, define MAILCOW_BACKUP_LOCATION as environment variable before starting the script: MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all Cronjob \u00b6 You can run the backup script regularly via cronjob. Make sure BACKUP_LOCATION exists: 5 4 * * * cd /opt/mailcow-dockerized/; MAILCOW_BACKUP_LOCATION=/mnt/mailcow_backups /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 Per default cron sends the full result of each backup operation by email. If you want cron to only mail on error (non-zero exit code) you may want to use the following snippet. Pathes need to be modified according to your setup (this script is a user contribution). This following script may be placed in /etc/cron.daily/mailcow-backup - do not forget to mark it as executable via chmod +x : #!/bin/sh # Backup mailcow data # https://mailcow.github.io/mailcow-dockerized-docs/b_n_r_backup/ set -e OUT=\"$(mktemp)\" export MAILCOW_BACKUP_LOCATION=\"/opt/backup\" SCRIPT=\"/opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh\" PARAMETERS=\"backup all\" OPTIONS=\"--delete-days 30\" # run command set +e \"${SCRIPT}\" ${PARAMETERS} ${OPTIONS} 2>&1 > \"$OUT\" RESULT=$? if [ $RESULT -ne 0 ] then echo \"${SCRIPT} ${PARAMETERS} ${OPTIONS} encounters an error:\" echo \"RESULT=$RESULT\" echo \"STDOUT / STDERR:\" cat \"$OUT\" fi Backup strategy with rsync and mailcow backup script \u00b6 Create the destination directory for mailcows helper script: mkdir -p /external_share/backups/backup_script Create cronjobs: 25 1 * * * rsync -aH --delete /opt/mailcow-dockerized /external_share/backups/mailcow-dockerized 40 2 * * * rsync -aH --delete /var/lib/docker/volumes /external_share/backups/var_lib_docker_volumes 5 4 * * * cd /opt/mailcow-dockerized/; BACKUP_LOCATION=/external_share/backups/backup_script /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 # If you want to, use the acl util to backup permissions of some/all folders/files: getfacl -Rn /path On the destination (in this case /external_share/backups ) you may want to have snapshot capabilities (ZFS, Btrfs etc.). Snapshot daily and keep for n days for a consistent backup. Do not rsync to a Samba share, you need to keep the correct permissions! To restore you'd simply need to run rsync the other way round and restart Docker to re-read the volumes. Run docker-compose pull and docker-compose up -d . If you are lucky Redis and MariaDB can automatically fix the inconsistent databases (if they are inconsistent). In case of a corrupted database you'd need to use the helper script to restore the inconsistent elements. If a restore fails, try to extract the backups and copy the files back manually. Keep the file permissions!","title":"Backup"},{"location":"b_n_r_backup/#backup","text":"","title":"Backup"},{"location":"b_n_r_backup/#manual","text":"You can use the provided script helper-scripts/backup_and_restore.sh to backup mailcow automatically. Please do not copy this script to another location. To run a backup, write \"backup\" as first parameter and either one or more components to backup as following parameters. You can also use \"all\" as second parameter to backup all components. Append --delete-days n to delete backups older than n days. # Syntax: # ./helper-scripts/backup_and_restore.sh backup (vmail|crypt|redis|rspamd|postfix|mysql|all|--delete-days) # Backup all, delete backups older than 3 days ./helper-scripts/backup_and_restore.sh backup all --delete-days 3 # Backup vmail, crypt and mysql data, delete backups older than 30 days ./helper-scripts/backup_and_restore.sh backup vmail crypt mysql --delete-days 30 # Backup vmail ./helper-scripts/backup_and_restore.sh backup vmail The script will ask you for a backup location. Inside of this location it will create folders in the format \"mailcow_DATE\". You should not rename those folders to not break the restore process. To run a backup unattended, define MAILCOW_BACKUP_LOCATION as environment variable before starting the script: MAILCOW_BACKUP_LOCATION=/opt/backup /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all","title":"Manual"},{"location":"b_n_r_backup/#cronjob","text":"You can run the backup script regularly via cronjob. Make sure BACKUP_LOCATION exists: 5 4 * * * cd /opt/mailcow-dockerized/; MAILCOW_BACKUP_LOCATION=/mnt/mailcow_backups /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 Per default cron sends the full result of each backup operation by email. If you want cron to only mail on error (non-zero exit code) you may want to use the following snippet. Pathes need to be modified according to your setup (this script is a user contribution). This following script may be placed in /etc/cron.daily/mailcow-backup - do not forget to mark it as executable via chmod +x : #!/bin/sh # Backup mailcow data # https://mailcow.github.io/mailcow-dockerized-docs/b_n_r_backup/ set -e OUT=\"$(mktemp)\" export MAILCOW_BACKUP_LOCATION=\"/opt/backup\" SCRIPT=\"/opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh\" PARAMETERS=\"backup all\" OPTIONS=\"--delete-days 30\" # run command set +e \"${SCRIPT}\" ${PARAMETERS} ${OPTIONS} 2>&1 > \"$OUT\" RESULT=$? if [ $RESULT -ne 0 ] then echo \"${SCRIPT} ${PARAMETERS} ${OPTIONS} encounters an error:\" echo \"RESULT=$RESULT\" echo \"STDOUT / STDERR:\" cat \"$OUT\" fi","title":"Cronjob"},{"location":"b_n_r_backup/#backup-strategy-with-rsync-and-mailcow-backup-script","text":"Create the destination directory for mailcows helper script: mkdir -p /external_share/backups/backup_script Create cronjobs: 25 1 * * * rsync -aH --delete /opt/mailcow-dockerized /external_share/backups/mailcow-dockerized 40 2 * * * rsync -aH --delete /var/lib/docker/volumes /external_share/backups/var_lib_docker_volumes 5 4 * * * cd /opt/mailcow-dockerized/; BACKUP_LOCATION=/external_share/backups/backup_script /opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup mysql crypt redis --delete-days 3 # If you want to, use the acl util to backup permissions of some/all folders/files: getfacl -Rn /path On the destination (in this case /external_share/backups ) you may want to have snapshot capabilities (ZFS, Btrfs etc.). Snapshot daily and keep for n days for a consistent backup. Do not rsync to a Samba share, you need to keep the correct permissions! To restore you'd simply need to run rsync the other way round and restart Docker to re-read the volumes. Run docker-compose pull and docker-compose up -d . If you are lucky Redis and MariaDB can automatically fix the inconsistent databases (if they are inconsistent). In case of a corrupted database you'd need to use the helper script to restore the inconsistent elements. If a restore fails, try to extract the backups and copy the files back manually. Keep the file permissions!","title":"Backup strategy with rsync and mailcow backup script"},{"location":"b_n_r_restore/","text":"Restore \u00b6 Please do not copy this script to another location. To run a restore, start mailcow , use the script with \"restore\" as first parameter. # Syntax: # ./helper-scripts/backup_and_restore.sh restore The script will ask you for a backup location containing the mailcow_DATE folders.","title":"Restore"},{"location":"b_n_r_restore/#restore","text":"Please do not copy this script to another location. To run a restore, start mailcow , use the script with \"restore\" as first parameter. # Syntax: # ./helper-scripts/backup_and_restore.sh restore The script will ask you for a backup location containing the mailcow_DATE folders.","title":"Restore"},{"location":"client/","text":"mailcow supports a variety of email clients, both on desktop computers and on smartphones. Below, you can find a number of configuration guides that explain how to configure your mailcow account. Tip If you access this page by logging into your mailcow server and clicking the \"Show configuration guides for email clients and smartphones\" link, all of the guides will be personalized with your email address and server name. Success Since you accessed this page after logging into your mailcow server, all of the guides have been personalized with your email address and server name. Android Apple iOS / macOS eM Client KDE Kontact / KMail Microsoft Outlook Mozilla Thunderbird Windows Mail Windows Phone Manual configuration","title":"Overview"},{"location":"debug-admin_login_sogo/","text":"This is an experimental feature that allows admins and domain admins to directly log into SOGo as a mailbox user, without knowing the users password. For this, an additional link to SOGo is displayed in the mailbox list. Multiple concurrent admin-logins to different mailboxes are also possible when using this feature. Enabling the feature \u00b6 The feature is disabled by default. It can be enabled in the mailcow.conf by setting: ALLOW_ADMIN_EMAIL_LOGIN=y and recreating the affected containers with docker-compose up -d Drawbacks when enabled \u00b6 Each SOGo page-load and each Active-Sync request will cause an additional execution of an internal PHP script. This might impact load-times of SOGo / EAS. In most cases, this should not be noticeable but should be kept in mind if you face any performance issues. SOGo will not display a logout link for admin-logins, to login normally one has to logout from the mailcow UI so the PHP session is destroyed. Technical details \u00b6 SOGoTrustProxyAuthentication option is set to YES which makes SOGo trust the x-webobjects-remote-user header. Dovecot will receive a random master-password which is valid for all mailboxes when used by the SOGo container. Clicking on the SOGo button in the mailbox list will open sogo-auth.php which checks permissions, sets session variables and redirects to the SOGo mailbox. Each SOGo, CardDAV, CalDAV and EAS http request will cause an additional, nginx internal auth_request call to sogo-auth.php with the following behavior: If a basic_auth header is present, the script will validate the credentials in place of SOGo and provide the following headers: x-webobjects-remote-user , Authorization and x-webobjects-auth-type . If no basic_auth header is present, the script will check for an active mailcow admin session for the requested email user and provide the same headers but with the dovecot master password used in the Authorization header. If both fails the headers will be set empty, which makes SOGo use its standard authentication methods. All of these options / behaviors are disabled if the ALLOW_ADMIN_EMAIL_LOGIN is not enabled in the config.","title":"Admin login to SOGo"},{"location":"debug-admin_login_sogo/#enabling-the-feature","text":"The feature is disabled by default. It can be enabled in the mailcow.conf by setting: ALLOW_ADMIN_EMAIL_LOGIN=y and recreating the affected containers with docker-compose up -d","title":"Enabling the feature"},{"location":"debug-admin_login_sogo/#drawbacks-when-enabled","text":"Each SOGo page-load and each Active-Sync request will cause an additional execution of an internal PHP script. This might impact load-times of SOGo / EAS. In most cases, this should not be noticeable but should be kept in mind if you face any performance issues. SOGo will not display a logout link for admin-logins, to login normally one has to logout from the mailcow UI so the PHP session is destroyed.","title":"Drawbacks when enabled"},{"location":"debug-admin_login_sogo/#technical-details","text":"SOGoTrustProxyAuthentication option is set to YES which makes SOGo trust the x-webobjects-remote-user header. Dovecot will receive a random master-password which is valid for all mailboxes when used by the SOGo container. Clicking on the SOGo button in the mailbox list will open sogo-auth.php which checks permissions, sets session variables and redirects to the SOGo mailbox. Each SOGo, CardDAV, CalDAV and EAS http request will cause an additional, nginx internal auth_request call to sogo-auth.php with the following behavior: If a basic_auth header is present, the script will validate the credentials in place of SOGo and provide the following headers: x-webobjects-remote-user , Authorization and x-webobjects-auth-type . If no basic_auth header is present, the script will check for an active mailcow admin session for the requested email user and provide the same headers but with the dovecot master password used in the Authorization header. If both fails the headers will be set empty, which makes SOGo use its standard authentication methods. All of these options / behaviors are disabled if the ALLOW_ADMIN_EMAIL_LOGIN is not enabled in the config.","title":"Technical details"},{"location":"debug-asan_rspamd/","text":"A quick guide to deeply analyze a malfunctioning Rspamd. docker-compose exec rspamd-mailcow bash if ! grep -qi 'apt-stable-asan' /etc/apt/sources.list.d/rspamd.list; then sed -i 's/apt-stable/apt-stable-asan/i' /etc/apt/sources.list.d/rspamd.list fi apt-get update ; apt-get upgrade rspamd nano /docker-entrypoint.sh # Before \"exec \"$@\"\" add the following lines: export G_SLICE=always-malloc export ASAN_OPTIONS=new_delete_type_mismatch=0:detect_leaks=1:detect_odr_violation=0:log_path=/tmp/rspamd-asan:quarantine_size_mb=2048:malloc_context_size=8:fast_unwind_on_malloc=0 Restart Rspamd: docker-compose restart rspamd-mailcow Your memory consumption will increase by a lot, it will also steadily grow, which is not related to a possible memory leak you are looking for. Leave the container running for a few minutes, hours or days (it should match the time you usually wait for the leak to \"happen\") and restart it: docker-compose restart rspamd-mailcow . Now enter the container by running docker-compose exec rspamd-mailcow bash , change the directory to /tmp and copy the asan Files to your desired location or upload them via termbin.com ( cat /tmp/rspamd-asan.* | nc termbin.com 9999 ).","title":"Advanced: Find memory leaks in Rspamd"},{"location":"debug-attach_service/","text":"Attaching a Container to your Shell \u00b6 To attach a container to your shell you can simply run docker-compose exec $Service_Name /bin/bash Connecting to Services \u00b6 If you want to connect to a service / application directly it is always a good idea to source mailcow.conf to get all relevant variables into your environment. MySQL \u00b6 source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} Redis \u00b6 docker-compose exec redis-mailcow redis-cli Service Descriptions \u00b6 Here is a brief overview of what container / service does what: Service Name Service Descriptions unbound-mailcow Local (DNSSEC) DNS Resolver mysql-mailcow Stores SOGo's and most of mailcow's settings postfix-mailcow Receives and sends mails dovecot-mailcow User logins and sieve filter redis-mailcow Storage back-end for DKIM keys and Rspamd rspamd-mailcow Mail filtering system. Used for av handling, dkim signing, spam handling clamd-mailcow Scans attachments for viruses olefy-mailcow Scans attached office documents for macro-viruses solr-mailcow Provides full-text search in Dovecot sogo-mailcow Webmail client that handles Microsoft ActiveSync and Cal- / CardDav nginx-mailcow Nginx remote proxy that handles all mailcow related HTTP / HTTPS requests acme-mailcow Automates HTTPS (SSL/TLS) certificate deployment memcached-mailcow Internal caching system for mailcow services watchdog-mailcow Allows the monitoring of docker containers / services php-fpm-mailcow Powers the mailcow web UI netfilter-mailcow Fail2Ban like integration","title":"Attach a Container"},{"location":"debug-attach_service/#attaching-a-container-to-your-shell","text":"To attach a container to your shell you can simply run docker-compose exec $Service_Name /bin/bash","title":"Attaching a Container to your Shell"},{"location":"debug-attach_service/#connecting-to-services","text":"If you want to connect to a service / application directly it is always a good idea to source mailcow.conf to get all relevant variables into your environment.","title":"Connecting to Services"},{"location":"debug-attach_service/#mysql","text":"source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}","title":"MySQL"},{"location":"debug-attach_service/#redis","text":"docker-compose exec redis-mailcow redis-cli","title":"Redis"},{"location":"debug-attach_service/#service-descriptions","text":"Here is a brief overview of what container / service does what: Service Name Service Descriptions unbound-mailcow Local (DNSSEC) DNS Resolver mysql-mailcow Stores SOGo's and most of mailcow's settings postfix-mailcow Receives and sends mails dovecot-mailcow User logins and sieve filter redis-mailcow Storage back-end for DKIM keys and Rspamd rspamd-mailcow Mail filtering system. Used for av handling, dkim signing, spam handling clamd-mailcow Scans attachments for viruses olefy-mailcow Scans attached office documents for macro-viruses solr-mailcow Provides full-text search in Dovecot sogo-mailcow Webmail client that handles Microsoft ActiveSync and Cal- / CardDav nginx-mailcow Nginx remote proxy that handles all mailcow related HTTP / HTTPS requests acme-mailcow Automates HTTPS (SSL/TLS) certificate deployment memcached-mailcow Internal caching system for mailcow services watchdog-mailcow Allows the monitoring of docker containers / services php-fpm-mailcow Powers the mailcow web UI netfilter-mailcow Fail2Ban like integration","title":"Service Descriptions"},{"location":"debug-common_problems/","text":"Here we list common problems and possible solutions: Mail loops back to myself \u00b6 Please check in your mailcow UI if you made the domain a backup MX : I can receive but not send mails \u00b6 There are a lot of things that could prevent you from sending mail: Check if your IP address is on any blacklists. You could use dnsbl.info or any other similar service to check for your IP address. There are some consumer ISP routers out there, that block mail ports for non whitelisted domains. Please check if you can reach your server on the ports 465 or 587 : # telnet 74.125.133.27 465 Trying 74.125.133.27... Connected to 74.125.133.27. Escape character is '^]'. My mails are identified as Spam \u00b6 Please read our guide on DNS configuration . docker-compose throws weird errors \u00b6 ... like: ERROR: Invalid interpolation format ... AttributeError: 'NoneType' object has no attribute 'keys' . ERROR: In file './docker-compose.yml' service 'version' doesn't have any configuration options . When you encounter one or similar messages while trying to run mailcow: dockerized please check if you have the latest version of Docker and docker-compose Container XY is unhealthy \u00b6 This error tries to tell you that one of the (health) conditions for a certain container are not met. Therefore it can't be started. This can have several reasons, the most common one is an updated git clone but old docker image or vice versa. A wrong configured firewall could also cause such a failure. The containers need to be able to talk to each other over the network 172.22.1.1/24. It might also be wrongly linked file (i.e. SSL certificate) that prevents a crucial container (nginx) from starting, so always check your logs to get an idea where your problem is coming from. Address already in use \u00b6 If you get an error message like: ERROR: for postfix-mailcow Cannot start service postfix-mailcow: driver failed programming external connectivity on endpoint mailcowdockerized_postfix-mailcow_1: Error starting userland proxy: listen tcp 0.0.0.0:25: bind: address already in use while trying to start / install mailcow: dockerized, make sure you've followed our section on the prerequisites . XYZ can't connect to ... \u00b6 Please check your local firewall! Docker and iptables-based firewalls sometimes create conflicting rules, so disable the firewall on your host to determine whether your connection issues are caused by such conflicts. If they are, you need to manually create appropriate rules in your host firewall to permit the necessary connections. If you experience connection problems from home, please check your ISP router's firewall too, some of them block mail traffic on the SMTP (587) or SMTPS (465) ports. It could also be, that your ISP is blocking the ports for SUBMISSION (25). While Linux users can chose from a variety of tools 1 to check if a port is open, the Windows user has only the PowerShell command Test-NetConnection -ComputerName host -Port port available by default. To enable telnet on a Windows after Vista please check this guide or enter the following command in an terminal with administrator privileges : dism /online /Enable-Feature /FeatureName:TelnetClient Inotify instance limit for user 5000 (UID vmail) exceeded ( see #453 ) \u00b6 Docker containers use the Docker hosts inotify limits. Setting them on your Docker host will pass them to the container. netcat , nmap , openssl , telnet , etc. \u21a9","title":"Common Problems"},{"location":"debug-common_problems/#mail-loops-back-to-myself","text":"Please check in your mailcow UI if you made the domain a backup MX :","title":"Mail loops back to myself"},{"location":"debug-common_problems/#i-can-receive-but-not-send-mails","text":"There are a lot of things that could prevent you from sending mail: Check if your IP address is on any blacklists. You could use dnsbl.info or any other similar service to check for your IP address. There are some consumer ISP routers out there, that block mail ports for non whitelisted domains. Please check if you can reach your server on the ports 465 or 587 : # telnet 74.125.133.27 465 Trying 74.125.133.27... Connected to 74.125.133.27. Escape character is '^]'.","title":"I can receive but not send mails"},{"location":"debug-common_problems/#my-mails-are-identified-as-spam","text":"Please read our guide on DNS configuration .","title":"My mails are identified as Spam"},{"location":"debug-common_problems/#docker-compose-throws-weird-errors","text":"... like: ERROR: Invalid interpolation format ... AttributeError: 'NoneType' object has no attribute 'keys' . ERROR: In file './docker-compose.yml' service 'version' doesn't have any configuration options . When you encounter one or similar messages while trying to run mailcow: dockerized please check if you have the latest version of Docker and docker-compose","title":"docker-compose throws weird errors"},{"location":"debug-common_problems/#container-xy-is-unhealthy","text":"This error tries to tell you that one of the (health) conditions for a certain container are not met. Therefore it can't be started. This can have several reasons, the most common one is an updated git clone but old docker image or vice versa. A wrong configured firewall could also cause such a failure. The containers need to be able to talk to each other over the network 172.22.1.1/24. It might also be wrongly linked file (i.e. SSL certificate) that prevents a crucial container (nginx) from starting, so always check your logs to get an idea where your problem is coming from.","title":"Container XY is unhealthy"},{"location":"debug-common_problems/#address-already-in-use","text":"If you get an error message like: ERROR: for postfix-mailcow Cannot start service postfix-mailcow: driver failed programming external connectivity on endpoint mailcowdockerized_postfix-mailcow_1: Error starting userland proxy: listen tcp 0.0.0.0:25: bind: address already in use while trying to start / install mailcow: dockerized, make sure you've followed our section on the prerequisites .","title":"Address already in use"},{"location":"debug-common_problems/#xyz-cant-connect-to","text":"Please check your local firewall! Docker and iptables-based firewalls sometimes create conflicting rules, so disable the firewall on your host to determine whether your connection issues are caused by such conflicts. If they are, you need to manually create appropriate rules in your host firewall to permit the necessary connections. If you experience connection problems from home, please check your ISP router's firewall too, some of them block mail traffic on the SMTP (587) or SMTPS (465) ports. It could also be, that your ISP is blocking the ports for SUBMISSION (25). While Linux users can chose from a variety of tools 1 to check if a port is open, the Windows user has only the PowerShell command Test-NetConnection -ComputerName host -Port port available by default. To enable telnet on a Windows after Vista please check this guide or enter the following command in an terminal with administrator privileges : dism /online /Enable-Feature /FeatureName:TelnetClient","title":"XYZ can't connect to ..."},{"location":"debug-common_problems/#inotify-instance-limit-for-user-5000-uid-vmail-exceeded-see-453","text":"Docker containers use the Docker hosts inotify limits. Setting them on your Docker host will pass them to the container. netcat , nmap , openssl , telnet , etc. \u21a9","title":"Inotify instance limit for user 5000 (UID vmail) exceeded (see #453)"},{"location":"debug-logs/","text":"Warning This section only applies for Dockers default logging driver (JSON). To view the logs of all mailcow: dockerized related containers, you can use docker-compose logs inside your mailcow-dockerized folder that contains your mailcow.conf . This is usually a bit much, but you could trim the output with --tail=100 to the last 100 lines per container, or add a -f to follow the live output of all your services. To view the logs of a specific service you can use docker-compose logs [options] $service_name Info The available options for the command docker-compose logs are: --no-color : Produce monochrome output. -f : Follow the log output. -t : Show timestamps. --tail=\"all\" : Number of lines to show from the end of the logs for each container.","title":"Logs"},{"location":"debug-mysql_upgrade/","text":"Run a manual mysql_upgrade \u00b6 This step is usually not necessary. docker-compose stop mysql-mailcow watchdog-mailcow docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && bash && exit 0\"' mysql-mailcow As soon as the SQL shell spawned, run mysql_upgrade and exit the container: mysql_upgrade exit","title":"Manual MySQL upgrade"},{"location":"debug-mysql_upgrade/#run-a-manual-mysql_upgrade","text":"This step is usually not necessary. docker-compose stop mysql-mailcow watchdog-mailcow docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && bash && exit 0\"' mysql-mailcow As soon as the SQL shell spawned, run mysql_upgrade and exit the container: mysql_upgrade exit","title":"Run a manual mysql_upgrade"},{"location":"debug-reset_pw/","text":"mailcow Admin Account \u00b6 Resets the mailcow admin account to a random password. Older mailcow: dockerized installations may find the mailcow-reset-admin.sh script in their mailcow root directory (mailcow_path). cd mailcow_path ./helper-scripts/mailcow-reset-admin.sh Reset MySQL Passwords \u00b6 Stop the stack by running docker-compose stop . When the containers came to a stop, run this command: docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && mysql -hlocalhost -uroot && exit 0\"' mysql-mailcow 1. Find database name \u00b6 # source mailcow.conf # docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mailcow_database | <===== | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec) 2. Reset one or more users \u00b6 2.1 Maria DB < 10.4 (older mailcow installations) \u00b6 Both \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both. MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('gotr00t'), password = PASSWORD('gotr00t') WHERE User = 'root'; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('mookuh'), password = PASSWORD('mookuh') WHERE User = 'mailcow' AND Host = '%'; MariaDB [(none)]> FLUSH PRIVILEGES; 2.2 Maria DB >= 10.4 (current mailcows) \u00b6 MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> ALTER USER 'mailcow'@'%' IDENTIFIED BY 'mookuh'; MariaDB [(none)]> ALTER USER 'root'@'%' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> FLUSH PRIVILEGES; Remove Two-Factor Authentication \u00b6 For mailcow WebUI: \u00b6 This works similar to resetting a MySQL password, now we do it from the host without connecting to the MySQL CLI: source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e \"DELETE FROM tfa WHERE username='YOUR_USERNAME';\" For SOGo: \u00b6 docker-compose exec -u sogo sogo-mailcow sogo-tool user-preferences set defaults user@example.com SOGoGoogleAuthenticatorEnabled '{\"SOGoGoogleAuthenticatorEnabled\":0}'","title":"Reset Passwords (incl. SQL)"},{"location":"debug-reset_pw/#mailcow-admin-account","text":"Resets the mailcow admin account to a random password. Older mailcow: dockerized installations may find the mailcow-reset-admin.sh script in their mailcow root directory (mailcow_path). cd mailcow_path ./helper-scripts/mailcow-reset-admin.sh","title":"mailcow Admin Account"},{"location":"debug-reset_pw/#reset-mysql-passwords","text":"Stop the stack by running docker-compose stop . When the containers came to a stop, run this command: docker-compose run --rm --entrypoint '/bin/sh -c \"gosu mysql mysqld --skip-grant-tables & sleep 10 && mysql -hlocalhost -uroot && exit 0\"' mysql-mailcow","title":"Reset MySQL Passwords"},{"location":"debug-reset_pw/#1-find-database-name","text":"# source mailcow.conf # docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mailcow_database | <===== | mysql | | performance_schema | +--------------------+ 4 rows in set (0.00 sec)","title":"1. Find database name"},{"location":"debug-reset_pw/#2-reset-one-or-more-users","text":"","title":"2. Reset one or more users"},{"location":"debug-reset_pw/#21-maria-db-104-older-mailcow-installations","text":"Both \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both. MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('gotr00t'), password = PASSWORD('gotr00t') WHERE User = 'root'; MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('mookuh'), password = PASSWORD('mookuh') WHERE User = 'mailcow' AND Host = '%'; MariaDB [(none)]> FLUSH PRIVILEGES;","title":"2.1 Maria DB < 10.4 (older mailcow installations)"},{"location":"debug-reset_pw/#22-maria-db-104-current-mailcows","text":"MariaDB [(none)]> SELECT user FROM mysql.user; +--------------+ | user | +--------------+ | mailcow | <===== | root | +--------------+ 2 rows in set (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> ALTER USER 'mailcow'@'%' IDENTIFIED BY 'mookuh'; MariaDB [(none)]> ALTER USER 'root'@'%' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY 'gotr00t'; MariaDB [(none)]> FLUSH PRIVILEGES;","title":"2.2 Maria DB >= 10.4 (current mailcows)"},{"location":"debug-reset_pw/#remove-two-factor-authentication","text":"","title":"Remove Two-Factor Authentication"},{"location":"debug-reset_pw/#for-mailcow-webui","text":"This works similar to resetting a MySQL password, now we do it from the host without connecting to the MySQL CLI: source mailcow.conf docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e \"DELETE FROM tfa WHERE username='YOUR_USERNAME';\"","title":"For mailcow WebUI:"},{"location":"debug-reset_pw/#for-sogo","text":"docker-compose exec -u sogo sogo-mailcow sogo-tool user-preferences set defaults user@example.com SOGoGoogleAuthenticatorEnabled '{\"SOGoGoogleAuthenticatorEnabled\":0}'","title":"For SOGo:"},{"location":"debug-reset_tls/","text":"In case you encounter problems with your certificate, key or Let's Encrypt account, please try to reset the TLS assets: source mailcow.conf docker-compose down rm -rf data/assets/ssl mkdir data/assets/ssl openssl req -x509 -newkey rsa:4096 -keyout data/assets/ssl-example/key.pem -out data/assets/ssl-example/cert.pem -days 365 -subj \"/C=DE/ST=NRW/L=Willich/O=mailcow/OU=mailcow/CN=${MAILCOW_HOSTNAME}\" -sha256 -nodes cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/ docker-compose up -d This will stop mailcow, source the variables we need, create a self-signed certificate and start mailcow. If you use Let's Encrypt you should be careful as you will create a new account and a new set of certificates. You will run into a ratelimit sooner or later. Please also note that previous TLSA records will be invalid.","title":"Reset TLS certificates"},{"location":"debug-rm_volumes/","text":"You may want to remove a set of persistent data to resolve a conflict or to start over. mailcowdockerized can vary and depends on your compose project name (if it's unchanged, mailcowdockerized is the correct value). If you are unsure about volume names, run docker volume ls for a full list. Delete a single volume: docker volume rm mailcowdockerized_${VOLUME_NAME} Remove volume mysql-vol-1 to remove all MySQL data. Remove volume redis-vol-1 to remove all Redis data. Remove volume vmail-vol-1 to remove all contents of /var/vmail mounted to dovecot-mailcow . Remove volume rspamd-vol-1 to remove all Rspamd data. Remove volume crypt-vol-1 to remove all crypto data. This will render all mails unreadable. Alternatively, running docker-compose down -v will destroy all mailcow: dockerized volumes and delete any related containers and networks.","title":"Remove Persistent Data"},{"location":"debug/","text":"When a problem occurs, then always for a reason! What you want to do in such a case is: Read your logs; follow them to see what the reason for your problem is. Follow the leads given to you in your logfiles and start investigating. Restarting the troubled service or the whole stack to see if the problem persists. Read the documentation of the troubled service and search it's bugtracker for your problem. Search our issues for your problem. Create an issue over at our GitHub repository if you think your problem might be a bug or a missing feature you badly need. But please make sure, that you include all the logs and a full description to your problem. Please do not ask for support on Git. Join our Telegram community or find the official support packages at Servercow .","title":"Introduction"},{"location":"firststeps-disable_ipv6/","text":"This is ONLY recommended if you do not have an IPv6 enabled network on your host! If you really need to, you can disable the usage of IPv6 in the compose file. Additionally, you can also disable the startup of container \"ipv6nat-mailcow\", as it's not needed if you won't use IPv6. Instead of editing docker-compose.yml directly, it is preferable to create an override file for it and implement your changes to the service there. Unfortunately, this right now only seems to work for services, not for network settings. To disable IPv6 on the mailcow network, open docker-compose.yml with your favourite text editor and search for the network section (it's near the bottom of the file). 1. Modify docker-compose.yml Change enable_ipv6: true to enable_ipv6: false : networks: mailcow-network: [...] enable_ipv6: true # <<< set to false [...] 2. Disable ipv6nat-mailcow To disable the ipv6nat-mailcow container as well, go to your mailcow directory and create a new file called \"docker-compose.override.yml\": NOTE: If you already have an override file, of course don't recreate it, but merge the lines below into your existing one accordingly! # cd /opt/mailcow-dockerized # touch docker-compose.override.yml Open the file in your favourite text editor and fill in the following: version: '2.1' services: ipv6nat-mailcow: image: bash:latest restart: \"no\" entrypoint: [\"echo\", \"ipv6nat disabled in compose.override.yml\"] For these changes to be effective, you need to fully stop and then restart the stack, so containers and networks are recreated: docker-compose down docker-compose up -d 3. Disable IPv6 in unbound-mailcow Edit data/conf/unbound/unbound.conf and set do-ip6 to \"no\": server: [...] do-ip6: no [...] Restart Unbound: docker-compose restart unbound-mailcow 4. Disable IPv6 in postfix-mailcow Create data/conf/postfix/extra.cf and set smtp_address_preference to ipv4 : smtp_address_preference = ipv4 inet_protocols = ipv4 Restart Postfix: docker-compose restart postfix-mailcow","title":"Disable IPv6"},{"location":"firststeps-dmarc_reporting/","text":"DMARC Reporting done via Rspamd DMARC Module. Rspamd documentation can be found here: https://rspamd.com/doc/modules/dmarc.html Important: Change example.com , mail.example.com and Example to reflect your setup DMARC reporting requires additional attention, especially over the first few days All receiving domains hosted on mailcow send from one reporting domain. It is recommended to use the parent domain of your MAILCOW_HOSTNAME : If your MAILCOW_HOSTNAME is mail.example.com change the following config to domain = \"example.com\"; Set email equally, e.g. email = \"noreply-dmarc@example.com\"; It is optional but recommended to create an email user noreply-dmarc in mailcow to handle bounces. Enable DMARC reporting \u00b6 Create the file data/conf/rspamd/local.d/dmarc.conf and set the following content: reporting { enabled = true; email = 'noreply-dmarc@example.com'; domain = 'example.com'; org_name = 'Example'; helo = 'rspamd'; smtp = 'postfix'; smtp_port = 25; from_name = 'Example DMARC Report'; msgid_from = 'rspamd.mail.example.com'; max_entries = 2k; keys_expire = 2d; } Create or modify docker-compose.override.yml in the mailcow-dockerized base directory: version: '2.1' services: rspamd-mailcow: environment: - MASTER=${MASTER:-y} labels: ofelia.enabled: \"true\" ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" ofelia.job-exec.rspamd_dmarc_reporting.command: \"/bin/bash -c \\\"[[ $${MASTER} == y ]] && /usr/bin/rspamadm dmarc_report > /var/lib/rspamd/dmarc_reports_last_log 2>&1 || exit 0\\\"\" ofelia-mailcow: depends_on: - rspamd-mailcow Run docker-compose up -d Send a copy reports to yourself \u00b6 To receive a hidden copy of reports generated by Rspamd you can set a bcc_addrs list in the reporting config section of data/conf/rspamd/local.d/dmarc.conf : reporting { enabled = true; email = 'noreply-dmarc@example.com'; bcc_addrs = [\"noreply-dmarc@example.com\",\"parsedmarc@example.com\"]; [...] Rspamd will load changes in real time, so you won't need to restart the container at this point. This can be useful if you... ...want to check that your DMARC reports are sent correctly and authenticated. ...want to analyze your own reports to get statistics, i.e. to use with ParseDMARC or other analytic systems. Troubleshooting \u00b6 Check when the report schedule last ran: docker-compose exec rspamd-mailcow date -r /var/lib/rspamd/dmarc_reports_last_log See the latest report output: docker-compose exec rspamd-mailcow cat /var/lib/rspamd/dmarc_reports_last_log Manually trigger a DMARC report: docker-compose exec rspamd-mailcow rspamadm dmarc_report Validate that Rspamd has recorded data in Redis: docker-compose exec redis-mailcow redis-cli KEYS 'dmarc;*' docker-compose exec redis-mailcow redis-cli HGETALL \"dmarc;example.com;20211231\" Change DMARC reporting frequency \u00b6 In the example above reports are sent once every 24 hours. You may want to change that interval: Edit docker-compose.override.yml and a djust ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" to a desired value. Run docker-compose up -d Run docker-compose restart ofelia-mailcow Disable DMARC Reporting \u00b6 To disable reporting: Set enabled to false in data/conf/rspamd/local.d/dmarc.conf Revert changes done to docker-compose.override.yml Run docker-compose up -d","title":"DMARC Reporting"},{"location":"firststeps-dmarc_reporting/#enable-dmarc-reporting","text":"Create the file data/conf/rspamd/local.d/dmarc.conf and set the following content: reporting { enabled = true; email = 'noreply-dmarc@example.com'; domain = 'example.com'; org_name = 'Example'; helo = 'rspamd'; smtp = 'postfix'; smtp_port = 25; from_name = 'Example DMARC Report'; msgid_from = 'rspamd.mail.example.com'; max_entries = 2k; keys_expire = 2d; } Create or modify docker-compose.override.yml in the mailcow-dockerized base directory: version: '2.1' services: rspamd-mailcow: environment: - MASTER=${MASTER:-y} labels: ofelia.enabled: \"true\" ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" ofelia.job-exec.rspamd_dmarc_reporting.command: \"/bin/bash -c \\\"[[ $${MASTER} == y ]] && /usr/bin/rspamadm dmarc_report > /var/lib/rspamd/dmarc_reports_last_log 2>&1 || exit 0\\\"\" ofelia-mailcow: depends_on: - rspamd-mailcow Run docker-compose up -d","title":"Enable DMARC reporting"},{"location":"firststeps-dmarc_reporting/#send-a-copy-reports-to-yourself","text":"To receive a hidden copy of reports generated by Rspamd you can set a bcc_addrs list in the reporting config section of data/conf/rspamd/local.d/dmarc.conf : reporting { enabled = true; email = 'noreply-dmarc@example.com'; bcc_addrs = [\"noreply-dmarc@example.com\",\"parsedmarc@example.com\"]; [...] Rspamd will load changes in real time, so you won't need to restart the container at this point. This can be useful if you... ...want to check that your DMARC reports are sent correctly and authenticated. ...want to analyze your own reports to get statistics, i.e. to use with ParseDMARC or other analytic systems.","title":"Send a copy reports to yourself"},{"location":"firststeps-dmarc_reporting/#troubleshooting","text":"Check when the report schedule last ran: docker-compose exec rspamd-mailcow date -r /var/lib/rspamd/dmarc_reports_last_log See the latest report output: docker-compose exec rspamd-mailcow cat /var/lib/rspamd/dmarc_reports_last_log Manually trigger a DMARC report: docker-compose exec rspamd-mailcow rspamadm dmarc_report Validate that Rspamd has recorded data in Redis: docker-compose exec redis-mailcow redis-cli KEYS 'dmarc;*' docker-compose exec redis-mailcow redis-cli HGETALL \"dmarc;example.com;20211231\"","title":"Troubleshooting"},{"location":"firststeps-dmarc_reporting/#change-dmarc-reporting-frequency","text":"In the example above reports are sent once every 24 hours. You may want to change that interval: Edit docker-compose.override.yml and a djust ofelia.job-exec.rspamd_dmarc_reporting.schedule: \"@every 24h\" to a desired value. Run docker-compose up -d Run docker-compose restart ofelia-mailcow","title":"Change DMARC reporting frequency"},{"location":"firststeps-dmarc_reporting/#disable-dmarc-reporting","text":"To disable reporting: Set enabled to false in data/conf/rspamd/local.d/dmarc.conf Revert changes done to docker-compose.override.yml Run docker-compose up -d","title":"Disable DMARC Reporting"},{"location":"firststeps-ip_bindings/","text":"Warning Changing the binding does not affect source NAT. See SNAT for required steps. IPv4 binding \u00b6 To adjust one or multiple IPv4 bindings, open mailcow.conf and edit one, multiple or all variables as per your needs: # For technical reasons, http bindings are a bit different from other service bindings. # You will find the following variables, separated by a bind address and its port: # Example: HTTP_BIND=1.2.3.4 HTTP_PORT=80 HTTP_BIND= HTTPS_PORT=443 HTTPS_BIND= # Other services are bound by using the following format: # SMTP_PORT=1.2.3.4:25 will bind SMTP to the IP 1.2.3.4 on port 25 # Important! Specifying an IPv4 address will skip all IPv6 bindings since Docker 20.x. # doveadm, SQL as well as Solr are bound to local ports only, please do not change that, unless you know what you are doing. SMTP_PORT=25 SMTPS_PORT=465 SUBMISSION_PORT=587 IMAP_PORT=143 IMAPS_PORT=993 POP_PORT=110 POPS_PORT=995 SIEVE_PORT=4190 DOVEADM_PORT=127.0.0.1:19991 SQL_PORT=127.0.0.1:13306 SOLR_PORT=127.0.0.1:18983 To apply your changes, run docker-compose down followed by docker-compose up -d . IPv6 binding \u00b6 Changing IPv6 bindings is different from IPv4. Again, this has a technical background. A docker-compose.override.yml file will be used instead of editing the docker-compose.yml file directly. This is to maintain updatability, as the docker-compose.yml file gets updated regularly and your changes will most likely be overwritten. Edit to create a file docker-compose.override.yml with the following content. Its content will be merged with the productive docker-compose.yml file. An imaginary IPv6 2a00:dead:beef::abc is given. The first suffix :PORT1 defines the external port, while the second suffix :PORT2 routes to the corresponding port inside the container and must not be changed. version: '2.1' services: dovecot-mailcow: ports: - '2a00:dead:beef::abc:143:143' - '2a00:dead:beef::abc:993:993' - '2a00:dead:beef::abc:110:110' - '2a00:dead:beef::abc:995:995' - '2a00:dead:beef::abc:4190:4190' postfix-mailcow: ports: - '2a00:dead:beef::abc:25:25' - '2a00:dead:beef::abc:465:465' - '2a00:dead:beef::abc:587:587' nginx-mailcow: ports: - '2a00:dead:beef::abc:80:80' - '2a00:dead:beef::abc:443:443' To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IP bindings"},{"location":"firststeps-ip_bindings/#ipv4-binding","text":"To adjust one or multiple IPv4 bindings, open mailcow.conf and edit one, multiple or all variables as per your needs: # For technical reasons, http bindings are a bit different from other service bindings. # You will find the following variables, separated by a bind address and its port: # Example: HTTP_BIND=1.2.3.4 HTTP_PORT=80 HTTP_BIND= HTTPS_PORT=443 HTTPS_BIND= # Other services are bound by using the following format: # SMTP_PORT=1.2.3.4:25 will bind SMTP to the IP 1.2.3.4 on port 25 # Important! Specifying an IPv4 address will skip all IPv6 bindings since Docker 20.x. # doveadm, SQL as well as Solr are bound to local ports only, please do not change that, unless you know what you are doing. SMTP_PORT=25 SMTPS_PORT=465 SUBMISSION_PORT=587 IMAP_PORT=143 IMAPS_PORT=993 POP_PORT=110 POPS_PORT=995 SIEVE_PORT=4190 DOVEADM_PORT=127.0.0.1:19991 SQL_PORT=127.0.0.1:13306 SOLR_PORT=127.0.0.1:18983 To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IPv4 binding"},{"location":"firststeps-ip_bindings/#ipv6-binding","text":"Changing IPv6 bindings is different from IPv4. Again, this has a technical background. A docker-compose.override.yml file will be used instead of editing the docker-compose.yml file directly. This is to maintain updatability, as the docker-compose.yml file gets updated regularly and your changes will most likely be overwritten. Edit to create a file docker-compose.override.yml with the following content. Its content will be merged with the productive docker-compose.yml file. An imaginary IPv6 2a00:dead:beef::abc is given. The first suffix :PORT1 defines the external port, while the second suffix :PORT2 routes to the corresponding port inside the container and must not be changed. version: '2.1' services: dovecot-mailcow: ports: - '2a00:dead:beef::abc:143:143' - '2a00:dead:beef::abc:993:993' - '2a00:dead:beef::abc:110:110' - '2a00:dead:beef::abc:995:995' - '2a00:dead:beef::abc:4190:4190' postfix-mailcow: ports: - '2a00:dead:beef::abc:25:25' - '2a00:dead:beef::abc:465:465' - '2a00:dead:beef::abc:587:587' nginx-mailcow: ports: - '2a00:dead:beef::abc:80:80' - '2a00:dead:beef::abc:443:443' To apply your changes, run docker-compose down followed by docker-compose up -d .","title":"IPv6 binding"},{"location":"firststeps-local_mta/","text":"The easiest option would be to disable the listener on port 25/tcp. Postfix users disable the listener by commenting the following line (starting with smtp or 25 ) in /etc/postfix/master.cf : #smtp inet n - - - - smtpd Furthermore, to relay over a dockerized mailcow, you may want to add 172.22.1.1 as relayhost and remove the Docker interface from \"inet_interfaces\": postconf -e 'relayhost = 172.22.1.1' postconf -e \"mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128\" postconf -e \"inet_interfaces = loopback-only\" postconf -e \"relay_transport = relay\" postconf -e \"default_transport = smtp\" Now it is important to not have the same FQDN in myhostname as you use for your dockerized mailcow. Check your local (non-Docker) Postfix' main.cf for myhostname and set it to something different, for example local.my.fqdn.tld . \"172.22.1.1\" is the mailcow created network gateway in Docker. Relaying over this interface is necessary (instead of - for example - relaying directly over ${MAILCOW_HOSTNAME}) to relay over a known internal network. Restart Postfix after applying your changes.","title":"Local MTA on Docker host"},{"location":"firststeps-logging/","text":"Logging in mailcow: dockerized consists of multiple stages, but is, after all, much more flexible and easier to integrate into a logging daemon than before. In Docker the containerized application (PID 1) writes its output to stdout. For real one-application containers this works just fine. Run docker-compose logs --help to learn more. Some containers log or stream to multiple destinations. No container will keep persistent logs in it. Containers are transient items! In the end, every line of logs will reach the Docker daemon - unfiltered. The default logging driver is \"json\" . Filtered logs \u00b6 Some logs are filtered and written to Redis keys but also streamed to a Redis channel. The Redis channel is used to stream logs with failed authentication attempts to be read by netfilter-mailcow. The Redis keys are persistent and will keep 10000 lines of logs for the web UI. This mechanism makes it possible to use whatever Docker logging driver you want to, without losing the ability to read logs from the UI or ban suspicious clients with netfilter-mailcow. Redis keys will only hold logs from applications and filter out system messages (think of cron etc.). Logging drivers \u00b6 Via docker-compose.override.yml \u00b6 Here is the good news: Since Docker has some great logging drivers, you can integrate mailcow: dockerized into your existing logging environment with ease. Create a docker-compose.override.yml and add, for example, this block to use the \"gelf\" logging plugin for postfix-mailcow : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"gelf\" options: gelf-address: \"udp://graylog:12201\" Another example for Syslog : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" dovecot-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" rspamd-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" # For Rsyslog only: # To move local3 input to /var/log/mailcow.log and stop processing, create a file \"/etc/rsyslog.d/docker.conf\": local3.* /var/log/mailcow.logs & ~ # Restart rsyslog afterwards. via daemon.json (globally) \u00b6 If you want to change the logging driver globally , edit Dockers daemon configuration file /etc/docker/daemon.json and restart the Docker service: { ... \"log-driver\": \"gelf\", \"log-opts\": { \"gelf-address\": \"udp://graylog:12201\" } ... } For Syslog: { ... \"log-driver\": \"syslog\", \"log-opts\": { \"syslog-address\": \"udp://1.2.3.4:514\" } ... } Restart the Docker daemon and run docker-compose down && docker-compose up -d to recreate the containers with the new logging driver.","title":"Logging"},{"location":"firststeps-logging/#filtered-logs","text":"Some logs are filtered and written to Redis keys but also streamed to a Redis channel. The Redis channel is used to stream logs with failed authentication attempts to be read by netfilter-mailcow. The Redis keys are persistent and will keep 10000 lines of logs for the web UI. This mechanism makes it possible to use whatever Docker logging driver you want to, without losing the ability to read logs from the UI or ban suspicious clients with netfilter-mailcow. Redis keys will only hold logs from applications and filter out system messages (think of cron etc.).","title":"Filtered logs"},{"location":"firststeps-logging/#logging-drivers","text":"","title":"Logging drivers"},{"location":"firststeps-logging/#via-docker-composeoverrideyml","text":"Here is the good news: Since Docker has some great logging drivers, you can integrate mailcow: dockerized into your existing logging environment with ease. Create a docker-compose.override.yml and add, for example, this block to use the \"gelf\" logging plugin for postfix-mailcow : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"gelf\" options: gelf-address: \"udp://graylog:12201\" Another example for Syslog : version: '2.1' services: postfix-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" dovecot-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" rspamd-mailcow: # or any other logging: driver: \"syslog\" options: syslog-address: \"udp://127.0.0.1:514\" syslog-facility: \"local3\" # For Rsyslog only: # To move local3 input to /var/log/mailcow.log and stop processing, create a file \"/etc/rsyslog.d/docker.conf\": local3.* /var/log/mailcow.logs & ~ # Restart rsyslog afterwards.","title":"Via docker-compose.override.yml"},{"location":"firststeps-logging/#via-daemonjson-globally","text":"If you want to change the logging driver globally , edit Dockers daemon configuration file /etc/docker/daemon.json and restart the Docker service: { ... \"log-driver\": \"gelf\", \"log-opts\": { \"gelf-address\": \"udp://graylog:12201\" } ... } For Syslog: { ... \"log-driver\": \"syslog\", \"log-opts\": { \"syslog-address\": \"udp://1.2.3.4:514\" } ... } Restart the Docker daemon and run docker-compose down && docker-compose up -d to recreate the containers with the new logging driver.","title":"via daemon.json (globally)"},{"location":"firststeps-rp/","text":"You don't need to change the Nginx site that comes with mailcow: dockerized. mailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. 1. Make sure you change HTTP_BIND and HTTPS_BIND in mailcow.conf to a local address and set the ports accordingly, for example: HTTP_BIND = 127 .0.0.1 HTTP_PORT = 8080 HTTPS_BIND = 127 .0.0.1 HTTPS_PORT = 8443 This will also change the bindings inside the Nginx container! This is important, if you decide to use a proxy within Docker. IMPORTANT: Do not use port 8081, 9081 or 65510! Recreate affected containers by running docker-compose up -d . Important information, please read them carefully! Info If you plan to use a reverse proxy and want to use another server name that is not MAILCOW_HOSTNAME, you need to read Adding additional server names for mailcow UI at the bottom of this page. Warning Make sure you run generate_config.sh before you enable any site configuration examples below. The script generate_config.sh copies snake-oil certificates to the correct location, so the services will not fail to start due to missing files. Warning If you enable TLS SNI ( ENABLE_TLS_SNI in mailcow.conf), the certificate paths in your reverse proxy must match the correct paths in data/assets/ssl/{hostname}. The certificates will be split into data/assets/ssl/{hostname1,hostname2,etc} and therefore will not work when you copy the examples from below pointing to data/assets/ssl/cert.pem etc. Info Using the site configs below will forward ACME requests to mailcow and let it handle certificates itself. The downside of using mailcow as ACME client behind a reverse proxy is, that you will need to reload your webserver after acme-mailcow changed/renewed/created the certificate. You can either reload your webserver daily or write a script to watch the file for changes. On many servers logrotate will reload the webserver daily anyway. If you want to use a local certbot installation, you will need to change the SSL certificate parameters accordingly. Make sure you run a post-hook script when you decide to use external ACME clients. You will find an example at the bottom of this page. 2. Configure your local webserver as reverse proxy: Apache 2.4 \u00b6 Required modules: a2enmod rewrite proxy proxy_http headers ssl Let's Encrypt will follow our rewrite, certificate requests in mailcow will work fine. Take care of highlighted lines. ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L] ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"http\" ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* # You should proxy to a plain HTTP session to offload SSL processing ProxyPass /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync connectiontimeout=4000 ProxyPassReverse /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"https\" SSLCertificateFile MAILCOW_PATH/data/assets/ssl/cert.pem SSLCertificateKeyFile MAILCOW_PATH/data/assets/ssl/key.pem # If you plan to proxy to a HTTPS host: #SSLProxyEngine On # If you plan to proxy to an untrusted HTTPS host: #SSLProxyVerify none #SSLProxyCheckPeerCN off #SSLProxyCheckPeerName off #SSLProxyCheckPeerExpire off Nginx \u00b6 Let's Encrypt will follow our rewrite, certificate requests will work fine. Take care of highlighted lines. server { listen 80 default_server; listen [::]:80 default_server; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; ssl_certificate MAILCOW_PATH/data/assets/ssl/cert.pem; ssl_certificate_key MAILCOW_PATH/data/assets/ssl/key.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # See https://ssl-config.mozilla.org/#server=nginx for the latest ssl settings recommendations # An example config is given below ssl_protocols TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!SHA1:!kRSA; ssl_prefer_server_ciphers off; location /Microsoft-Server-ActiveSync { proxy_pass http://127.0.0.1:8080/Microsoft-Server-ActiveSync; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 75; proxy_send_timeout 3650; proxy_read_timeout 3650; proxy_buffers 64 256k; client_body_buffer_size 512k; client_max_body_size 0; } location / { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } } HAProxy (community supported) \u00b6 Warning This is an unsupported community contribution. Feel free to provide fixes. Important/Fixme : This example only forwards HTTPS traffic and does not use mailcows built-in ACME client. frontend https-in bind :::443 v4v6 ssl crt mailcow.pem default_backend mailcow backend mailcow option forwardfor http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server mailcow 127.0.0.1:8080 check Traefik v2 (community supported) \u00b6 Warning This is an unsupported community contribution. Feel free to provide fixes. Important : This config only covers the \"reverseproxing\" of the webpannel (nginx-mailcow) using Traefik v2, if you also want to reverseproxy the mail services such as dovecot, postfix... you'll just need to adapt the following config to each container and create an EntryPoint on your traefik.toml or traefik.yml (depending which config you use) for each port. For this section we'll assume you have your Traefik 2 [certificatesresolvers] properly configured on your traefik configuration file, and also using acme, also, the following example uses Lets Encrypt, but feel free to change it to your own cert resolver. You can find a basic Traefik 2 toml config file with all the above implemented which can be used for this example here traefik.toml if you need one, or a hint on how to adapt your config. So, first of all, we are going to disable the acme-mailcow container since we'll use the certs that traefik will provide us. For this we'll have to set SKIP_LETS_ENCRYPT=y on our mailcow.conf , and run docker-compose up -d to apply the changes. Then we'll create a docker-compose.override.yml file in order to override the main docker-compose.yml found in your mailcow root folder. version : '2.1' services : nginx-mailcow : networks : # add Traefik's network web : labels : - traefik.enable=true # Creates a router called \"moo\" for the container, and sets up a rule to link the container to certain rule, # in this case, a Host rule with our MAILCOW_HOSTNAME var. - traefik.http.routers.moo.rule=Host(`${MAILCOW_HOSTNAME}`) # Enables tls over the router we created before. - traefik.http.routers.moo.tls=true # Specifies which kind of cert resolver we'll use, in this case le (Lets Encrypt). - traefik.http.routers.moo.tls.certresolver=le # Creates a service called \"moo\" for the container, and specifies which internal port of the container # should traefik route the incoming data to. - traefik.http.services.moo.loadbalancer.server.port=${HTTP_PORT} # Specifies which entrypoint (external port) should traefik listen to, for this container. # websecure being port 443, check the traefik.toml file liked above. - traefik.http.routers.moo.entrypoints=websecure # Make sure traefik uses the web network, not the mailcowdockerized_mailcow-network - traefik.docker.network=web certdumper : image : humenius/traefik-certs-dumper container_name : traefik_certdumper network_mode : none volumes : # mount the folder which contains Traefik's `acme.json' file # in this case Traefik is started from its own docker-compose in ../traefik - ../traefik/data:/traefik:ro # mount mailcow's SSL folder - ./data/assets/ssl/:/output:rw environment : # only change this, if you're using another domain for mailcow's web frontend compared to the standard config - DOMAIN=${MAILCOW_HOSTNAME} networks : web : external : true Start the new containers with docker-compose up -d . Now, there's only one thing left to do, which is setup the certs so that the mail services can use them as well, since Traefik 2 uses an acme v2 format to save ALL the license from all the domains we have, we'll need to find a way to dump the certs, lucky we have this tiny container which grabs the acme.json file trough a volume, and a variable DOMAIN=example.org , and with these, the container will output the cert.pem and key.pem files, for this we'll simply run the traefik-certs-dumper container binding the /traefik volume to the folder where our acme.json is saved, bind the /output volume to our mailcow data/assets/ssl/ folder, and set up the DOMAIN=example.org variable to the domain we want the certs dumped from. This container will watch over the acme.json file for any changes, and regenerate the cert.pem and key.pem files directly into data/assets/ssl/ being the path binded to the container's /output path. You can use the command line to run it, or use the docker-compose shown here . After we have the certs dumped, we'll have to reload the configs from our postfix and dovecot containers, and check the certs, you can see how here . Aaand that should be it \ud83d\ude0a, you can check if the Traefik router works fine trough Traefik's dashboard / traefik logs / accessing the setted domain trough https, or / and check HTTPS, SMTP and IMAP trough the commands shown on the page linked before. Optional: Post-hook script for non-mailcow ACME clients \u00b6 Using a local certbot (or any other ACME client) requires to restart some containers, you can do this with a post-hook script. Make sure you change the paths accordingly: #!/bin/bash cp /etc/letsencrypt/live/my.domain.tld/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem cp /etc/letsencrypt/live/my.domain.tld/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem postfix_c=$(docker ps -qaf name=postfix-mailcow) dovecot_c=$(docker ps -qaf name=dovecot-mailcow) nginx_c=$(docker ps -qaf name=nginx-mailcow) docker restart ${postfix_c} ${dovecot_c} ${nginx_c} Adding additional server names for mailcow UI \u00b6 If you plan to use a server name that is not MAILCOW_HOSTNAME in your reverse proxy, make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES first. Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond to your reverse proxy with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Reverse Proxy"},{"location":"firststeps-rp/#apache-24","text":"Required modules: a2enmod rewrite proxy proxy_http headers ssl Let's Encrypt will follow our rewrite, certificate requests in mailcow will work fine. Take care of highlighted lines. ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L] ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"http\" ServerName CHANGE_TO_MAILCOW_HOSTNAME ServerAlias autodiscover.* ServerAlias autoconfig.* # You should proxy to a plain HTTP session to offload SSL processing ProxyPass /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync connectiontimeout=4000 ProxyPassReverse /Microsoft-Server-ActiveSync http://127.0.0.1:8080/Microsoft-Server-ActiveSync ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ ProxyPreserveHost On ProxyAddHeaders On RequestHeader set X-Forwarded-Proto \"https\" SSLCertificateFile MAILCOW_PATH/data/assets/ssl/cert.pem SSLCertificateKeyFile MAILCOW_PATH/data/assets/ssl/key.pem # If you plan to proxy to a HTTPS host: #SSLProxyEngine On # If you plan to proxy to an untrusted HTTPS host: #SSLProxyVerify none #SSLProxyCheckPeerCN off #SSLProxyCheckPeerName off #SSLProxyCheckPeerExpire off ","title":"Apache 2.4"},{"location":"firststeps-rp/#nginx","text":"Let's Encrypt will follow our rewrite, certificate requests will work fine. Take care of highlighted lines. server { listen 80 default_server; listen [::]:80 default_server; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name CHANGE_TO_MAILCOW_HOSTNAME autodiscover.* autoconfig.*; ssl_certificate MAILCOW_PATH/data/assets/ssl/cert.pem; ssl_certificate_key MAILCOW_PATH/data/assets/ssl/key.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # See https://ssl-config.mozilla.org/#server=nginx for the latest ssl settings recommendations # An example config is given below ssl_protocols TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!SHA1:!kRSA; ssl_prefer_server_ciphers off; location /Microsoft-Server-ActiveSync { proxy_pass http://127.0.0.1:8080/Microsoft-Server-ActiveSync; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 75; proxy_send_timeout 3650; proxy_read_timeout 3650; proxy_buffers 64 256k; client_body_buffer_size 512k; client_max_body_size 0; } location / { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } }","title":"Nginx"},{"location":"firststeps-rp/#haproxy-community-supported","text":"Warning This is an unsupported community contribution. Feel free to provide fixes. Important/Fixme : This example only forwards HTTPS traffic and does not use mailcows built-in ACME client. frontend https-in bind :::443 v4v6 ssl crt mailcow.pem default_backend mailcow backend mailcow option forwardfor http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server mailcow 127.0.0.1:8080 check","title":"HAProxy (community supported)"},{"location":"firststeps-rp/#traefik-v2-community-supported","text":"Warning This is an unsupported community contribution. Feel free to provide fixes. Important : This config only covers the \"reverseproxing\" of the webpannel (nginx-mailcow) using Traefik v2, if you also want to reverseproxy the mail services such as dovecot, postfix... you'll just need to adapt the following config to each container and create an EntryPoint on your traefik.toml or traefik.yml (depending which config you use) for each port. For this section we'll assume you have your Traefik 2 [certificatesresolvers] properly configured on your traefik configuration file, and also using acme, also, the following example uses Lets Encrypt, but feel free to change it to your own cert resolver. You can find a basic Traefik 2 toml config file with all the above implemented which can be used for this example here traefik.toml if you need one, or a hint on how to adapt your config. So, first of all, we are going to disable the acme-mailcow container since we'll use the certs that traefik will provide us. For this we'll have to set SKIP_LETS_ENCRYPT=y on our mailcow.conf , and run docker-compose up -d to apply the changes. Then we'll create a docker-compose.override.yml file in order to override the main docker-compose.yml found in your mailcow root folder. version : '2.1' services : nginx-mailcow : networks : # add Traefik's network web : labels : - traefik.enable=true # Creates a router called \"moo\" for the container, and sets up a rule to link the container to certain rule, # in this case, a Host rule with our MAILCOW_HOSTNAME var. - traefik.http.routers.moo.rule=Host(`${MAILCOW_HOSTNAME}`) # Enables tls over the router we created before. - traefik.http.routers.moo.tls=true # Specifies which kind of cert resolver we'll use, in this case le (Lets Encrypt). - traefik.http.routers.moo.tls.certresolver=le # Creates a service called \"moo\" for the container, and specifies which internal port of the container # should traefik route the incoming data to. - traefik.http.services.moo.loadbalancer.server.port=${HTTP_PORT} # Specifies which entrypoint (external port) should traefik listen to, for this container. # websecure being port 443, check the traefik.toml file liked above. - traefik.http.routers.moo.entrypoints=websecure # Make sure traefik uses the web network, not the mailcowdockerized_mailcow-network - traefik.docker.network=web certdumper : image : humenius/traefik-certs-dumper container_name : traefik_certdumper network_mode : none volumes : # mount the folder which contains Traefik's `acme.json' file # in this case Traefik is started from its own docker-compose in ../traefik - ../traefik/data:/traefik:ro # mount mailcow's SSL folder - ./data/assets/ssl/:/output:rw environment : # only change this, if you're using another domain for mailcow's web frontend compared to the standard config - DOMAIN=${MAILCOW_HOSTNAME} networks : web : external : true Start the new containers with docker-compose up -d . Now, there's only one thing left to do, which is setup the certs so that the mail services can use them as well, since Traefik 2 uses an acme v2 format to save ALL the license from all the domains we have, we'll need to find a way to dump the certs, lucky we have this tiny container which grabs the acme.json file trough a volume, and a variable DOMAIN=example.org , and with these, the container will output the cert.pem and key.pem files, for this we'll simply run the traefik-certs-dumper container binding the /traefik volume to the folder where our acme.json is saved, bind the /output volume to our mailcow data/assets/ssl/ folder, and set up the DOMAIN=example.org variable to the domain we want the certs dumped from. This container will watch over the acme.json file for any changes, and regenerate the cert.pem and key.pem files directly into data/assets/ssl/ being the path binded to the container's /output path. You can use the command line to run it, or use the docker-compose shown here . After we have the certs dumped, we'll have to reload the configs from our postfix and dovecot containers, and check the certs, you can see how here . Aaand that should be it \ud83d\ude0a, you can check if the Traefik router works fine trough Traefik's dashboard / traefik logs / accessing the setted domain trough https, or / and check HTTPS, SMTP and IMAP trough the commands shown on the page linked before.","title":"Traefik v2 (community supported)"},{"location":"firststeps-rp/#optional-post-hook-script-for-non-mailcow-acme-clients","text":"Using a local certbot (or any other ACME client) requires to restart some containers, you can do this with a post-hook script. Make sure you change the paths accordingly: #!/bin/bash cp /etc/letsencrypt/live/my.domain.tld/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem cp /etc/letsencrypt/live/my.domain.tld/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem postfix_c=$(docker ps -qaf name=postfix-mailcow) dovecot_c=$(docker ps -qaf name=dovecot-mailcow) nginx_c=$(docker ps -qaf name=nginx-mailcow) docker restart ${postfix_c} ${dovecot_c} ${nginx_c}","title":"Optional: Post-hook script for non-mailcow ACME clients"},{"location":"firststeps-rp/#adding-additional-server-names-for-mailcow-ui","text":"If you plan to use a server name that is not MAILCOW_HOSTNAME in your reverse proxy, make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES first. Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond to your reverse proxy with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Adding additional server names for mailcow UI"},{"location":"firststeps-rspamd_ui/","text":"Rspamd is an easy to use spam filtering tool presently installed with mailcow. Go to the mailcow web admin interface Navigate to the Access tab. (Configuration > Administration > Access) Modify the Rspamd UI password Go to https://${MAILCOW_HOSTNAME}/rspamd in a browser and log in! Additional configuration options and documentation can be found here : https://rspamd.com/webui/","title":"Rspamd UI"},{"location":"firststeps-snat/","text":"SNAT is used to change the source address of the packets sent by mailcow. It can be used to change the outgoing IP address on systems with multiple IP addresses. Open mailcow.conf , set either or both of the following parameters: # Use this IPv4 for outgoing connections (SNAT) SNAT_TO_SOURCE=1.2.3.4 # Use this IPv6 for outgoing connections (SNAT) SNAT6_TO_SOURCE=dead:beef Run docker-compose up -d . The values are read by netfilter-mailcow. netfilter-mailcow will make sure, the post-routing rules are on position 1 in the netfilter table. It does automatically delete and re-create them if they are found on another position than 1. Check the output of docker-compose logs --tail=200 netfilter-mailcow to ensure the SNAT settings have been applied.","title":"SNAT"},{"location":"firststeps-ssl/","text":"Let's Encrypt (out-of-the-box) \u00b6 The \"acme-mailcow\" container will try to obtain a LE certificate for ${MAILCOW_HOSTNAME} , autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN . Warning mailcow must be available on port 80 for the acme-client to work. Our reverse proxy example configurations do cover that. You can also use any external ACME client (certbot for example) to obtain certificates, but you will need to make sure, that they are copied to the correct location and a post-hook reloads affected containers. See more in the Reverse Proxy documentation. By default, which means 0 domains are added to mailcow, it will try to obtain a certificate for ${MAILCOW_HOSTNAME} . For each domain you add, it will try to resolve autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN to its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. If it succeeds, a name will be added as SAN to the certificate request. Only names that can be validated, will be added as SAN. For every domain you remove, the certificate will be moved and a new certificate will be requested. It is not possible to keep domains in a certificate, when we are not able validate the challenge for those. If you want to re-run the ACME client, use docker-compose restart acme-mailcow and monitor its logs with docker-compose logs --tail=200 -f acme-mailcow . Additional domain names \u00b6 Edit \"mailcow.conf\" and add a parameter ADDITIONAL_SAN like this: Do not use quotes ( \" ) and do not use spaces between the names! ADDITIONAL_SAN=smtp.*,cert1.example.com,cert2.example.org,whatever.* Each name will be validated against its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. A wildcard name like smtp.* will try to obtain a smtp.DOMAIN_NAME SAN for each domain added to mailcow. Run docker-compose up -d to recreate affected containers automatically. Info Using names other name MAILCOW_HOSTNAME to access the mailcow UI may need further configuration. If you plan to use a server name that is not MAILCOW_HOSTNAME to access the mailcow UI (for example by adding mail.* to ADDITIONAL_SAN make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES . Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply. Force renewal \u00b6 To force a renewal, you need to create a file named force_renew and restart the acme-mailcow container: cd /opt/mailcow-dockerized touch data/assets/ssl/force_renew docker-compose restart acme-mailcow # Now check the logs for a renewal docker-compose logs --tail=200 -f acme-mailcow The file will be deleted automatically. Validation errors and how to skip validation \u00b6 You can skip the IP verification by setting SKIP_IP_CHECK=y in mailcow.conf (no quotes). Be warned that a misconfiguration will get you ratelimited by Let's Encrypt! This is primarily useful for multi-IP setups where the IP check would return the incorrect source IP address. Due to using dynamic IPs for acme-mailcow, source NAT is not consistent over restarts. If you encounter problems with \"HTTP validation\", but your IP address confirmation succeeds, you are most likely using firewalld, ufw or any other firewall, that disallows connections from br-mailcow to your external interface. Both firewalld and ufw disallow this by default. It is often not enough to just stop these firewall services. You'd need to stop mailcow ( docker-compose down ), stop the firewall service, flush the chains and restart Docker. You can also skip this validation method by setting SKIP_HTTP_VERIFICATION=y in \"mailcow.conf\". Be warned that this is discouraged. In most cases, the HTTP verification is skipped to workaround unknown NAT reflection issues, which are not resolved by ignoring this specific network misconfiguration. If you encounter problems generating TLSA records in the DNS overview within mailcow, you are most likely having issues with NAT reflection you should fix. If you changed a SKIP_* parameter, run docker-compose up -d to apply your changes. Disable Let's Encrypt \u00b6 Disable Let's Encrypt completely \u00b6 Set SKIP_LETS_ENCRYPT=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Skip all names but ${MAILCOW_HOSTNAME} \u00b6 Add ONLY_MAILCOW_HOSTNAME=y to \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . The Let's Encrypt subjectAltName limit of 100 domains \u00b6 Let's Encrypt currently has a limit of 100 Domain Names per Certificate . By default, \"acme-mailcow\" will create a single SAN certificate for all validated domains (see the first section and Additional domain names ). This provides best compatibility but means the Let's Encrypt limit exceeds if you add too many domains to a single mailcow installation. To solve this, you can configure ENABLE_SSL_SNI to generate: A main server certificate with MAILCOW_HOSTNAME and all fully qualified domain names in the ADDITIONAL_SAN config One additional certificate for each domain found in the database with autodiscover. , autoconfig. and any other ADDITIONAL_SAN configured in this format (subdomain.*). Limitations: A certificate name ADDITIONAL_SAN=test.example.com will be added as SAN to the main certificate. A separate certificate/key pair will not be generated for this format. Postfix, Dovecot and Nginx will then serve these certificates with SNI. Set ENABLE_SSL_SNI=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Warning Not all clients support SNI, see Dovecot documentation or Wikipedia . You should make sure these clients use the MAILCOW_HOSTNAME for secure connections if you enable this feature. Here is an example: MAILCOW_HOSTNAME=server.email.tld ADDITIONAL_SAN=webmail.email.tld,mail.* Mailcow email domains: \"domain1.tld\" and \"domain2.tld\" The following certificates will be generated: server.email.tld, webmail.email.tld -> this is the default certificate, all clients can connect with these domains mail.domain1.tld, autoconfig.domain1.tld, autodiscover.domain1.tld -> individual certificate for domain1.tld, cannot be used by clients without SNI support mail.domain2.tld, autoconfig.domain2.tld, autodiscover.domain2.tld -> individual certificate for domain2.tld, cannot be used by clients without SNI support How to use your own certificate \u00b6 Make sure you disable mailcows internal LE client (see above). To use your own certificates, just save the combined certificate (containing the certificate and intermediate CA/CA if any) to data/assets/ssl/cert.pem and the corresponding key to data/assets/ssl/key.pem . IMPORTANT: Do not use symbolic links! Make sure you copy the certificates and do not link them to data/assets/ssl . Restart affected services afterwards: docker restart $(docker ps -qaf name=postfix-mailcow) docker restart $(docker ps -qaf name=nginx-mailcow) docker restart $(docker ps -qaf name=dovecot-mailcow) See Post-hook script for non-mailcow ACME clients for a full example script. Test against staging ACME directory \u00b6 Edit mailcow.conf and add LE_STAGING=y . Run docker-compose up -d to activate your changes. Custom directory URL \u00b6 Edit mailcow.conf and add the corresponding directory URL to the new variable DIRECTORY_URL : DIRECTORY_URL=https://acme-custom-v9000.api.letsencrypt.org/directory You cannot use LE_STAGING with DIRECTORY_URL . If both are set, only LE_STAGING is used. Run docker-compose up -d to activate your changes. Check your configuration \u00b6 Run docker-compose logs acme-mailcow to find out why a validation fails. To check if nginx serves the correct certificate, simply use a browser of your choice and check the displayed certificate. To check the certificate served by Postfix, Dovecot and Nginx we will use openssl : # Connect via SMTP (587) echo \"Q\" | openssl s_client -starttls smtp -crlf -connect mx.mailcow.email:587 # Connect via IMAP (143) echo \"Q\" | openssl s_client -starttls imap -showcerts -connect mx.mailcow.email:143 # Connect via HTTPS (443) echo \"Q\" | openssl s_client -connect mx.mailcow.email:443 To validate the expiry dates as returned by openssl against MAILCOW_HOSTNAME, you are able to use our helper script: cd /opt/mailcow-dockerized bash helper-scripts/expiry-dates.sh","title":"Advanced SSL"},{"location":"firststeps-ssl/#lets-encrypt-out-of-the-box","text":"The \"acme-mailcow\" container will try to obtain a LE certificate for ${MAILCOW_HOSTNAME} , autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN . Warning mailcow must be available on port 80 for the acme-client to work. Our reverse proxy example configurations do cover that. You can also use any external ACME client (certbot for example) to obtain certificates, but you will need to make sure, that they are copied to the correct location and a post-hook reloads affected containers. See more in the Reverse Proxy documentation. By default, which means 0 domains are added to mailcow, it will try to obtain a certificate for ${MAILCOW_HOSTNAME} . For each domain you add, it will try to resolve autodiscover.ADDED_MAIL_DOMAIN and autoconfig.ADDED_MAIL_DOMAIN to its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. If it succeeds, a name will be added as SAN to the certificate request. Only names that can be validated, will be added as SAN. For every domain you remove, the certificate will be moved and a new certificate will be requested. It is not possible to keep domains in a certificate, when we are not able validate the challenge for those. If you want to re-run the ACME client, use docker-compose restart acme-mailcow and monitor its logs with docker-compose logs --tail=200 -f acme-mailcow .","title":"Let's Encrypt (out-of-the-box)"},{"location":"firststeps-ssl/#additional-domain-names","text":"Edit \"mailcow.conf\" and add a parameter ADDITIONAL_SAN like this: Do not use quotes ( \" ) and do not use spaces between the names! ADDITIONAL_SAN=smtp.*,cert1.example.com,cert2.example.org,whatever.* Each name will be validated against its IPv6 address or - if IPv6 is not configured in your domain - IPv4 address. A wildcard name like smtp.* will try to obtain a smtp.DOMAIN_NAME SAN for each domain added to mailcow. Run docker-compose up -d to recreate affected containers automatically. Info Using names other name MAILCOW_HOSTNAME to access the mailcow UI may need further configuration. If you plan to use a server name that is not MAILCOW_HOSTNAME to access the mailcow UI (for example by adding mail.* to ADDITIONAL_SAN make sure to populate that name in mailcow.conf via ADDITIONAL_SERVER_NAMES . Names must be separated by commas and must not contain spaces. If you skip this step, mailcow may respond with an incorrect site. ADDITIONAL_SERVER_NAMES=webmail.domain.tld,other.example.tld Run docker-compose up -d to apply.","title":"Additional domain names"},{"location":"firststeps-ssl/#force-renewal","text":"To force a renewal, you need to create a file named force_renew and restart the acme-mailcow container: cd /opt/mailcow-dockerized touch data/assets/ssl/force_renew docker-compose restart acme-mailcow # Now check the logs for a renewal docker-compose logs --tail=200 -f acme-mailcow The file will be deleted automatically.","title":"Force renewal"},{"location":"firststeps-ssl/#validation-errors-and-how-to-skip-validation","text":"You can skip the IP verification by setting SKIP_IP_CHECK=y in mailcow.conf (no quotes). Be warned that a misconfiguration will get you ratelimited by Let's Encrypt! This is primarily useful for multi-IP setups where the IP check would return the incorrect source IP address. Due to using dynamic IPs for acme-mailcow, source NAT is not consistent over restarts. If you encounter problems with \"HTTP validation\", but your IP address confirmation succeeds, you are most likely using firewalld, ufw or any other firewall, that disallows connections from br-mailcow to your external interface. Both firewalld and ufw disallow this by default. It is often not enough to just stop these firewall services. You'd need to stop mailcow ( docker-compose down ), stop the firewall service, flush the chains and restart Docker. You can also skip this validation method by setting SKIP_HTTP_VERIFICATION=y in \"mailcow.conf\". Be warned that this is discouraged. In most cases, the HTTP verification is skipped to workaround unknown NAT reflection issues, which are not resolved by ignoring this specific network misconfiguration. If you encounter problems generating TLSA records in the DNS overview within mailcow, you are most likely having issues with NAT reflection you should fix. If you changed a SKIP_* parameter, run docker-compose up -d to apply your changes.","title":"Validation errors and how to skip validation"},{"location":"firststeps-ssl/#disable-lets-encrypt","text":"","title":"Disable Let's Encrypt"},{"location":"firststeps-ssl/#disable-lets-encrypt-completely","text":"Set SKIP_LETS_ENCRYPT=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d .","title":"Disable Let's Encrypt completely"},{"location":"firststeps-ssl/#skip-all-names-but-mailcow_hostname","text":"Add ONLY_MAILCOW_HOSTNAME=y to \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d .","title":"Skip all names but ${MAILCOW_HOSTNAME}"},{"location":"firststeps-ssl/#the-lets-encrypt-subjectaltname-limit-of-100-domains","text":"Let's Encrypt currently has a limit of 100 Domain Names per Certificate . By default, \"acme-mailcow\" will create a single SAN certificate for all validated domains (see the first section and Additional domain names ). This provides best compatibility but means the Let's Encrypt limit exceeds if you add too many domains to a single mailcow installation. To solve this, you can configure ENABLE_SSL_SNI to generate: A main server certificate with MAILCOW_HOSTNAME and all fully qualified domain names in the ADDITIONAL_SAN config One additional certificate for each domain found in the database with autodiscover. , autoconfig. and any other ADDITIONAL_SAN configured in this format (subdomain.*). Limitations: A certificate name ADDITIONAL_SAN=test.example.com will be added as SAN to the main certificate. A separate certificate/key pair will not be generated for this format. Postfix, Dovecot and Nginx will then serve these certificates with SNI. Set ENABLE_SSL_SNI=y in \"mailcow.conf\" and recreate \"acme-mailcow\" by running docker-compose up -d . Warning Not all clients support SNI, see Dovecot documentation or Wikipedia . You should make sure these clients use the MAILCOW_HOSTNAME for secure connections if you enable this feature. Here is an example: MAILCOW_HOSTNAME=server.email.tld ADDITIONAL_SAN=webmail.email.tld,mail.* Mailcow email domains: \"domain1.tld\" and \"domain2.tld\" The following certificates will be generated: server.email.tld, webmail.email.tld -> this is the default certificate, all clients can connect with these domains mail.domain1.tld, autoconfig.domain1.tld, autodiscover.domain1.tld -> individual certificate for domain1.tld, cannot be used by clients without SNI support mail.domain2.tld, autoconfig.domain2.tld, autodiscover.domain2.tld -> individual certificate for domain2.tld, cannot be used by clients without SNI support","title":"The Let's Encrypt subjectAltName limit of 100 domains"},{"location":"firststeps-ssl/#how-to-use-your-own-certificate","text":"Make sure you disable mailcows internal LE client (see above). To use your own certificates, just save the combined certificate (containing the certificate and intermediate CA/CA if any) to data/assets/ssl/cert.pem and the corresponding key to data/assets/ssl/key.pem . IMPORTANT: Do not use symbolic links! Make sure you copy the certificates and do not link them to data/assets/ssl . Restart affected services afterwards: docker restart $(docker ps -qaf name=postfix-mailcow) docker restart $(docker ps -qaf name=nginx-mailcow) docker restart $(docker ps -qaf name=dovecot-mailcow) See Post-hook script for non-mailcow ACME clients for a full example script.","title":"How to use your own certificate"},{"location":"firststeps-ssl/#test-against-staging-acme-directory","text":"Edit mailcow.conf and add LE_STAGING=y . Run docker-compose up -d to activate your changes.","title":"Test against staging ACME directory"},{"location":"firststeps-ssl/#custom-directory-url","text":"Edit mailcow.conf and add the corresponding directory URL to the new variable DIRECTORY_URL : DIRECTORY_URL=https://acme-custom-v9000.api.letsencrypt.org/directory You cannot use LE_STAGING with DIRECTORY_URL . If both are set, only LE_STAGING is used. Run docker-compose up -d to activate your changes.","title":"Custom directory URL"},{"location":"firststeps-ssl/#check-your-configuration","text":"Run docker-compose logs acme-mailcow to find out why a validation fails. To check if nginx serves the correct certificate, simply use a browser of your choice and check the displayed certificate. To check the certificate served by Postfix, Dovecot and Nginx we will use openssl : # Connect via SMTP (587) echo \"Q\" | openssl s_client -starttls smtp -crlf -connect mx.mailcow.email:587 # Connect via IMAP (143) echo \"Q\" | openssl s_client -starttls imap -showcerts -connect mx.mailcow.email:143 # Connect via HTTPS (443) echo \"Q\" | openssl s_client -connect mx.mailcow.email:443 To validate the expiry dates as returned by openssl against MAILCOW_HOSTNAME, you are able to use our helper script: cd /opt/mailcow-dockerized bash helper-scripts/expiry-dates.sh","title":"Check your configuration"},{"location":"firststeps-sync_jobs_migration/","text":"Sync jobs are used to copy or move existing emails from an external IMAP server or within mailcow's existing mailboxes. Info Depending on your mailbox's ACL you may not have the option to add a sync job. Please contact your domain administrator if so. Setup a Sync Job \u00b6 In the \"Mail Setup\" or \"User Settings\" interface, create a new sync job. If you are an administrator, select the username of the downstream mailcow mailbox in the \"Username\" dropdown. Fill in the \"Host\" and \"Port\" fields with their respective correct values from the upstream IMAP server. In the \"Username\" and \"Password\" fields, supply the correct access credentials from the upstream IMAP server. Select the \"Encryption Method\". If the upstream IMAP server uses port 143, it is likely that the encryption method is TLS and SSL for port 993. Nevertheless, you can use PLAIN authentication, but it is stongly discouraged. For all ther other fields, you can leave them as is or modify them as desired. Make sure to tick \"Active\" and click \"Add\". Info Once Completed, log into the mailbox and check if all emails are imported correctly. If all goes well, all your mails shall end up in your new mailbox. And don't forget to delete or deactivate the sync job after it is used.","title":"Sync job migration"},{"location":"firststeps-sync_jobs_migration/#setup-a-sync-job","text":"In the \"Mail Setup\" or \"User Settings\" interface, create a new sync job. If you are an administrator, select the username of the downstream mailcow mailbox in the \"Username\" dropdown. Fill in the \"Host\" and \"Port\" fields with their respective correct values from the upstream IMAP server. In the \"Username\" and \"Password\" fields, supply the correct access credentials from the upstream IMAP server. Select the \"Encryption Method\". If the upstream IMAP server uses port 143, it is likely that the encryption method is TLS and SSL for port 993. Nevertheless, you can use PLAIN authentication, but it is stongly discouraged. For all ther other fields, you can leave them as is or modify them as desired. Make sure to tick \"Active\" and click \"Add\". Info Once Completed, log into the mailbox and check if all emails are imported correctly. If all goes well, all your mails shall end up in your new mailbox. And don't forget to delete or deactivate the sync job after it is used.","title":"Setup a Sync Job"},{"location":"i_u_m_deinstall/","text":"To remove mailcow: dockerized with all it's volumes, images and containers do: docker-compose down -v --rmi all --remove-orphans Info -v Remove named volumes declared in the volumes section of the Compose file and anonymous volumes attached to containers. --rmi Remove images. Type must be one of: all : Remove all images used by any service. local : Remove only images that don't have a custom tag set by the image field. --remove-orphans Remove containers for services not defined in the compose file. By default docker-compose down only removes currently active containers and networks defined in the docker-compose.yml .","title":"Deinstallation"},{"location":"i_u_m_install/","text":"You need Docker (a version >= 20.10.2 is required) and Docker Compose (a version <= 2.0 is required). 1. Learn how to install Docker and Docker Compose . Quick installation for most operation systems: Docker curl -sSL https://get.docker.com/ | CHANNEL=stable sh # After the installation process is finished, you may need to enable the service and make sure it is started (e.g. CentOS 7) systemctl enable --now docker Docker-Compose Warning mailcow requires the latest version of docker-compose v1. It is highly recommended to use the commands below to install docker-compose . Package managers (e.g. apt , yum ) likely won't give you the correct version. Note: This command downloads docker-compose from the official Docker Github repository and is a safe method. The snippet will determine the latest supported version by mailcow. In almost all cases this is the latest version available (exceptions are broken releases or major changes not yet supported by mailcow). curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Please use the latest Docker engine available and do not use the engine that ships with your distros repository. 1.1. On SELinux enabled systems, e.g. CentOS 7: Check if \"container-selinux\" package is present on your system: rpm -qa | grep container-selinux If the above command returns an empty or no output, you should install it via your package manager. Check if docker has SELinux support enabled: docker info | grep selinux If the above command returns an empty or no output, create or edit /etc/docker/daemon.json and add \"selinux-enabled\": true . Example file content: { \"selinux-enabled\": true } Restart the docker daemon and verify SELinux is now enabled. This step is required to make sure mailcows volumes are properly labeled as declared in the compose file. If you are interested in how this works, you can check out the readme of https://github.com/containers/container-selinux which links to a lot of useful information on that topic. 2. Clone the master branch of the repository, make sure your umask equals 0022. Please clone the repository as root user and also control the stack as root. We will modify attributes - if necessary - while boostrapping the containers automatically and make sure everything is secured. The update.sh script must therefore also be run as root. It might be necessary to change ownership and other attributes of files you will otherwise not have access to. We drop permissions for every exposed application and will not run an exposed service as root! Controlling the Docker daemon as non-root user does not give you additional security. The unprivileged user will spawn the containers as root likewise. The behaviour of the stack is identical. $ su # umask 0022 # <- Verify it is 0022 # cd /opt # git clone https://github.com/mailcow/mailcow-dockerized # cd mailcow-dockerized 3. Generate a configuration file. Use a FQDN ( host.domain.tld ) as hostname when asked. ./generate_config.sh 4. Change configuration if you want or need to. nano mailcow.conf If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080. You may need to stop an existing pre-installed MTA which blocks port 25/tcp. See this chapter to learn how to reconfigure Postfix to run besides mailcow after a successful installation. Some updates modify mailcow.conf and add new parameters. It is hard to keep track of them in the documentation. Please check their description and, if unsure, ask at the known channels for advise. 4.1. Users with a MTU not equal to 1500 (e.g. OpenStack): Whenever you run into trouble and strange phenomena, please check your MTU. Edit docker-compose.yml and change the network settings according to your MTU. Add the new driver_opts parameter like this: networks: mailcow-network: ... driver_opts: com.docker.network.driver.mtu: 1450 ... 4.2. Users without an IPv6 enabled network on their host system: Enable IPv6. Finally. If you do not have an IPv6 enabled network on your host and you don't care for a better internet (thehe), it is recommended to disable IPv6 for the mailcow network to prevent unforeseen issues. 5. Pull the images and run the compose file. The parameter -d will start mailcow: dockerized detached: docker-compose pull docker-compose up -d Done! You can now access https://${MAILCOW_HOSTNAME} with the default credentials admin + password moohoo . Info If you are not using mailcow behind a reverse proxy, you should redirect all HTTP requests to HTTPS . The database will be initialized right after a connection to MySQL can be established. Your data will persist in multiple Docker volumes, that are not deleted when you recreate or delete containers. Run docker volume ls to see a list of all volumes. You can safely run docker-compose down without removing persistent data.","title":"Installation"},{"location":"i_u_m_migration/","text":"Warning This guide assumes you intend to migrate an existing mailcow server (source) over to a brand new, empty server (target). It takes no care about preserving any existing data on your target server and will erase anything within /var/lib/docker/volumes and thus any Docker volumes you may have already set up. Tip Alternatively, you can use the ./helper-scripts/backup_and_restore.sh script to create a full backup on the source machine, then install mailcow on the target machine as usual, copy over your mailcow.conf and use the same script to restore your backup to the target machine. 1. Install Docker and Docker Compose on your new server. Quick installation for most operation systems: Docker curl -sSL https://get.docker.com/ | CHANNEL=stable sh # After the installation process is finished, you may need to enable the service and make sure it is started (e.g. CentOS 7) systemctl enable docker.service docker-compose curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Please use the latest Docker engine available and do not use the engine that ships with your distros repository. 2. Stop Docker and assure Docker has stopped: systemctl stop docker.service systemctl status docker.service 3. Run the following commands on the source machine (take care of adding the trailing slashes in the first path parameter as shown below!) - WARNING: This command will erase anything that may already exist under /var/lib/docker/volumes on the target machine : rsync -aHhP --numeric-ids --delete /opt/mailcow-dockerized/ root@target-machine.example.com:/opt/mailcow-dockerized rsync -aHhP --numeric-ids --delete /var/lib/docker/volumes/ root@target-machine.example.com:/var/lib/docker/volumes 4. Shut down mailcow and stop Docker on the source machine. cd /opt/mailcow-dockerized docker-compose down systemctl stop docker.service 5. Repeat step 3 with the same commands. This will be much quicker than the first time. 6. Switch over to the target machine and start Docker. systemctl start docker.service 7. Now pull the mailcow Docker images on the target machine. cd /opt/mailcow-dockerized docker-compose pull 8. Start the whole mailcow stack and everything should be done! docker-compose up -d 9. Finally, change your DNS settings to point to the target server.","title":"Migration"},{"location":"i_u_m_update/","text":"Automatic update \u00b6 An update script in your mailcow-dockerized directory will take care of updates. But use it with caution! If you think you made a lot of changes to the mailcow code, you should use the manual update guide below. Run the update script: ./update.sh If it needs to, it will ask you how you wish to proceed. Merge errors will be reported. Some minor conflicts will be auto-corrected (in favour for the mailcow: dockerized repository code). Options \u00b6 # Options can be combined # - Check for updates and show changes ./update.sh --check # Do not try to update docker-compose, **make sure to use the latest docker-compose available** ./update.sh --no-update-compose # - Do not start mailcow after applying an update ./update.sh --skip-start # - Force update (unattended, but unsupported, use at own risk) ./update.sh --force # - Run garbage collector to cleanup old image tags and exit ./update.sh --gc # - Update with merge strategy option \"ours\" instead of \"theirs\" # This will **solve conflicts** when merging in favor for your local changes and should be avoided. Local changes will always be kept, unless we changed file XY, too. ./update.sh --ours # - Don't update, but prefetch images and exit ./update.sh --prefetch I forgot what I changed before running update.sh \u00b6 See git log --pretty=oneline | grep -i \"before update\" , you will have an output similar to this: 22cd00b5e28893ef9ddef3c2b5436453cc5223ab Before update on 2020-09-28_19_25_45 dacd4fb9b51e9e1c8a37d84485b92ffaf6c59353 Before update on 2020-08-07_13_31_31 Run git diff 22cd00b5e28893ef9ddef3c2b5436453cc5223ab to see what changed. Can I roll back? \u00b6 Yes. See the topic above, instead of a diff, you run checkout: docker-compose down # Replace commit ID 22cd00b5e28893ef9ddef3c2b5436453cc5223ab by your ID git checkout 22cd00b5e28893ef9ddef3c2b5436453cc5223ab docker-compose pull docker-compose up -d Hooks \u00b6 You can hook into the update mechanism by adding scripts called pre_commit_hook.sh and post_commit_hook.sh to your mailcows root directory. See this for more details. Footnotes \u00b6 There is no release cycle regarding updates.","title":"Update"},{"location":"i_u_m_update/#automatic-update","text":"An update script in your mailcow-dockerized directory will take care of updates. But use it with caution! If you think you made a lot of changes to the mailcow code, you should use the manual update guide below. Run the update script: ./update.sh If it needs to, it will ask you how you wish to proceed. Merge errors will be reported. Some minor conflicts will be auto-corrected (in favour for the mailcow: dockerized repository code).","title":"Automatic update"},{"location":"i_u_m_update/#options","text":"# Options can be combined # - Check for updates and show changes ./update.sh --check # Do not try to update docker-compose, **make sure to use the latest docker-compose available** ./update.sh --no-update-compose # - Do not start mailcow after applying an update ./update.sh --skip-start # - Force update (unattended, but unsupported, use at own risk) ./update.sh --force # - Run garbage collector to cleanup old image tags and exit ./update.sh --gc # - Update with merge strategy option \"ours\" instead of \"theirs\" # This will **solve conflicts** when merging in favor for your local changes and should be avoided. Local changes will always be kept, unless we changed file XY, too. ./update.sh --ours # - Don't update, but prefetch images and exit ./update.sh --prefetch","title":"Options"},{"location":"i_u_m_update/#i-forgot-what-i-changed-before-running-updatesh","text":"See git log --pretty=oneline | grep -i \"before update\" , you will have an output similar to this: 22cd00b5e28893ef9ddef3c2b5436453cc5223ab Before update on 2020-09-28_19_25_45 dacd4fb9b51e9e1c8a37d84485b92ffaf6c59353 Before update on 2020-08-07_13_31_31 Run git diff 22cd00b5e28893ef9ddef3c2b5436453cc5223ab to see what changed.","title":"I forgot what I changed before running update.sh"},{"location":"i_u_m_update/#can-i-roll-back","text":"Yes. See the topic above, instead of a diff, you run checkout: docker-compose down # Replace commit ID 22cd00b5e28893ef9ddef3c2b5436453cc5223ab by your ID git checkout 22cd00b5e28893ef9ddef3c2b5436453cc5223ab docker-compose pull docker-compose up -d","title":"Can I roll back?"},{"location":"i_u_m_update/#hooks","text":"You can hook into the update mechanism by adding scripts called pre_commit_hook.sh and post_commit_hook.sh to your mailcows root directory. See this for more details.","title":"Hooks"},{"location":"i_u_m_update/#footnotes","text":"There is no release cycle regarding updates.","title":"Footnotes"},{"location":"model-acl/","text":"Editing a domain administrator or a mailbox user allows to set restrictions to that account. Important : For overlapping modules like sync jobs, which both domain administrators and mailbox users can be granted access to, the domain administrators permissions are inherited, when logging in as mailbox user. Some examples: 1. A domain administror has not access to sync jobs but can login as mailbox user When logging in as mailbox user, he does not gain access to sync jobs, even if the given mailbox user has access when logging in directly 2. A domain administror has access to sync jobs and can login as mailbox user The mailbox user he tries to login as has not access to sync jobs The domain administrator, now logged in as mailbox user, inherits its permission to the mailbox user and can access sync jobs 3. A domain administrator logs in as mailbox user Every permission, that does not exist in a domain administrators ACL, is automatically granted (example: time-limited alias, TLS policy etc.)","title":"ACL"},{"location":"model-passwd/","text":"Fully supported hashing methods \u00b6 The most current mailcow fully supports the following hashing methods. The default hashing method is written in bold: BLF-CRYPT SSHA SSHA256 SSHA512 The methods above can be used in mailcow.conf as MAILCOW_PASS_SCHEME value. Read-only hashing methods \u00b6 The following methods are supported read only . If you plan to use SOGo (as per default), you need a SOGo compatible hashing method. Please see the note at the bottom of this page how to update the view if necessary. With SOGo disabled, all hashing methods below will be able to be read by mailcow and Dovecot. ARGON2I (SOGo compatible) ARGON2ID (SOGo compatible) CLEAR CLEARTEXT CRYPT (SOGo compatible) DES-CRYPT LDAP-MD5 (SOGo compatible) MD5 (SOGo compatible) MD5-CRYPT (SOGo compatible) PBKDF2 (SOGo compatible) PLAIN (SOGo compatible) PLAIN-MD4 PLAIN-MD5 PLAIN-TRUNC SHA (SOGo compatible) SHA1 (SOGo compatible) SHA256 (SOGo compatible) SHA256-CRYPT (SOGo compatible) SHA512 (SOGo compatible) SHA512-CRYPT (SOGo compatible) SMD5 (SOGo compatible) That means mailcow is able to verify users with a hash like {MD5}1a1dc91c907325c69271ddf0c944bc72 from the database. The value of MAILCOW_PASS_SCHEME will always be used to encrypt new passwords. I changed the password hashes in the \"mailbox\" SQL table and cannot login. A \"view\" needs to be updated. You can trigger this by restarting sogo-mailcow: docker-compose restart sogo-mailcow","title":"Password hashing"},{"location":"model-passwd/#fully-supported-hashing-methods","text":"The most current mailcow fully supports the following hashing methods. The default hashing method is written in bold: BLF-CRYPT SSHA SSHA256 SSHA512 The methods above can be used in mailcow.conf as MAILCOW_PASS_SCHEME value.","title":"Fully supported hashing methods"},{"location":"model-passwd/#read-only-hashing-methods","text":"The following methods are supported read only . If you plan to use SOGo (as per default), you need a SOGo compatible hashing method. Please see the note at the bottom of this page how to update the view if necessary. With SOGo disabled, all hashing methods below will be able to be read by mailcow and Dovecot. ARGON2I (SOGo compatible) ARGON2ID (SOGo compatible) CLEAR CLEARTEXT CRYPT (SOGo compatible) DES-CRYPT LDAP-MD5 (SOGo compatible) MD5 (SOGo compatible) MD5-CRYPT (SOGo compatible) PBKDF2 (SOGo compatible) PLAIN (SOGo compatible) PLAIN-MD4 PLAIN-MD5 PLAIN-TRUNC SHA (SOGo compatible) SHA1 (SOGo compatible) SHA256 (SOGo compatible) SHA256-CRYPT (SOGo compatible) SHA512 (SOGo compatible) SHA512-CRYPT (SOGo compatible) SMD5 (SOGo compatible) That means mailcow is able to verify users with a hash like {MD5}1a1dc91c907325c69271ddf0c944bc72 from the database. The value of MAILCOW_PASS_SCHEME will always be used to encrypt new passwords. I changed the password hashes in the \"mailbox\" SQL table and cannot login. A \"view\" needs to be updated. You can trigger this by restarting sogo-mailcow: docker-compose restart sogo-mailcow","title":"Read-only hashing methods"},{"location":"model-sender_rcv/","text":"When a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address. Mailbox me @example . org is created . example . org is a primary domain . Note : a mailbox cannot be created in an alias domain . me @example . org is only known as me @example . org . me @example . org is allowed to send as me @example . org . We can add an alias domain for example.org: Alias domain alias . com is added and assigned to primary domain example . org . me @example . org is now known as me @example . org and me @alias . com . me @example . org is now allowed to send as me @example . org and me @alias . com . We can add aliases for a mailbox to receive mail for and to send from this new address. It is important to know, that you are not able to receive mail for my-alias@my-alias-domain.tld . You would need to create this particular alias. me @example . org is assigned the alias alias @example . org me @example . org is now known as me @example . org , me @alias . com , alias @example . org me @example . org is NOT known as alias @alias . com . Please note that this does not apply to catch-all aliases: Alias domain alias . com is added and assigned to primary domain example . org me @example . org is assigned the catch - all alias @example . org me @example . org is still just known as me @example . org , which is the only available send - as option Any email send to alias . com will match the catch - all alias for example . org Administrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users (\"delegate\" them). You can choose between mailbox users or completely disable the sender check for domains. SOGo \"mail from\" addresses \u00b6 Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains. If you want to select another existing mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator needs to grant you access as described above.","title":"Sender and receiver model"},{"location":"model-sender_rcv/#sogo-mail-from-addresses","text":"Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains. If you want to select another existing mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator needs to grant you access as described above.","title":"SOGo \"mail from\" addresses"},{"location":"prerequisite-dns/","text":"Below you can find a list of recommended DNS records . While some are mandatory for a mail server (A, MX), others are recommended to build a good reputation score (TXT/SPF) or used for auto-configuration of mail clients (SRV). References \u00b6 A good article covering all relevant topics: \"3 DNS Records Every Email Marketer Must Know\" Another great one, but Zimbra as an example platform: \"Best Practices on Email Protection: SPF, DKIM and DMARC\" An in-depth discussion of SPF, DKIM and DMARC: \"How to eliminate spam and protect your name with DMARC\" A thorough guide on understanding DMARC: \"Demystifying DMARC: A guide to preventing email spoofing\" Reverse DNS of your IP address \u00b6 Make sure that the PTR record of your IP address matches the FQDN of your mailcow host: ${MAILCOW_HOSTNAME} 1 . This record is usually set at the provider you leased the IP address (server) from. The minimal DNS configuration \u00b6 This example shows you a set of records for one domain managed by mailcow. Each domain that is added to mailcow needs at least this set of records to function correctly. # Name Type Value mail IN A 1.2.3.4 autodiscover IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) autoconfig IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) @ IN MX 10 mail.example.org. (your ${MAILCOW_HOSTNAME}) DKIM, SPF and DMARC \u00b6 In the example DNS zone file snippet below, a simple SPF TXT record is used to only allow THIS server (the MX) to send mail for your domain. Every other server is disallowed but able to (\" ~all \"). Please refer to SPF Project for further reading. # Name Type Value @ IN TXT \"v=spf1 mx a -all\" It is highly recommended to create a DKIM TXT record in your mailcow UI and set the corresponding TXT record in your DNS records. Please refer to OpenDKIM for further reading. # Name Type Value dkim._domainkey IN TXT \"v=DKIM1; k=rsa; t=s; s=email; p=...\" The last step in protecting yourself and others is the implementation of a DMARC TXT record, for example by using the DMARC Assistant ( check ). # Name Type Value _dmarc IN TXT \"v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.org\" The advanced DNS configuration \u00b6 SRV records specify the server(s) for a specific protocol on your domain. If you want to explicitly announce a service as not provided, give \".\" as the target address (instead of \"mail.example.org.\"). Please refer to RFC 2782 . # Name Type Priority Weight Port Value _autodiscover._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN TXT \"path=/SOGo/dav/\" _carddavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _carddavs._tcp IN TXT \"path=/SOGo/dav/\" _imap._tcp IN SRV 0 1 143 mail.example.org. (your ${MAILCOW_HOSTNAME}) _imaps._tcp IN SRV 0 1 993 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3._tcp IN SRV 0 1 110 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3s._tcp IN SRV 0 1 995 mail.example.org. (your ${MAILCOW_HOSTNAME}) _sieve._tcp IN SRV 0 1 4190 mail.example.org. (your ${MAILCOW_HOSTNAME}) _smtps._tcp IN SRV 0 1 465 mail.example.org. (your ${MAILCOW_HOSTNAME}) _submission._tcp IN SRV 0 1 587 mail.example.org. (your ${MAILCOW_HOSTNAME}) Testing \u00b6 Here are some tools you can use to verify your DNS configuration: MX Toolbox (DNS, SMTP, RBL) port25.com (DKIM, SPF) Mail-tester (DKIM, DMARC, SPF) DMARC Analyzer (DMARC, SPF) MultiRBL.valli.org (DNSBL, RBL, FCrDNS) Misc \u00b6 Optional DMARC Statistics \u00b6 If you are interested in statistics, you can additionally register with some of the many below DMARC statistic services - or self-host your own. Tip It is worth considering that if you request DMARC statistic reports to your mailcow server and your mailcow server is not configured correctly to receive these reports, you may not get accurate and complete results. Please consider using an alternative email domain for receiving DMARC reports. It is worth mentioning, that the following suggestions are not a comprehensive list of all services and tools available, but only a small few of the many choices. Postmaster Tool parsedmarc (self-hosted) Fraudmarc Postmark Dmarcian Tip These services may provide you with a TXT record you need to insert into your DNS records as the provider specifies. Please ensure you read the provider's documentation from the service you choose as this process may vary. Email test for SPF, DKIM and DMARC: \u00b6 To run a rudimentary email authentication check, send a mail to check-auth at verifier.port25.com and wait for a reply. You will find a report similar to the following: ========================================================== Summary of Results ========================================================== SPF check: pass \"iprev\" check: pass DKIM check: pass DKIM check: pass SpamAssassin check: ham ========================================================== Details: ========================================================== .... The full report will contain more technical details. Fully Qualified Domain Name (FQDN) \u00b6 A Fully Qualified Domain Name ( FQDN ) is the complete (absolute) domain name for a specific computer or host, on the Internet. The FQDN consists of at least three parts divided by a dot: the hostname, the domain name, and the Top Level Domain ( TLD for short). In the example of mx.mailcow.email the hostname would be mx , the domain name mailcow and the TLD email . \u21a9","title":"DNS setup"},{"location":"prerequisite-dns/#references","text":"A good article covering all relevant topics: \"3 DNS Records Every Email Marketer Must Know\" Another great one, but Zimbra as an example platform: \"Best Practices on Email Protection: SPF, DKIM and DMARC\" An in-depth discussion of SPF, DKIM and DMARC: \"How to eliminate spam and protect your name with DMARC\" A thorough guide on understanding DMARC: \"Demystifying DMARC: A guide to preventing email spoofing\"","title":"References"},{"location":"prerequisite-dns/#reverse-dns-of-your-ip-address","text":"Make sure that the PTR record of your IP address matches the FQDN of your mailcow host: ${MAILCOW_HOSTNAME} 1 . This record is usually set at the provider you leased the IP address (server) from.","title":"Reverse DNS of your IP address"},{"location":"prerequisite-dns/#the-minimal-dns-configuration","text":"This example shows you a set of records for one domain managed by mailcow. Each domain that is added to mailcow needs at least this set of records to function correctly. # Name Type Value mail IN A 1.2.3.4 autodiscover IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) autoconfig IN CNAME mail.example.org. (your ${MAILCOW_HOSTNAME}) @ IN MX 10 mail.example.org. (your ${MAILCOW_HOSTNAME})","title":"The minimal DNS configuration"},{"location":"prerequisite-dns/#dkim-spf-and-dmarc","text":"In the example DNS zone file snippet below, a simple SPF TXT record is used to only allow THIS server (the MX) to send mail for your domain. Every other server is disallowed but able to (\" ~all \"). Please refer to SPF Project for further reading. # Name Type Value @ IN TXT \"v=spf1 mx a -all\" It is highly recommended to create a DKIM TXT record in your mailcow UI and set the corresponding TXT record in your DNS records. Please refer to OpenDKIM for further reading. # Name Type Value dkim._domainkey IN TXT \"v=DKIM1; k=rsa; t=s; s=email; p=...\" The last step in protecting yourself and others is the implementation of a DMARC TXT record, for example by using the DMARC Assistant ( check ). # Name Type Value _dmarc IN TXT \"v=DMARC1; p=reject; rua=mailto:mailauth-reports@example.org\"","title":"DKIM, SPF and DMARC"},{"location":"prerequisite-dns/#the-advanced-dns-configuration","text":"SRV records specify the server(s) for a specific protocol on your domain. If you want to explicitly announce a service as not provided, give \".\" as the target address (instead of \"mail.example.org.\"). Please refer to RFC 2782 . # Name Type Priority Weight Port Value _autodiscover._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _caldavs._tcp IN TXT \"path=/SOGo/dav/\" _carddavs._tcp IN SRV 0 1 443 mail.example.org. (your ${MAILCOW_HOSTNAME}) _carddavs._tcp IN TXT \"path=/SOGo/dav/\" _imap._tcp IN SRV 0 1 143 mail.example.org. (your ${MAILCOW_HOSTNAME}) _imaps._tcp IN SRV 0 1 993 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3._tcp IN SRV 0 1 110 mail.example.org. (your ${MAILCOW_HOSTNAME}) _pop3s._tcp IN SRV 0 1 995 mail.example.org. (your ${MAILCOW_HOSTNAME}) _sieve._tcp IN SRV 0 1 4190 mail.example.org. (your ${MAILCOW_HOSTNAME}) _smtps._tcp IN SRV 0 1 465 mail.example.org. (your ${MAILCOW_HOSTNAME}) _submission._tcp IN SRV 0 1 587 mail.example.org. (your ${MAILCOW_HOSTNAME})","title":"The advanced DNS configuration"},{"location":"prerequisite-dns/#testing","text":"Here are some tools you can use to verify your DNS configuration: MX Toolbox (DNS, SMTP, RBL) port25.com (DKIM, SPF) Mail-tester (DKIM, DMARC, SPF) DMARC Analyzer (DMARC, SPF) MultiRBL.valli.org (DNSBL, RBL, FCrDNS)","title":"Testing"},{"location":"prerequisite-dns/#misc","text":"","title":"Misc"},{"location":"prerequisite-dns/#optional-dmarc-statistics","text":"If you are interested in statistics, you can additionally register with some of the many below DMARC statistic services - or self-host your own. Tip It is worth considering that if you request DMARC statistic reports to your mailcow server and your mailcow server is not configured correctly to receive these reports, you may not get accurate and complete results. Please consider using an alternative email domain for receiving DMARC reports. It is worth mentioning, that the following suggestions are not a comprehensive list of all services and tools available, but only a small few of the many choices. Postmaster Tool parsedmarc (self-hosted) Fraudmarc Postmark Dmarcian Tip These services may provide you with a TXT record you need to insert into your DNS records as the provider specifies. Please ensure you read the provider's documentation from the service you choose as this process may vary.","title":"Optional DMARC Statistics"},{"location":"prerequisite-dns/#email-test-for-spf-dkim-and-dmarc","text":"To run a rudimentary email authentication check, send a mail to check-auth at verifier.port25.com and wait for a reply. You will find a report similar to the following: ========================================================== Summary of Results ========================================================== SPF check: pass \"iprev\" check: pass DKIM check: pass DKIM check: pass SpamAssassin check: ham ========================================================== Details: ========================================================== .... The full report will contain more technical details.","title":"Email test for SPF, DKIM and DMARC:"},{"location":"prerequisite-dns/#fully-qualified-domain-name-fqdn","text":"A Fully Qualified Domain Name ( FQDN ) is the complete (absolute) domain name for a specific computer or host, on the Internet. The FQDN consists of at least three parts divided by a dot: the hostname, the domain name, and the Top Level Domain ( TLD for short). In the example of mx.mailcow.email the hostname would be mx , the domain name mailcow and the TLD email . \u21a9","title":"Fully Qualified Domain Name (FQDN)"},{"location":"prerequisite-system/","text":"Before you run mailcow: dockerized , there are a few requirements that you should check: Warning Do not try to install mailcow on a Synology/QNAP device (any NAS), OpenVZ, LXC or other container platforms. KVM, ESX, Hyper-V and other full virtualization platforms are supported. Info mailcow: dockerized requires some ports to be open for incoming connections, so make sure that your firewall is not blocking these. Make sure that no other application is interfering with mailcow's configuration, such as another mail service A correct DNS setup is crucial to every good mailserver setup, so please make sure you got at least the basics covered before you begin! Make sure that your system has a correct date and time setup . This is crucial for various components like two factor TOTP authentication. Minimum System Resources \u00b6 OpenVZ, Virtuozzo and LXC are not supported . Please make sure that your system has at least the following resources: Resource mailcow: dockerized CPU 1 GHz RAM Minimum 6 GiB + 1 GiB swap (default config) Disk 20 GiB (without emails) System Type x86_64 We recommend using any distribution listed as supported by Docker CE (check https://docs.docker.com/install/ ). We test on CentOS 7, Debian 9/10 and Ubuntu 18.04/20.04. ClamAV and Solr can be greedy with RAM. You may disable them in mailcow.conf by settings SKIP_CLAMD=y and SKIP_SOLR=y . Info : We are aware that a pure MTA can run on 128 MiB RAM. mailcow is a full-grown and ready-to-use groupware with many extras making life easier. mailcow comes with a webserver, webmailer, ActiveSync (MS), antivirus, antispam, indexing (Solr), document scanner (Oletools), SQL (MariaDB), Cache (Redis), MDA, MTA, various web services etc. A single SOGo worker can acquire ~350 MiB RAM before it gets purged. The more ActiveSync connections you plan to use, the more RAM you will need. A default configuration spawns 20 workers. Usage examples \u00b6 A company with 15 phones (EAS enabled) and about 50 concurrent IMAP connections should plan 16 GiB RAM. 6 GiB RAM + 1 GiB swap are fine for most private installations while 8 GiB RAM are recommended for ~5 to 10 users. We can help to correctly plan your setup as part of our support. Firewall & Ports \u00b6 Please check if any of mailcow's standard ports are open and not in use by other applications: ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' # or: netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' Warning There are several problems with running mailcow on a firewalld/ufw enabled system. You should disable it (if possible) and move your ruleset to the DOCKER-USER chain, which is not cleared by a Docker service restart, instead. See this (blog.donnex.net) or this (unrouted.io) guide for information about how to use iptables-persistent with the DOCKER-USER chain. As mailcow runs dockerized, INPUT rules have no effect on restricting access to mailcow. Use the FORWARD chain instead. If this command returns any results please remove or stop the application running on that port. You may also adjust mailcows ports via the mailcow.conf configuration file. Default Ports \u00b6 If you have a firewall in front of mailcow, please make sure that these ports are open for incoming connections: Service Protocol Port Container Variable Postfix SMTP TCP 25 postfix-mailcow ${SMTP_PORT} Postfix SMTPS TCP 465 postfix-mailcow ${SMTPS_PORT} Postfix Submission TCP 587 postfix-mailcow ${SUBMISSION_PORT} Dovecot IMAP TCP 143 dovecot-mailcow ${IMAP_PORT} Dovecot IMAPS TCP 993 dovecot-mailcow ${IMAPS_PORT} Dovecot POP3 TCP 110 dovecot-mailcow ${POP_PORT} Dovecot POP3S TCP 995 dovecot-mailcow ${POPS_PORT} Dovecot ManageSieve TCP 4190 dovecot-mailcow ${SIEVE_PORT} HTTP(S) TCP 80/443 nginx-mailcow ${HTTP_PORT} / ${HTTPS_PORT} To bind a service to an IP address, you can prepend the IP like this: SMTP_PORT=1.2.3.4:25 Important : You cannot use IP:PORT bindings in HTTP_PORT and HTTPS_PORT. Please use HTTP_PORT=1234 and HTTP_BIND=1.2.3.4 instead. Important for Hetzner firewalls \u00b6 Quoting https://github.com/chermsen via https://github.com/mailcow/mailcow-dockerized/issues/497#issuecomment-469847380 (THANK YOU!): For all who are struggling with the Hetzner firewall: Port 53 unimportant for the firewall configuration in this case. According to the documentation unbound uses the port range 1024-65535 for outgoing requests. Since the Hetzner Robot Firewall is a static firewall (each incoming packet is checked isolated) - the following rules must be applied: For TCP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: tcp TCP flags: ack Action: Accept For UDP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: udp Action: Accept If you want to apply a more restrictive port range you have to change the config of unbound first (after installation): {mailcow-dockerized}/data/conf/unbound/unbound.conf: outgoing-port-avoid: 0-32767 Now the firewall rules can be adjusted as follows: [...] DST Port: 32768-65535 [...] Date and Time \u00b6 To ensure that you have the correct date and time setup on your system, please check the output of timedatectl status : $ timedatectl status Local time: Sat 2017-05-06 02:12:33 CEST Universal time: Sat 2017-05-06 00:12:33 UTC RTC time: Sat 2017-05-06 00:12:32 Time zone: Europe/Berlin (CEST, +0200) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: yes Last DST change: DST began at Sun 2017-03-26 01:59:59 CET Sun 2017-03-26 03:00:00 CEST Next DST change: DST ends (the clock jumps one hour backwards) at Sun 2017-10-29 02:59:59 CEST Sun 2017-10-29 02:00:00 CET The lines NTP enabled: yes and NTP synchronized: yes indicate whether you have NTP enabled and if it's synchronized. To enable NTP you need to run the command timedatectl set-ntp true . You also need to edit your /etc/systemd/timesyncd.conf : # vim /etc/systemd/timesyncd.conf [Time] Servers=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org Hetzner Cloud (and probably others) \u00b6 Check /etc/network/interfaces.d/50-cloud-init.cfg and change the IPv6 interface from eth0:0 to eth0: # Wrong: auto eth0:0 iface eth0:0 inet6 static # Right: auto eth0 iface eth0 inet6 static Reboot or restart the interface. You may want to disable cloud-init network changes. MTU \u00b6 Especially relevant for OpenStack users: Check your MTU and set it accordingly in docker-compose.yml. See 4.1 in our installation docs .","title":"Prepare your system"},{"location":"prerequisite-system/#minimum-system-resources","text":"OpenVZ, Virtuozzo and LXC are not supported . Please make sure that your system has at least the following resources: Resource mailcow: dockerized CPU 1 GHz RAM Minimum 6 GiB + 1 GiB swap (default config) Disk 20 GiB (without emails) System Type x86_64 We recommend using any distribution listed as supported by Docker CE (check https://docs.docker.com/install/ ). We test on CentOS 7, Debian 9/10 and Ubuntu 18.04/20.04. ClamAV and Solr can be greedy with RAM. You may disable them in mailcow.conf by settings SKIP_CLAMD=y and SKIP_SOLR=y . Info : We are aware that a pure MTA can run on 128 MiB RAM. mailcow is a full-grown and ready-to-use groupware with many extras making life easier. mailcow comes with a webserver, webmailer, ActiveSync (MS), antivirus, antispam, indexing (Solr), document scanner (Oletools), SQL (MariaDB), Cache (Redis), MDA, MTA, various web services etc. A single SOGo worker can acquire ~350 MiB RAM before it gets purged. The more ActiveSync connections you plan to use, the more RAM you will need. A default configuration spawns 20 workers.","title":"Minimum System Resources"},{"location":"prerequisite-system/#usage-examples","text":"A company with 15 phones (EAS enabled) and about 50 concurrent IMAP connections should plan 16 GiB RAM. 6 GiB RAM + 1 GiB swap are fine for most private installations while 8 GiB RAM are recommended for ~5 to 10 users. We can help to correctly plan your setup as part of our support.","title":"Usage examples"},{"location":"prerequisite-system/#firewall-ports","text":"Please check if any of mailcow's standard ports are open and not in use by other applications: ss -tlpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' # or: netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190|5222|5269|5443' Warning There are several problems with running mailcow on a firewalld/ufw enabled system. You should disable it (if possible) and move your ruleset to the DOCKER-USER chain, which is not cleared by a Docker service restart, instead. See this (blog.donnex.net) or this (unrouted.io) guide for information about how to use iptables-persistent with the DOCKER-USER chain. As mailcow runs dockerized, INPUT rules have no effect on restricting access to mailcow. Use the FORWARD chain instead. If this command returns any results please remove or stop the application running on that port. You may also adjust mailcows ports via the mailcow.conf configuration file.","title":"Firewall & Ports"},{"location":"prerequisite-system/#default-ports","text":"If you have a firewall in front of mailcow, please make sure that these ports are open for incoming connections: Service Protocol Port Container Variable Postfix SMTP TCP 25 postfix-mailcow ${SMTP_PORT} Postfix SMTPS TCP 465 postfix-mailcow ${SMTPS_PORT} Postfix Submission TCP 587 postfix-mailcow ${SUBMISSION_PORT} Dovecot IMAP TCP 143 dovecot-mailcow ${IMAP_PORT} Dovecot IMAPS TCP 993 dovecot-mailcow ${IMAPS_PORT} Dovecot POP3 TCP 110 dovecot-mailcow ${POP_PORT} Dovecot POP3S TCP 995 dovecot-mailcow ${POPS_PORT} Dovecot ManageSieve TCP 4190 dovecot-mailcow ${SIEVE_PORT} HTTP(S) TCP 80/443 nginx-mailcow ${HTTP_PORT} / ${HTTPS_PORT} To bind a service to an IP address, you can prepend the IP like this: SMTP_PORT=1.2.3.4:25 Important : You cannot use IP:PORT bindings in HTTP_PORT and HTTPS_PORT. Please use HTTP_PORT=1234 and HTTP_BIND=1.2.3.4 instead.","title":"Default Ports"},{"location":"prerequisite-system/#important-for-hetzner-firewalls","text":"Quoting https://github.com/chermsen via https://github.com/mailcow/mailcow-dockerized/issues/497#issuecomment-469847380 (THANK YOU!): For all who are struggling with the Hetzner firewall: Port 53 unimportant for the firewall configuration in this case. According to the documentation unbound uses the port range 1024-65535 for outgoing requests. Since the Hetzner Robot Firewall is a static firewall (each incoming packet is checked isolated) - the following rules must be applied: For TCP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: tcp TCP flags: ack Action: Accept For UDP SRC-IP: --- DST IP: --- SRC Port: --- DST Port: 1024-65535 Protocol: udp Action: Accept If you want to apply a more restrictive port range you have to change the config of unbound first (after installation): {mailcow-dockerized}/data/conf/unbound/unbound.conf: outgoing-port-avoid: 0-32767 Now the firewall rules can be adjusted as follows: [...] DST Port: 32768-65535 [...]","title":"Important for Hetzner firewalls"},{"location":"prerequisite-system/#date-and-time","text":"To ensure that you have the correct date and time setup on your system, please check the output of timedatectl status : $ timedatectl status Local time: Sat 2017-05-06 02:12:33 CEST Universal time: Sat 2017-05-06 00:12:33 UTC RTC time: Sat 2017-05-06 00:12:32 Time zone: Europe/Berlin (CEST, +0200) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: yes Last DST change: DST began at Sun 2017-03-26 01:59:59 CET Sun 2017-03-26 03:00:00 CEST Next DST change: DST ends (the clock jumps one hour backwards) at Sun 2017-10-29 02:59:59 CEST Sun 2017-10-29 02:00:00 CET The lines NTP enabled: yes and NTP synchronized: yes indicate whether you have NTP enabled and if it's synchronized. To enable NTP you need to run the command timedatectl set-ntp true . You also need to edit your /etc/systemd/timesyncd.conf : # vim /etc/systemd/timesyncd.conf [Time] Servers=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org","title":"Date and Time"},{"location":"prerequisite-system/#hetzner-cloud-and-probably-others","text":"Check /etc/network/interfaces.d/50-cloud-init.cfg and change the IPv6 interface from eth0:0 to eth0: # Wrong: auto eth0:0 iface eth0:0 inet6 static # Right: auto eth0 iface eth0 inet6 static Reboot or restart the interface. You may want to disable cloud-init network changes.","title":"Hetzner Cloud (and probably others)"},{"location":"prerequisite-system/#mtu","text":"Especially relevant for OpenStack users: Check your MTU and set it accordingly in docker-compose.yml. See 4.1 in our installation docs .","title":"MTU"},{"location":"restrictions_ip_accss/","text":"WIP Protocol restrictions and IP access \u00b6 Denied access will be shown to the user as failed login attempts. Protocol restrictions in Dovecot \u00b6 Protocol restrictions work by filtering the passdb query for IMAP and POP3 as well as reading the JSON value for %s_access where %s reflects the protocol seen by Dovecot. In the future we may use virtual colums in SQL to add an index on these values. Protocol restrictions in Postfix \u00b6 Filtering SMTP protocol access works by using a check_sasl_map in the smtpd_recipient_restrictions.","title":"Restrictions ip accss"},{"location":"restrictions_ip_accss/#protocol-restrictions-and-ip-access","text":"Denied access will be shown to the user as failed login attempts.","title":"Protocol restrictions and IP access"},{"location":"restrictions_ip_accss/#protocol-restrictions-in-dovecot","text":"Protocol restrictions work by filtering the passdb query for IMAP and POP3 as well as reading the JSON value for %s_access where %s reflects the protocol seen by Dovecot. In the future we may use virtual colums in SQL to add an index on these values.","title":"Protocol restrictions in Dovecot"},{"location":"restrictions_ip_accss/#protocol-restrictions-in-postfix","text":"Filtering SMTP protocol access works by using a check_sasl_map in the smtpd_recipient_restrictions.","title":"Protocol restrictions in Postfix"},{"location":"third_party-borgmatic/","text":"Borgmatic Backup \u00b6 Introduction \u00b6 Borgmatic is a great way to run backups on your Mailcow setup as it securely encrypts your data and is extremely easy to set up. Due to it's deduplication capabilities you can store a great number of backups without wasting large amounts of disk space. This allows you to run backups in very short intervals to ensure minimal data loss when the need arises to recover data from a backup. This document guides you through the process to enable continuous backups for mailcow with borgmatic. The borgmatic functionality is provided by the borgmatic Docker image by b3vis . Check out the README in that repository to find out about the other options (such as push notifications) that are available. This guide only covers the basics. Setting up borgmatic \u00b6 Create or amend docker-compose.override.yml \u00b6 In the mailcow-dockerized root folder create or edit docker-compose.override.yml and insert the following configuration: version : '2.1' services : borgmatic-mailcow : image : b3vis/borgmatic restart : always dns : ${IPV4_NETWORK:-172.22.1}.254 volumes : - vmail-vol-1:/mnt/source/vmail:ro - crypt-vol-1:/mnt/source/crypt:ro - mysql-socket-vol-1:/var/run/mysqld/:z - ./data/conf/borgmatic/etc:/etc/borgmatic.d:Z - ./data/conf/borgmatic/state:/root/.config/borg:Z - ./data/conf/borgmatic/ssh:/root/.ssh:Z environment : - TZ=${TZ} - BORG_PASSPHRASE=YouBetterPutSomethingRealGoodHere networks : mailcow-network : aliases : - borgmatic Ensure that you change the BORG_PASSPHRASE to a secure passphrase of your choosing. For security reasons we mount the maildir as read-only. If you later want to restore data you will need to remove the ro flag prior to restoring the data. This is described in the section on restoring backups. Create data/conf/borgmatic/etc/config.yaml \u00b6 Next, we need to create the borgmatic configuration. source mailcow.conf cat < data/conf/borgmatic/etc/config.yaml location: source_directories: - /mnt/source repositories: - user@rsync.net:mailcow remote_path: borg1 retention: keep_hourly: 24 keep_daily: 7 keep_weekly: 4 keep_monthly: 6 hooks: mysql_databases: - name: ${DBNAME} username: ${DBUSER} password: ${DBPASS} options: --default-character-set=utf8mb4 EOF Creating the file in this way ensures the correct MySQL credentials are pulled in from mailcow.conf . This file is a minimal example for using borgmatic with an account user on the cloud storage provider rsync.net for a repository called mailcow (see repositories setting). It will backup both the maildir and MySQL database, which is all you should need to restore your mailcow setup after an incident. The retention settings will keep one archive for each hour of the past 24 hours, one per day of the week, one per week of the month and one per month of the past half year. Check the borgmatic documentation on how to use other types of repositories or configuration options. If you choose to use a local filesystem as a backup destination make sure to mount it into the container. The container defines a volume called /mnt/borg-repository for this purpose. Note If you do not use rsync.net you can most likely drop the remote_path element from your config. Create a crontab \u00b6 Create a new text file in data/conf/borgmatic/etc/crontab.txt with the following content: 14 * * * * PATH=$PATH:/usr/bin /usr/bin/borgmatic --stats -v 0 2>&1 This file expects crontab syntax. The example shown here will trigger the backup to run every hour at 14 minutes past the hour and log some nice stats at the end. Place SSH keys in folder \u00b6 Place the SSH keys you intend to use for remote repository connections in data/conf/borgmatic/ssh . OpenSSH expects the usual id_rsa , id_ed25519 or similar to be in this directory. Ensure the file is chmod 600 and not world readable or OpenSSH will refuse to use the SSH key. Bring up the container \u00b6 For the next step we need the container to be up and running in a configured state. To do that run: docker-compose up -d Initialize the repository \u00b6 By now your borgmatic container is up and running, but the backups will currently fail due to the repository not being initialized. To initialize the repository run: docker-compose exec borgmatic-mailcow borgmatic init --encryption repokey-blake2 You will be asked you to authenticate the SSH host key of your remote repository server. See if it matches and confirm the prompt by entering yes . The repository will be initialized with the passphrase you set in the BORG_PASSPHRASE environment variable earlier. When using any of the repokey encryption methods the encryption key will be stored in the repository itself and not on the client, so there is no further action required in this regard. If you decide to use a keyfile instead of a repokey make sure you export the key and back it up separately. Check the Exporting Keys section for how to retrieve the key. Restart container \u00b6 Now that we finished configuring and initializing the repository restart the container to ensure it is in a defined state: docker-compose restart borgmatic-mailcow Restoring from a backup \u00b6 Restoring a backup assumes you are starting off with a fresh installation of mailcow, and you currently do not have any custom data in your maildir or your mailcow database. Restore maildir \u00b6 Warning Doing this will overwrite files in your maildir! Do not run this unless you actually intend to recover mail files from a backup. If you use SELinux in Enforcing mode If you are using mailcow on a host with SELinux in Enforcing mode you will have to temporarily disable it during extraction of the archive as the mailcow setup labels the vmail volume as private, belonging to the dovecot container exclusively. SELinux will (rightfully) prevent any other container, such as the borgmatic container, from writing to this volume. Before running a restore you must make the vmail volume writeable in docker-compose.override.yml by removing the ro flag from the volume. Then you can use the following command to restore the maildir from a backup: docker-compose exec borgmatic-mailcow borgmatic extract --path mnt/source --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives ) Restore MySQL \u00b6 Warning Running this command will delete and recreate the mailcow database! Do not run this unless you actually intend to recover the mailcow database from a backup. To restore the MySQL database from the latest archive use this command: docker-compose exec borgmatic-mailcow borgmatic restore --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives ) After restoring \u00b6 After restoring you need to restart mailcow. If you disabled SELinux enforcing mode now would be a good time to re-enable it. To restart mailcow use the follwing command: docker-compose down && docker-compose up -d If you use SELinux this will also trigger the re-labeling of all files in your vmail volume. Be patient, as this may take a while if you have lots of files. Useful commands \u00b6 Manual archiving run (with debugging output) \u00b6 docker-compose exec borgmatic-mailcow borgmatic -v 2 Listing all available archives \u00b6 docker-compose exec borgmatic-mailcow borgmatic list Break lock \u00b6 When borg is interrupted during an archiving run it will leave behind a stale lock that needs to be cleared before any new operations can be performed: docker-compose exec borgmatic-mailcow borg break-lock user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository. Now would be a good time to do a manual archiving run to ensure it can be successfully performed. Exporting keys \u00b6 When using any of the keyfile methods for encryption you MUST take care of backing up the key files yourself. The key files are generated when you initialize the repository. The repokey methods store the key file within the repository, so a manual backup isn't as essential. Note that in either case you also must have the passphrase to decrypt any archives. To fetch the keyfile run: docker-compose exec borgmatic-mailcow borg key export --paper user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository.","title":"Borgmatic Backup"},{"location":"third_party-borgmatic/#borgmatic-backup","text":"","title":"Borgmatic Backup"},{"location":"third_party-borgmatic/#introduction","text":"Borgmatic is a great way to run backups on your Mailcow setup as it securely encrypts your data and is extremely easy to set up. Due to it's deduplication capabilities you can store a great number of backups without wasting large amounts of disk space. This allows you to run backups in very short intervals to ensure minimal data loss when the need arises to recover data from a backup. This document guides you through the process to enable continuous backups for mailcow with borgmatic. The borgmatic functionality is provided by the borgmatic Docker image by b3vis . Check out the README in that repository to find out about the other options (such as push notifications) that are available. This guide only covers the basics.","title":"Introduction"},{"location":"third_party-borgmatic/#setting-up-borgmatic","text":"","title":"Setting up borgmatic"},{"location":"third_party-borgmatic/#create-or-amend-docker-composeoverrideyml","text":"In the mailcow-dockerized root folder create or edit docker-compose.override.yml and insert the following configuration: version : '2.1' services : borgmatic-mailcow : image : b3vis/borgmatic restart : always dns : ${IPV4_NETWORK:-172.22.1}.254 volumes : - vmail-vol-1:/mnt/source/vmail:ro - crypt-vol-1:/mnt/source/crypt:ro - mysql-socket-vol-1:/var/run/mysqld/:z - ./data/conf/borgmatic/etc:/etc/borgmatic.d:Z - ./data/conf/borgmatic/state:/root/.config/borg:Z - ./data/conf/borgmatic/ssh:/root/.ssh:Z environment : - TZ=${TZ} - BORG_PASSPHRASE=YouBetterPutSomethingRealGoodHere networks : mailcow-network : aliases : - borgmatic Ensure that you change the BORG_PASSPHRASE to a secure passphrase of your choosing. For security reasons we mount the maildir as read-only. If you later want to restore data you will need to remove the ro flag prior to restoring the data. This is described in the section on restoring backups.","title":"Create or amend docker-compose.override.yml"},{"location":"third_party-borgmatic/#create-dataconfborgmaticetcconfigyaml","text":"Next, we need to create the borgmatic configuration. source mailcow.conf cat < data/conf/borgmatic/etc/config.yaml location: source_directories: - /mnt/source repositories: - user@rsync.net:mailcow remote_path: borg1 retention: keep_hourly: 24 keep_daily: 7 keep_weekly: 4 keep_monthly: 6 hooks: mysql_databases: - name: ${DBNAME} username: ${DBUSER} password: ${DBPASS} options: --default-character-set=utf8mb4 EOF Creating the file in this way ensures the correct MySQL credentials are pulled in from mailcow.conf . This file is a minimal example for using borgmatic with an account user on the cloud storage provider rsync.net for a repository called mailcow (see repositories setting). It will backup both the maildir and MySQL database, which is all you should need to restore your mailcow setup after an incident. The retention settings will keep one archive for each hour of the past 24 hours, one per day of the week, one per week of the month and one per month of the past half year. Check the borgmatic documentation on how to use other types of repositories or configuration options. If you choose to use a local filesystem as a backup destination make sure to mount it into the container. The container defines a volume called /mnt/borg-repository for this purpose. Note If you do not use rsync.net you can most likely drop the remote_path element from your config.","title":"Create data/conf/borgmatic/etc/config.yaml"},{"location":"third_party-borgmatic/#create-a-crontab","text":"Create a new text file in data/conf/borgmatic/etc/crontab.txt with the following content: 14 * * * * PATH=$PATH:/usr/bin /usr/bin/borgmatic --stats -v 0 2>&1 This file expects crontab syntax. The example shown here will trigger the backup to run every hour at 14 minutes past the hour and log some nice stats at the end.","title":"Create a crontab"},{"location":"third_party-borgmatic/#place-ssh-keys-in-folder","text":"Place the SSH keys you intend to use for remote repository connections in data/conf/borgmatic/ssh . OpenSSH expects the usual id_rsa , id_ed25519 or similar to be in this directory. Ensure the file is chmod 600 and not world readable or OpenSSH will refuse to use the SSH key.","title":"Place SSH keys in folder"},{"location":"third_party-borgmatic/#bring-up-the-container","text":"For the next step we need the container to be up and running in a configured state. To do that run: docker-compose up -d","title":"Bring up the container"},{"location":"third_party-borgmatic/#initialize-the-repository","text":"By now your borgmatic container is up and running, but the backups will currently fail due to the repository not being initialized. To initialize the repository run: docker-compose exec borgmatic-mailcow borgmatic init --encryption repokey-blake2 You will be asked you to authenticate the SSH host key of your remote repository server. See if it matches and confirm the prompt by entering yes . The repository will be initialized with the passphrase you set in the BORG_PASSPHRASE environment variable earlier. When using any of the repokey encryption methods the encryption key will be stored in the repository itself and not on the client, so there is no further action required in this regard. If you decide to use a keyfile instead of a repokey make sure you export the key and back it up separately. Check the Exporting Keys section for how to retrieve the key.","title":"Initialize the repository"},{"location":"third_party-borgmatic/#restart-container","text":"Now that we finished configuring and initializing the repository restart the container to ensure it is in a defined state: docker-compose restart borgmatic-mailcow","title":"Restart container"},{"location":"third_party-borgmatic/#restoring-from-a-backup","text":"Restoring a backup assumes you are starting off with a fresh installation of mailcow, and you currently do not have any custom data in your maildir or your mailcow database.","title":"Restoring from a backup"},{"location":"third_party-borgmatic/#restore-maildir","text":"Warning Doing this will overwrite files in your maildir! Do not run this unless you actually intend to recover mail files from a backup. If you use SELinux in Enforcing mode If you are using mailcow on a host with SELinux in Enforcing mode you will have to temporarily disable it during extraction of the archive as the mailcow setup labels the vmail volume as private, belonging to the dovecot container exclusively. SELinux will (rightfully) prevent any other container, such as the borgmatic container, from writing to this volume. Before running a restore you must make the vmail volume writeable in docker-compose.override.yml by removing the ro flag from the volume. Then you can use the following command to restore the maildir from a backup: docker-compose exec borgmatic-mailcow borgmatic extract --path mnt/source --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives )","title":"Restore maildir"},{"location":"third_party-borgmatic/#restore-mysql","text":"Warning Running this command will delete and recreate the mailcow database! Do not run this unless you actually intend to recover the mailcow database from a backup. To restore the MySQL database from the latest archive use this command: docker-compose exec borgmatic-mailcow borgmatic restore --archive latest Alternatively you can specify any archive name from the list of archives (see Listing all available archives )","title":"Restore MySQL"},{"location":"third_party-borgmatic/#after-restoring","text":"After restoring you need to restart mailcow. If you disabled SELinux enforcing mode now would be a good time to re-enable it. To restart mailcow use the follwing command: docker-compose down && docker-compose up -d If you use SELinux this will also trigger the re-labeling of all files in your vmail volume. Be patient, as this may take a while if you have lots of files.","title":"After restoring"},{"location":"third_party-borgmatic/#useful-commands","text":"","title":"Useful commands"},{"location":"third_party-borgmatic/#manual-archiving-run-with-debugging-output","text":"docker-compose exec borgmatic-mailcow borgmatic -v 2","title":"Manual archiving run (with debugging output)"},{"location":"third_party-borgmatic/#listing-all-available-archives","text":"docker-compose exec borgmatic-mailcow borgmatic list","title":"Listing all available archives"},{"location":"third_party-borgmatic/#break-lock","text":"When borg is interrupted during an archiving run it will leave behind a stale lock that needs to be cleared before any new operations can be performed: docker-compose exec borgmatic-mailcow borg break-lock user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository. Now would be a good time to do a manual archiving run to ensure it can be successfully performed.","title":"Break lock"},{"location":"third_party-borgmatic/#exporting-keys","text":"When using any of the keyfile methods for encryption you MUST take care of backing up the key files yourself. The key files are generated when you initialize the repository. The repokey methods store the key file within the repository, so a manual backup isn't as essential. Note that in either case you also must have the passphrase to decrypt any archives. To fetch the keyfile run: docker-compose exec borgmatic-mailcow borg key export --paper user@rsync.net:mailcow Where user@rsync.net:mailcow is the URI to your repository.","title":"Exporting keys"},{"location":"third_party-exchange_onprem/","text":"Using Microsoft Exchange in a hybrid setup is possible with mailcow. With this setup you can add mailboxes on your mailcow and still use Exchange Online Protection . All mailboxes setup in Exchange will receive their mails as usual , while with the hybrid approach additional Mailboxes can be setup in mailcow without any further configuration. This setup becomes very handy if you have enabled the Office 365 security defaults and third party applications can no longer login into your mailboxes by any of the supported methods . Requirements \u00b6 The mx Record of your domain needs to point at the Exchange mail service. Log into your Admin center and look out for the dns settings of your domain to find your personalized gateway domain. It should look like this contoso-com.mail.protection.outlook.com . Contact your domain registrant to get further information on how to change mx record. The domain you want to have additional mailboxes for must be setup as internal relay domain in Exchange. Log in to your Exchange Admin Center Select the mail flow pane and click on accepted domains Select the domain and switch it from authorative to internal relay Set up the mailcow \u00b6 Your mailcow needs to relay all mails to your personalized Exchange Host. It is the same host address we already looked up for the mx Record. Add the domain to your mailcow Add your personalized Exchange Host address as relayhost Add your personalized Exchange Host address as forwarding host to unconditionally accepted all relayed mails from Exchange. (Admin > Configuration & Details > Configuration Dropdown > Forwarding Hosts) Go to the domain settings and select the newly added host on the Sender-dependent transports dropdown. Enable relaying by ticking the Relay this domain , Relay all recipients and the Relay non-existing mailboxes only. checkboxes Info From now on your mailcow will accept all mails relayed from Exchange. The inbound filtering and so the neural learning of your cow will no longer work . Because all mails are routed through Exchange the filtering process is handled there . Set up Connectors in Exchange \u00b6 All mail traffic now goes through Exchange. At this point the Exchange Online Protection already filters all incoming and outgoing mails. Now we need to set up two connectors to relay incoming mails from our Exchange Service to the mailcow and another one to allow mails relayed from the mailcow to our exchange service. You can follow the official guide from Microsoft . Warning For the connector that handles mails from your mailcow to Exchange Microsoft offers two ways of authenticating it. The recommended way is to use a tls certificate configured with a subject name that matches an accepted domain in Exchange. Otherwise you need to choose authentication with the static ip address of your mailcow. Validating \u00b6 The easiest way to validate the hybrid setup is by sending a mail from the internet to a mailbox that only exists on the mailcow and vice versa. Common Issues \u00b6 The connector validation from Exchange to your mailcow failed with 550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipient test@contoso.com not found by SMTP address lookup Possible Solution: Your domain is not set up as internal relay . Exchange therefore cannot find the recipient Mails sent from the mailcow to a mailbox in the internet cannot be sent. Non Delivery Report with error 550 5.7.64 TenantAttribution; Relay Access Denied Possible Solution: The authentication method failed. Make sure the certificate subject matches an accepted domain in Exchange. Try authenticating by static ip instead. Microsoft Guide for the connector setup and additional requirements: https://docs.microsoft.com/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail#prerequisites-for-your-on-premises-email-environment","title":"Exchange Hybrid Setup"},{"location":"third_party-exchange_onprem/#requirements","text":"The mx Record of your domain needs to point at the Exchange mail service. Log into your Admin center and look out for the dns settings of your domain to find your personalized gateway domain. It should look like this contoso-com.mail.protection.outlook.com . Contact your domain registrant to get further information on how to change mx record. The domain you want to have additional mailboxes for must be setup as internal relay domain in Exchange. Log in to your Exchange Admin Center Select the mail flow pane and click on accepted domains Select the domain and switch it from authorative to internal relay","title":"Requirements"},{"location":"third_party-exchange_onprem/#set-up-the-mailcow","text":"Your mailcow needs to relay all mails to your personalized Exchange Host. It is the same host address we already looked up for the mx Record. Add the domain to your mailcow Add your personalized Exchange Host address as relayhost Add your personalized Exchange Host address as forwarding host to unconditionally accepted all relayed mails from Exchange. (Admin > Configuration & Details > Configuration Dropdown > Forwarding Hosts) Go to the domain settings and select the newly added host on the Sender-dependent transports dropdown. Enable relaying by ticking the Relay this domain , Relay all recipients and the Relay non-existing mailboxes only. checkboxes Info From now on your mailcow will accept all mails relayed from Exchange. The inbound filtering and so the neural learning of your cow will no longer work . Because all mails are routed through Exchange the filtering process is handled there .","title":"Set up the mailcow"},{"location":"third_party-exchange_onprem/#set-up-connectors-in-exchange","text":"All mail traffic now goes through Exchange. At this point the Exchange Online Protection already filters all incoming and outgoing mails. Now we need to set up two connectors to relay incoming mails from our Exchange Service to the mailcow and another one to allow mails relayed from the mailcow to our exchange service. You can follow the official guide from Microsoft . Warning For the connector that handles mails from your mailcow to Exchange Microsoft offers two ways of authenticating it. The recommended way is to use a tls certificate configured with a subject name that matches an accepted domain in Exchange. Otherwise you need to choose authentication with the static ip address of your mailcow.","title":"Set up Connectors in Exchange"},{"location":"third_party-exchange_onprem/#validating","text":"The easiest way to validate the hybrid setup is by sending a mail from the internet to a mailbox that only exists on the mailcow and vice versa.","title":"Validating"},{"location":"third_party-exchange_onprem/#common-issues","text":"The connector validation from Exchange to your mailcow failed with 550 5.1.10 RESOLVER.ADR.RecipientNotFound; Recipient test@contoso.com not found by SMTP address lookup Possible Solution: Your domain is not set up as internal relay . Exchange therefore cannot find the recipient Mails sent from the mailcow to a mailbox in the internet cannot be sent. Non Delivery Report with error 550 5.7.64 TenantAttribution; Relay Access Denied Possible Solution: The authentication method failed. Make sure the certificate subject matches an accepted domain in Exchange. Try authenticating by static ip instead. Microsoft Guide for the connector setup and additional requirements: https://docs.microsoft.com/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail#prerequisites-for-your-on-premises-email-environment","title":"Common Issues"},{"location":"third_party-gitea/","text":"With Gitea' ability to authenticate over SMTP it is trivial to integrate it with mailcow. Few changes are needed: 1. Open docker-compose.override.yml and add gitea: version: '2.1' services: gitea-mailcow: image: gitea/gitea:1 volumes: - ./data/gitea:/data networks: mailcow-network: aliases: - gitea ports: - \"${GITEA_SSH_PORT:-127.0.0.1:4000}:22\" 2. Create data/conf/nginx/site.gitea.custom , add: location /gitea/ { proxy_pass http://gitea:3000/; } 3. Open mailcow.conf and define the binding you want gitea to use for SSH. Example: GITEA_SSH_PORT=127.0.0.1:4000 5. Run docker-compose up -d to bring up the gitea container and run docker-compose restart nginx-mailcow afterwards. 6. If you forced mailcow to https, execute step 9 and restart gitea with docker-compose restart gitea-mailcow . Go head with step 7 (Remember to use https instead of http, https://mx.example.org/gitea/ 7. Open http://${MAILCOW_HOSTNAME}/gitea/ , for example http://mx.example.org/gitea/ . For database details set mysql as database host. Use the value of DBNAME found in mailcow.conf as database name, DBUSER as database user and DBPASS as database password. 8. Once the installation is complete, login as admin and set \"settings\" -> \"authorization\" -> \"enable SMTP\". SMTP Host should be postfix with port 587 , set Skip TLS Verify as we are using an unlisted SAN (\"postfix\" is most likely not part of your certificate). 9. Create data/gitea/gitea/conf/app.ini and set following values. You can consult gitea cheat sheet for their meaning and other possible values. [server] SSH_LISTEN_PORT = 22 # For GITEA_SSH_PORT=127.0.0.1:4000 in mailcow.conf, set: SSH_DOMAIN = 127.0.0.1 SSH_PORT = 4000 # For MAILCOW_HOSTNAME=mx.example.org in mailcow.conf (and default ports for HTTPS), set: ROOT_URL = https://mx.example.org/gitea/ 10. Restart gitea with docker-compose restart gitea-mailcow . Your users should be able to login with mailcow managed accounts.","title":"Gitea"},{"location":"third_party-gogs/","text":"With Gogs' ability to authenticate over SMTP it is trivial to integrate it with mailcow. Few changes are needed: 1. Open docker-compose.override.yml and add Gogs: version: '2.1' services: gogs-mailcow: image: gogs/gogs volumes: - ./data/gogs:/data networks: mailcow-network: aliases: - gogs ports: - \"${GOGS_SSH_PORT:-127.0.0.1:4000}:22\" 2. Create data/conf/nginx/site.gogs.custom , add: location /gogs/ { proxy_pass http://gogs:3000/; } 3. Open mailcow.conf and define the binding you want Gogs to use for SSH. Example: GOGS_SSH_PORT=127.0.0.1:4000 5. Run docker-compose up -d to bring up the Gogs container and run docker-compose restart nginx-mailcow afterwards. 6. Open http://${MAILCOW_HOSTNAME}/gogs/ , for example http://mx.example.org/gogs/ . For database details set mysql as database host. Use the value of DBNAME found in mailcow.conf as database name, DBUSER as database user and DBPASS as database password. 7. Once the installation is complete, login as admin and set \"settings\" -> \"authorization\" -> \"enable SMTP\". SMTP Host should be postfix with port 587 , set Skip TLS Verify as we are using an unlisted SAN (\"postfix\" is most likely not part of your certificate). 8. Create data/gogs/gogs/conf/app.ini and set following values. You can consult Gogs cheat sheet for their meaning and other possible values. [server] SSH_LISTEN_PORT = 22 # For GOGS_SSH_PORT=127.0.0.1:4000 in mailcow.conf, set: SSH_DOMAIN = 127.0.0.1 SSH_PORT = 4000 # For MAILCOW_HOSTNAME=mx.example.org in mailcow.conf (and default ports for HTTPS), set: ROOT_URL = https://mx.example.org/gogs/ 9. Restart Gogs with docker-compose restart gogs-mailcow . Your users should be able to login with mailcow managed accounts.","title":"Gogs"},{"location":"third_party-mailman3/","text":"Installing mailcow and Mailman 3 based on dockerized versions \u00b6 Info This guide is a copy from dockerized-mailcow-mailman . Please post issues, questions and improvements in the issue tracker there. Warning mailcow is not responsible for any data loss, hardware damage or broken keyboards. This guide comes without any warranty. Make backups before starting, 'coze: No backup no pity! Introduction \u00b6 This guide aims to install and configure mailcow-dockerized with docker-mailman and to provide some useful scripts. An essential condition is, to preserve mailcow and Mailman in their own installations for independent updates. There are some guides and projects on the internet, but they are not up to date and/or incomplete in documentation or configuration. This guide is based on the work of: mailcow-mailman3-dockerized by Shadowghost mailman-mailcow-integration After finishing this guide, mailcow-dockerized and docker-mailman will run and Apache as a reverse proxy will serve the web frontends. The operating system used is an Ubuntu 20.04 LTS . Installation \u00b6 This guide is based on different steps: DNS setup Install Apache as a reverse proxy Obtain SSL certificates with Let's Encrypt Install mailcow with Mailman integration Install Mailman \ud83c\udfc3 Run DNS setup \u00b6 Most of the configuration is covered by mailcow s DNS setup . After finishing this setup add another subdomain for Mailman , e.g. lists.example.org that points to the same server: # Name Type Value lists IN A 1.2.3.4 lists IN AAAA dead:beef Install Apache as a reverse proxy \u00b6 Install Apache , e.g. with this guide from Digital Ocean : How To Install the Apache Web Server on Ubuntu 20.04 . Activate certain Apache modules (as root or sudo ): a2enmod rewrite proxy proxy_http headers ssl wsgi proxy_uwsgi http2 Maybe you have to install further packages to get these modules. This PPA by Ond\u0159ej Sur\u00fd may help you. vHost configuration \u00b6 Copy the mailcow.conf and the mailman.conf in the Apache conf folder sites-available (e.g. under /etc/apache2/sites-available ). Change in mailcow.conf : - MAILCOW_HOSTNAME to your MAILCOW_HOSTNAME Change in mailman.conf : - MAILMAN_DOMAIN to your Mailman domain (e.g. lists.example.org ) Don't activate the configuration, as the ssl certificates and directories are missing yet. Obtain SSL certificates with Let's Encrypt \u00b6 Check if your DNS config is available over the internet and points to the right IP addresses, e.g. with MXToolBox : https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILMAN_DOMAIN https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILMAN_DOMAIN Install certbot (as root or sudo ): apt install certbot Get the desired certificates (as root or sudo ): certbot certonly -d mailcow_HOSTNAME certbot certonly -d MAILMAN_DOMAIN Install mailcow with Mailman integration \u00b6 Install mailcow \u00b6 Follow the mailcow installation . Omit step 5 and do not pull and up with docker-compose ! Configure mailcow \u00b6 This is also Step 4 in the official mailcow installation ( nano mailcow.conf ). So change to your needs and alter the following variables: HTTP_PORT=18080 # don't use 8080 as mailman needs it HTTP_BIND=127.0.0.1 # HTTPS_PORT=18443 # you may use 8443 HTTPS_BIND=127.0.0.1 # SKIP_LETS_ENCRYPT=y # reverse proxy will do the SSL termination SNAT_TO_SOURCE=1.2.3.4 # change this to your IPv4 SNAT6_TO_SOURCE=dead:beef # change this to your global IPv6 Add Mailman integration \u00b6 Create the file /opt/mailcow-dockerized/docker-compose.override.yml (e.g. with nano ) and add the following lines: version: '2.1' services: postfix-mailcow: volumes: - /opt/mailman:/opt/mailman networks: - docker-mailman_mailman networks: docker-mailman_mailman: external: true The additional volume is used by Mailman to generate additional config files for mailcow postfix . The external network is build and used by Mailman . mailcow needs it to deliver incoming list mails to Mailman . Create the file /opt/mailcow-dockerized/data/conf/postfix/extra.cf (e.g. with nano ) and add the following lines: # mailman recipient_delimiter = + unknown_local_recipient_reject_code = 550 owner_request_special = no local_recipient_maps = regexp:/opt/mailman/core/var/data/postfix_lmtp, proxy:unix:passwd.byname, $alias_maps virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre, pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf, regexp:/opt/mailman/core/var/data/postfix_domains relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp As we overwrite mailcow postfix configuration here, this step may break your normal mail transports. Check the original configuration files if anything changed. SSL certificates \u00b6 As we proxying mailcow , we need to copy the SSL certificates into the mailcow file structure. This task will do the script renew-ssl.sh for us: Copy the file to /opt/mailcow-dockerized Change mailcow_HOSTNAME to your mailcow hostname Make it executable ( chmod a+x renew-ssl.sh ) Do not run it yet, as we first need Mailman You have to create a cronjob , so that new certificates will be copied. Execute as root or sudo : crontab -e To run the script every day at 5am, add: 0 5 * * * /opt/mailcow-dockerized/renew-ssl.sh Install Mailman \u00b6 Basicly follow the instructions at docker-mailman . As they are a lot, here is in a nuthshell what to do: As root or sudo : cd /opt mkdir -p mailman/core mkdir -p mailman/web git clone https://github.com/maxking/docker-mailman cd docker-mailman Configure Mailman \u00b6 Create a long key for Hyperkitty , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as HYPERKITTY_KEY. Create a long password for the database, e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this password for a moment as DBPASS. Create a long key for Django , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as DJANGO_KEY. Create the file /opt/docker-mailman/docker-compose.override.yaml and replace HYPERKITTY_KEY , DBPASS and DJANGO_KEY with the generated values: version: '2' services: mailman-core: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - MTA=postfix restart: always networks: - mailman mailman-web: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - SECRET_KEY=DJANGO_KEY - SERVE_FROM_DOMAIN=MAILMAN_DOMAIN # e.g. lists.example.org - MAILMAN_ADMIN_USER=admin # the admin user - MAILMAN_ADMIN_EMAIL=admin@example.org # the admin mail address - UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static restart: always database: environment: - POSTGRES_PASSWORD=DBPASS restart: always At mailman-web fill in correct values for SERVE_FROM_DOMAIN (e.g. lists.example.org ), MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL . You need the admin credentials to log into the web interface ( Pistorius ). For setting the password for the first time use the Forgot password function in the web interface. About other configuration options read Mailman-web and Mailman-core documentation. Configure Mailman core and Mailman web \u00b6 Create the file /opt/mailman/core/mailman-extra.cfg with the following content. mailman@example.org should be pointing to a valid mail box or redirection. [mailman] default_language: de site_owner: mailman@example.org Create the file /opt/mailman/web/settings_local.py with the following content. mailman@example.org should be pointing to a valid mail box or redirection. # locale LANGUAGE_CODE = 'de-de' # disable social authentication SOCIALACCOUNT_PROVIDERS = {} # change it DEFAULT_FROM_EMAIL = 'mailman@example.org' DEBUG = False You can change LANGUAGE_CODE and SOCIALACCOUNT_PROVIDERS to your needs. At the moment SOCIALACCOUNT_PROVIDERS has no effect, see issue #2 . \ud83c\udfc3 Run \u00b6 Run (as root or sudo ) a2ensite mailcow.conf a2ensite mailman.conf systemctl restart apache2 cd /opt/docker-mailman docker-compose pull docker-compose up -d cd /opt/mailcow-dockerized/ docker-compose pull ./renew-ssl.sh Wait a few minutes! The containers have to create there databases and config files. This can last up to 1 minute and more. Remarks \u00b6 New lists aren't recognized by postfix instantly \u00b6 When you create a new list and try to immediately send an e-mail, postfix responses with User doesn't exist , because postfix won't deliver it to Mailman yet. The configuration at /opt/mailman/core/var/data/postfix_lmtp is not instantly updated. If you need the list instantly, restart postifx manually: cd /opt/mailcow-dockerized docker-compose restart postfix-mailcow Update \u00b6 mailcow has it's own update script in `/opt/mailcow-dockerized/update.sh', see the docs . For Mailman just fetch the newest version from the github repository . Backup \u00b6 mailcow has an own backup script. Read the docs for further informations. Mailman won't state backup instructions in the README.md. In the gitbucket of pgollor is a script that may be helpful. ToDo \u00b6 install script \u00b6 Write a script like in mailman-mailcow-integration/mailman-install.sh as many of the steps are automatable. Ask for all the configuration variables and create passwords and keys. Do a (semi-)automatic installation. Have fun!","title":"Mailman3"},{"location":"third_party-mailman3/#installing-mailcow-and-mailman-3-based-on-dockerized-versions","text":"Info This guide is a copy from dockerized-mailcow-mailman . Please post issues, questions and improvements in the issue tracker there. Warning mailcow is not responsible for any data loss, hardware damage or broken keyboards. This guide comes without any warranty. Make backups before starting, 'coze: No backup no pity!","title":"Installing mailcow and Mailman 3 based on dockerized versions"},{"location":"third_party-mailman3/#introduction","text":"This guide aims to install and configure mailcow-dockerized with docker-mailman and to provide some useful scripts. An essential condition is, to preserve mailcow and Mailman in their own installations for independent updates. There are some guides and projects on the internet, but they are not up to date and/or incomplete in documentation or configuration. This guide is based on the work of: mailcow-mailman3-dockerized by Shadowghost mailman-mailcow-integration After finishing this guide, mailcow-dockerized and docker-mailman will run and Apache as a reverse proxy will serve the web frontends. The operating system used is an Ubuntu 20.04 LTS .","title":"Introduction"},{"location":"third_party-mailman3/#installation","text":"This guide is based on different steps: DNS setup Install Apache as a reverse proxy Obtain SSL certificates with Let's Encrypt Install mailcow with Mailman integration Install Mailman \ud83c\udfc3 Run","title":"Installation"},{"location":"third_party-mailman3/#dns-setup","text":"Most of the configuration is covered by mailcow s DNS setup . After finishing this setup add another subdomain for Mailman , e.g. lists.example.org that points to the same server: # Name Type Value lists IN A 1.2.3.4 lists IN AAAA dead:beef","title":"DNS setup"},{"location":"third_party-mailman3/#install-apache-as-a-reverse-proxy","text":"Install Apache , e.g. with this guide from Digital Ocean : How To Install the Apache Web Server on Ubuntu 20.04 . Activate certain Apache modules (as root or sudo ): a2enmod rewrite proxy proxy_http headers ssl wsgi proxy_uwsgi http2 Maybe you have to install further packages to get these modules. This PPA by Ond\u0159ej Sur\u00fd may help you.","title":"Install Apache as a reverse proxy"},{"location":"third_party-mailman3/#vhost-configuration","text":"Copy the mailcow.conf and the mailman.conf in the Apache conf folder sites-available (e.g. under /etc/apache2/sites-available ). Change in mailcow.conf : - MAILCOW_HOSTNAME to your MAILCOW_HOSTNAME Change in mailman.conf : - MAILMAN_DOMAIN to your Mailman domain (e.g. lists.example.org ) Don't activate the configuration, as the ssl certificates and directories are missing yet.","title":"vHost configuration"},{"location":"third_party-mailman3/#obtain-ssl-certificates-with-lets-encrypt","text":"Check if your DNS config is available over the internet and points to the right IP addresses, e.g. with MXToolBox : https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILCOW_HOSTNAME https://mxtoolbox.com/SuperTool.aspx?action=a%3aMAILMAN_DOMAIN https://mxtoolbox.com/SuperTool.aspx?action=aaaa%3aMAILMAN_DOMAIN Install certbot (as root or sudo ): apt install certbot Get the desired certificates (as root or sudo ): certbot certonly -d mailcow_HOSTNAME certbot certonly -d MAILMAN_DOMAIN","title":"Obtain SSL certificates with Let's Encrypt"},{"location":"third_party-mailman3/#install-mailcow-with-mailman-integration","text":"","title":"Install mailcow with Mailman integration"},{"location":"third_party-mailman3/#install-mailcow","text":"Follow the mailcow installation . Omit step 5 and do not pull and up with docker-compose !","title":"Install mailcow"},{"location":"third_party-mailman3/#configure-mailcow","text":"This is also Step 4 in the official mailcow installation ( nano mailcow.conf ). So change to your needs and alter the following variables: HTTP_PORT=18080 # don't use 8080 as mailman needs it HTTP_BIND=127.0.0.1 # HTTPS_PORT=18443 # you may use 8443 HTTPS_BIND=127.0.0.1 # SKIP_LETS_ENCRYPT=y # reverse proxy will do the SSL termination SNAT_TO_SOURCE=1.2.3.4 # change this to your IPv4 SNAT6_TO_SOURCE=dead:beef # change this to your global IPv6","title":"Configure mailcow"},{"location":"third_party-mailman3/#add-mailman-integration","text":"Create the file /opt/mailcow-dockerized/docker-compose.override.yml (e.g. with nano ) and add the following lines: version: '2.1' services: postfix-mailcow: volumes: - /opt/mailman:/opt/mailman networks: - docker-mailman_mailman networks: docker-mailman_mailman: external: true The additional volume is used by Mailman to generate additional config files for mailcow postfix . The external network is build and used by Mailman . mailcow needs it to deliver incoming list mails to Mailman . Create the file /opt/mailcow-dockerized/data/conf/postfix/extra.cf (e.g. with nano ) and add the following lines: # mailman recipient_delimiter = + unknown_local_recipient_reject_code = 550 owner_request_special = no local_recipient_maps = regexp:/opt/mailman/core/var/data/postfix_lmtp, proxy:unix:passwd.byname, $alias_maps virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre, pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf, regexp:/opt/mailman/core/var/data/postfix_domains relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf, regexp:/opt/mailman/core/var/data/postfix_lmtp As we overwrite mailcow postfix configuration here, this step may break your normal mail transports. Check the original configuration files if anything changed.","title":"Add Mailman integration"},{"location":"third_party-mailman3/#ssl-certificates","text":"As we proxying mailcow , we need to copy the SSL certificates into the mailcow file structure. This task will do the script renew-ssl.sh for us: Copy the file to /opt/mailcow-dockerized Change mailcow_HOSTNAME to your mailcow hostname Make it executable ( chmod a+x renew-ssl.sh ) Do not run it yet, as we first need Mailman You have to create a cronjob , so that new certificates will be copied. Execute as root or sudo : crontab -e To run the script every day at 5am, add: 0 5 * * * /opt/mailcow-dockerized/renew-ssl.sh","title":"SSL certificates"},{"location":"third_party-mailman3/#install-mailman","text":"Basicly follow the instructions at docker-mailman . As they are a lot, here is in a nuthshell what to do: As root or sudo : cd /opt mkdir -p mailman/core mkdir -p mailman/web git clone https://github.com/maxking/docker-mailman cd docker-mailman","title":"Install Mailman"},{"location":"third_party-mailman3/#configure-mailman","text":"Create a long key for Hyperkitty , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as HYPERKITTY_KEY. Create a long password for the database, e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this password for a moment as DBPASS. Create a long key for Django , e.g. with the linux command cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c30; echo . Save this key for a moment as DJANGO_KEY. Create the file /opt/docker-mailman/docker-compose.override.yaml and replace HYPERKITTY_KEY , DBPASS and DJANGO_KEY with the generated values: version: '2' services: mailman-core: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - MTA=postfix restart: always networks: - mailman mailman-web: environment: - DATABASE_URL=postgres://mailman:DBPASS@database/mailmandb - HYPERKITTY_API_KEY=HYPERKITTY_KEY - TZ=Europe/Berlin - SECRET_KEY=DJANGO_KEY - SERVE_FROM_DOMAIN=MAILMAN_DOMAIN # e.g. lists.example.org - MAILMAN_ADMIN_USER=admin # the admin user - MAILMAN_ADMIN_EMAIL=admin@example.org # the admin mail address - UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static restart: always database: environment: - POSTGRES_PASSWORD=DBPASS restart: always At mailman-web fill in correct values for SERVE_FROM_DOMAIN (e.g. lists.example.org ), MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL . You need the admin credentials to log into the web interface ( Pistorius ). For setting the password for the first time use the Forgot password function in the web interface. About other configuration options read Mailman-web and Mailman-core documentation.","title":"Configure Mailman"},{"location":"third_party-mailman3/#configure-mailman-core-and-mailman-web","text":"Create the file /opt/mailman/core/mailman-extra.cfg with the following content. mailman@example.org should be pointing to a valid mail box or redirection. [mailman] default_language: de site_owner: mailman@example.org Create the file /opt/mailman/web/settings_local.py with the following content. mailman@example.org should be pointing to a valid mail box or redirection. # locale LANGUAGE_CODE = 'de-de' # disable social authentication SOCIALACCOUNT_PROVIDERS = {} # change it DEFAULT_FROM_EMAIL = 'mailman@example.org' DEBUG = False You can change LANGUAGE_CODE and SOCIALACCOUNT_PROVIDERS to your needs. At the moment SOCIALACCOUNT_PROVIDERS has no effect, see issue #2 .","title":"Configure Mailman core and Mailman web"},{"location":"third_party-mailman3/#run","text":"Run (as root or sudo ) a2ensite mailcow.conf a2ensite mailman.conf systemctl restart apache2 cd /opt/docker-mailman docker-compose pull docker-compose up -d cd /opt/mailcow-dockerized/ docker-compose pull ./renew-ssl.sh Wait a few minutes! The containers have to create there databases and config files. This can last up to 1 minute and more.","title":"\ud83c\udfc3 Run"},{"location":"third_party-mailman3/#remarks","text":"","title":"Remarks"},{"location":"third_party-mailman3/#new-lists-arent-recognized-by-postfix-instantly","text":"When you create a new list and try to immediately send an e-mail, postfix responses with User doesn't exist , because postfix won't deliver it to Mailman yet. The configuration at /opt/mailman/core/var/data/postfix_lmtp is not instantly updated. If you need the list instantly, restart postifx manually: cd /opt/mailcow-dockerized docker-compose restart postfix-mailcow","title":"New lists aren't recognized by postfix instantly"},{"location":"third_party-mailman3/#update","text":"mailcow has it's own update script in `/opt/mailcow-dockerized/update.sh', see the docs . For Mailman just fetch the newest version from the github repository .","title":"Update"},{"location":"third_party-mailman3/#backup","text":"mailcow has an own backup script. Read the docs for further informations. Mailman won't state backup instructions in the README.md. In the gitbucket of pgollor is a script that may be helpful.","title":"Backup"},{"location":"third_party-mailman3/#todo","text":"","title":"ToDo"},{"location":"third_party-mailman3/#install-script","text":"Write a script like in mailman-mailcow-integration/mailman-install.sh as many of the steps are automatable. Ask for all the configuration variables and create passwords and keys. Do a (semi-)automatic installation. Have fun!","title":"install script"},{"location":"third_party-mailpiler_integration/","text":"This is a simple integration of mailcow aliases and the mailbox name into mailpiler when using IMAP authentication. Disclaimer : This is not officially maintained nor supported by the mailcow project nor its contributors. No warranty or support is being provided, however you're free to open issues on GitHub for filing a bug or provide further ideas. GitHub repo can be found here . Info Support for domain wildcards were implemented in Piler 1.3.10 which was released on 03.01.2021. Prior versions basically do work, but after logging in you won't see emails sent from or to the domain alias. (e.g. when @example.com is an alias for admin@example.com ) The problem to solve \u00b6 mailpiler offers the authentication based on IMAP, for example: $config['ENABLE_IMAP_AUTH'] = 1; $config['IMAP_HOST'] = 'mail.example.com'; $config['IMAP_PORT'] = 993; $config['IMAP_SSL'] = true; So when you log in using patrik@example.com , you will only see delivered emails sent from or to this specific email address. When additional aliases are defined in mailcow, like team@example.com , you won't see emails sent to or from this email address even the fact you're a recipient of mails sent to this alias address. By hooking into the authentication process of mailpiler, we are able to get required data via the mailcow API during login. This fires API requests to the mailcow API (requiring read-only API access) to read out the aliases your email address participates and also the \"Name\" of the mailbox specified to display it on the top-right of mailpiler after login. Permitted email addresses can be seen in the mailpiler settings top-right after logging in. Info This is only pulled once during the authentication process. The authorized aliases and the realname are valid for the whole duration of the user session as mailpiler sets them in the session data. If user is removed from specific alias, this will only take effect after next login. The solution \u00b6 Note: File paths might vary depending on your setup. Requirements \u00b6 A working mailcow instance A working mailpiler instance ( You can find an installation guide here , check supported versions here ) An mailcow API key (read-only works just fine): Configuration & Details - Access - Read-Only Access . Don't forget to allow API access from your mailpiler IP. Warning As mailpiler authenticates against mailcow, our IMAP server, failed logins of users or bots might trigger a block for your mailpiler instance. Therefore you might want to consider whitelisting the IP address of the mailpiler instance within mailcow: Configuration & Details - Configuration - Fail2ban parameters - Whitelisted networks/hosts . Setup \u00b6 Set the custom query function of mailpiler and append this to /usr/local/etc/piler/config-site.php : $config['MAILCOW_API_KEY'] = 'YOUR_READONLY_API_KEY'; $config['MAILCOW_SET_REALNAME'] = true; // when not specified, then default is false $config['CUSTOM_EMAIL_QUERY_FUNCTION'] = 'query_mailcow_for_email_access'; include('auth-mailcow.php'); You can also change the mailcow hostname, if required: $config['MAILCOW_HOST'] = 'mail.domain.tld'; // defaults to $config['IMAP_HOST'] Download the PHP file with the functions from the GitHub repo : curl -o /usr/local/etc/piler/auth-mailcow.php https://raw.githubusercontent.com/patschi/mailpiler-mailcow-integration/master/auth-mailcow.php Done! Make sure to re-login with your IMAP credentials for changes to take effect. If it doesn't work, most likely something's wrong with the API query itself. Consider debugging by sending manual API requests to the API. (Tip: Open https://mail.domain.tld/api on your instance)","title":"Mailpiler Integration"},{"location":"third_party-mailpiler_integration/#the-problem-to-solve","text":"mailpiler offers the authentication based on IMAP, for example: $config['ENABLE_IMAP_AUTH'] = 1; $config['IMAP_HOST'] = 'mail.example.com'; $config['IMAP_PORT'] = 993; $config['IMAP_SSL'] = true; So when you log in using patrik@example.com , you will only see delivered emails sent from or to this specific email address. When additional aliases are defined in mailcow, like team@example.com , you won't see emails sent to or from this email address even the fact you're a recipient of mails sent to this alias address. By hooking into the authentication process of mailpiler, we are able to get required data via the mailcow API during login. This fires API requests to the mailcow API (requiring read-only API access) to read out the aliases your email address participates and also the \"Name\" of the mailbox specified to display it on the top-right of mailpiler after login. Permitted email addresses can be seen in the mailpiler settings top-right after logging in. Info This is only pulled once during the authentication process. The authorized aliases and the realname are valid for the whole duration of the user session as mailpiler sets them in the session data. If user is removed from specific alias, this will only take effect after next login.","title":"The problem to solve"},{"location":"third_party-mailpiler_integration/#the-solution","text":"Note: File paths might vary depending on your setup.","title":"The solution"},{"location":"third_party-mailpiler_integration/#requirements","text":"A working mailcow instance A working mailpiler instance ( You can find an installation guide here , check supported versions here ) An mailcow API key (read-only works just fine): Configuration & Details - Access - Read-Only Access . Don't forget to allow API access from your mailpiler IP. Warning As mailpiler authenticates against mailcow, our IMAP server, failed logins of users or bots might trigger a block for your mailpiler instance. Therefore you might want to consider whitelisting the IP address of the mailpiler instance within mailcow: Configuration & Details - Configuration - Fail2ban parameters - Whitelisted networks/hosts .","title":"Requirements"},{"location":"third_party-mailpiler_integration/#setup","text":"Set the custom query function of mailpiler and append this to /usr/local/etc/piler/config-site.php : $config['MAILCOW_API_KEY'] = 'YOUR_READONLY_API_KEY'; $config['MAILCOW_SET_REALNAME'] = true; // when not specified, then default is false $config['CUSTOM_EMAIL_QUERY_FUNCTION'] = 'query_mailcow_for_email_access'; include('auth-mailcow.php'); You can also change the mailcow hostname, if required: $config['MAILCOW_HOST'] = 'mail.domain.tld'; // defaults to $config['IMAP_HOST'] Download the PHP file with the functions from the GitHub repo : curl -o /usr/local/etc/piler/auth-mailcow.php https://raw.githubusercontent.com/patschi/mailpiler-mailcow-integration/master/auth-mailcow.php Done! Make sure to re-login with your IMAP credentials for changes to take effect. If it doesn't work, most likely something's wrong with the API query itself. Consider debugging by sending manual API requests to the API. (Tip: Open https://mail.domain.tld/api on your instance)","title":"Setup"},{"location":"third_party-nextcloud/","text":"Manage Nextcloud using the helper script \u00b6 Nextcloud can be set up (parameter -i ) and removed (parameter -p ) with the helper script included with mailcow. In order to install Nextcloud simply navigate to your mailcow-dockerized root folder and run the helper script as follows: ./helper-scripts/nextcloud.sh -i In case you have forgotten the password (e.g. for admin) and can't request a new one via the password reset link on the login screen calling the helper script with -r as parameter allows you to set a new password. Only use this option if your Nextcloud isn't configured to use mailcow for authentication as described in the next section. Configure Nextcloud to use mailcow for authentication \u00b6 The following describes how set up authentication via mailcow using the OAuth2 protocol. We will only assume that you have already set up Nextcloud at cloud.example.com and that your mailcow is running at mail.example.com . It does not matter if your Nextcloud is running on a different server, you can still use mailcow for authentication. 1. Log into mailcow as administrator. 2. Scroll down to OAuth2 Apps and click the Add button. Specify the redirect URI as https://cloud.example.com/index.php/apps/sociallogin/custom_oauth2/Mailcow and click Add . Save the client ID and secret for later. Info Some installations, including those setup using the helper script of mailcow, need to remove index.php/ from the URL to get a successful redirect: https://cloud.example.com/apps/sociallogin/custom_oauth2/Mailcow 3. Log into Nextcloud as administrator. 4. Click the button in the top right corner and select Apps . Click the search button in the toolbar, search for the Social Login plugin and click Download and enable next to it. 5. Click the button in the top right corner and select Settings . Scroll down to the Administration section on the left and click Social login . 6. Uncheck the following items: \"Disable auto create new users\" \"Allow users to connect social logins with their accounts\" \"Do not prune not available user groups on login\" \"Automatically create groups if they do not exists\" \"Restrict login for users without mapped groups\" 7. Check the following items: \"Prevent creating an account if the email address exists in another account\" \"Update user profile every login\" \"Disable notify admins about new users\" Click the Save button. 8. Scroll down to Custom OAuth2 and click the + button. 9. Configure the parameters as follows: Internal name: Mailcow Title: Mailcow API Base URL: https://mail.example.com Authorize URL: https://mail.example.com/oauth/authorize Token URL: https://mail.example.com/oauth/token Profile URL: https://mail.example.com/oauth/profile Logout URL: (leave blank) Client ID: (what you obtained in step 1) Client Secret: (what you obtained in step 1) Scope: profile Click the Save button at the very bottom of the page. If you have previously used Nextcloud with mailcow authentication via user_external/IMAP, you need to perform some additional steps to link your existing user accounts with OAuth2. 1. Click the button in the top right corner and select Apps . Scroll down to the External user authentication app and click Remove next to it. 2. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_users (uid, uid_lower) SELECT DISTINCT uid, LOWER(uid) FROM nc_users_external; INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users_external; If you have previously used Nextcloud without mailcow authentication, but with the same usernames as mailcow, you can also link your existing user accounts with OAuth2. 1. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users; Update \u00b6 The Nextcloud instance can be updated easily with the web update mechanism. In the case of larger updates, there may be further changes to be made after the update. After the Nextcloud instance has been checked, problems are shown. This can be e.g. missing indices in the DB or similar. It shows which commands have to be executed, these have to be placed in the php-fpm-mailcow container. As an an example run the following command to add the missing indices. docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c \"php /web/nextcloud/occ db:add-missing-indices\" Debugging & Troubleshooting \u00b6 It may happen that you cannot reach the Nextcloud instance from your network. This may be due to the fact that the entry of your subnet in the array 'trusted_proxies' is missing. You can make changes in the Nextcloud config.php in data/web/nextcloud/config/* . 'trusted_proxies' => array ( 0 => 'fd4d:6169:6c63:6f77::/64', 1 => '172.22.1.0/24', 2 => 'NewSubnet/24', ), After the changes have been made, the nginx container must be restarted. docker-compose restart nginx-mailcow","title":"Nextcloud"},{"location":"third_party-nextcloud/#manage-nextcloud-using-the-helper-script","text":"Nextcloud can be set up (parameter -i ) and removed (parameter -p ) with the helper script included with mailcow. In order to install Nextcloud simply navigate to your mailcow-dockerized root folder and run the helper script as follows: ./helper-scripts/nextcloud.sh -i In case you have forgotten the password (e.g. for admin) and can't request a new one via the password reset link on the login screen calling the helper script with -r as parameter allows you to set a new password. Only use this option if your Nextcloud isn't configured to use mailcow for authentication as described in the next section.","title":"Manage Nextcloud using the helper script"},{"location":"third_party-nextcloud/#configure-nextcloud-to-use-mailcow-for-authentication","text":"The following describes how set up authentication via mailcow using the OAuth2 protocol. We will only assume that you have already set up Nextcloud at cloud.example.com and that your mailcow is running at mail.example.com . It does not matter if your Nextcloud is running on a different server, you can still use mailcow for authentication. 1. Log into mailcow as administrator. 2. Scroll down to OAuth2 Apps and click the Add button. Specify the redirect URI as https://cloud.example.com/index.php/apps/sociallogin/custom_oauth2/Mailcow and click Add . Save the client ID and secret for later. Info Some installations, including those setup using the helper script of mailcow, need to remove index.php/ from the URL to get a successful redirect: https://cloud.example.com/apps/sociallogin/custom_oauth2/Mailcow 3. Log into Nextcloud as administrator. 4. Click the button in the top right corner and select Apps . Click the search button in the toolbar, search for the Social Login plugin and click Download and enable next to it. 5. Click the button in the top right corner and select Settings . Scroll down to the Administration section on the left and click Social login . 6. Uncheck the following items: \"Disable auto create new users\" \"Allow users to connect social logins with their accounts\" \"Do not prune not available user groups on login\" \"Automatically create groups if they do not exists\" \"Restrict login for users without mapped groups\" 7. Check the following items: \"Prevent creating an account if the email address exists in another account\" \"Update user profile every login\" \"Disable notify admins about new users\" Click the Save button. 8. Scroll down to Custom OAuth2 and click the + button. 9. Configure the parameters as follows: Internal name: Mailcow Title: Mailcow API Base URL: https://mail.example.com Authorize URL: https://mail.example.com/oauth/authorize Token URL: https://mail.example.com/oauth/token Profile URL: https://mail.example.com/oauth/profile Logout URL: (leave blank) Client ID: (what you obtained in step 1) Client Secret: (what you obtained in step 1) Scope: profile Click the Save button at the very bottom of the page. If you have previously used Nextcloud with mailcow authentication via user_external/IMAP, you need to perform some additional steps to link your existing user accounts with OAuth2. 1. Click the button in the top right corner and select Apps . Scroll down to the External user authentication app and click Remove next to it. 2. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_users (uid, uid_lower) SELECT DISTINCT uid, LOWER(uid) FROM nc_users_external; INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users_external; If you have previously used Nextcloud without mailcow authentication, but with the same usernames as mailcow, you can also link your existing user accounts with OAuth2. 1. Run the following queries in your Nextcloud database (if you set up Nextcloud using mailcow's script, you can run source mailcow.conf && docker-compose exec mysql-mailcow mysql -u$DBUSER -p$DBPASS $DBNAME ): INSERT INTO nc_sociallogin_connect (uid, identifier) SELECT DISTINCT uid, CONCAT(\"Mailcow-\", uid) FROM nc_users;","title":"Configure Nextcloud to use mailcow for authentication"},{"location":"third_party-nextcloud/#update","text":"The Nextcloud instance can be updated easily with the web update mechanism. In the case of larger updates, there may be further changes to be made after the update. After the Nextcloud instance has been checked, problems are shown. This can be e.g. missing indices in the DB or similar. It shows which commands have to be executed, these have to be placed in the php-fpm-mailcow container. As an an example run the following command to add the missing indices. docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c \"php /web/nextcloud/occ db:add-missing-indices\"","title":"Update"},{"location":"third_party-nextcloud/#debugging-troubleshooting","text":"It may happen that you cannot reach the Nextcloud instance from your network. This may be due to the fact that the entry of your subnet in the array 'trusted_proxies' is missing. You can make changes in the Nextcloud config.php in data/web/nextcloud/config/* . 'trusted_proxies' => array ( 0 => 'fd4d:6169:6c63:6f77::/64', 1 => '172.22.1.0/24', 2 => 'NewSubnet/24', ), After the changes have been made, the nginx container must be restarted. docker-compose restart nginx-mailcow","title":"Debugging & Troubleshooting"},{"location":"third_party-portainer/","text":"In order to enable Portainer, the docker-compose.yml and site.conf for Nginx must be modified. 1. Create a new file docker-compose.override.yml in the mailcow-dockerized root folder and insert the following configuration version: '2.1' services: portainer-mailcow: image: portainer/portainer-ce volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data/conf/portainer:/data restart: always dns: - 172.22.1.254 dns_search: mailcow-network networks: mailcow-network: aliases: - portainer 2a. Create data/conf/nginx/portainer.conf : upstream portainer { server portainer-mailcow:9000; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } 2b. Insert a new location to the default mailcow site by creating the file data/conf/nginx/site.portainer.custom : location /portainer/ { proxy_http_version 1.1; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; proxy_set_header Connection \"\"; proxy_buffers 32 4k; proxy_pass http://portainer/; } location /portainer/api/websocket/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_pass http://portainer/api/websocket/; } 3. Apply your changes: docker-compose up -d && docker-compose restart nginx-mailcow Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You\u2019ll then be prompted to specify a new password for the admin account. After specifying your password, you\u2019ll then be able to connect to the Portainer UI.","title":"Portainer"},{"location":"third_party-roundcube/","text":"Download Roundcube 1.5.x to the web htdocs directory and extract it (here rc/ ): # Check for a newer release! cd data/web wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.5-rc/roundcubemail-1.5-rc-complete.tar.gz | tar xfvz - # Change folder name mv roundcubemail-1.5-rc rc # Change permissions chown -R root: rc/ If you need spell check features, create a file data/hooks/phpfpm/aspell.sh with the following content, then chmod +x data/hooks/phpfpm/aspell.sh . This installs a local spell check engine. #!/bin/bash apk update apk add aspell-en # or any other language Create a file data/web/rc/config/config.inc.php with the following content. Change the des_key parameter to a random value. It is used to temporarily store your IMAP password. The \"db_prefix\" is optional but recommended. array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); $config['enable_installer'] = true; $config['smtp_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); $config['db_prefix'] = 'mailcow_rc1'; Point your browser to https://myserver/rc/installer and follow the instructions. Initialize the database and leave the installer. Delete the directory data/web/rc/installer after a successful installation! Configure ManageSieve filtering \u00b6 Open data/web/rc/plugins/managesieve/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['managesieve_port'] = 4190; $config['managesieve_host'] = 'tls://dovecot'; $config['managesieve_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); // Enables separate management interface for vacation responses (out-of-office) // 0 - no separate section (default), // 1 - add Vacation section, // 2 - add Vacation section, but hide Filters section $config['managesieve_vacation'] = 1; Enable change password function in Roundcube \u00b6 Open data/web/rc/config/config.inc.php and enable the password plugin: ... $config['plugins'] = array( 'archive', 'password', ); ... Open data/web/rc/plugins/password/password.php , search for case 'ssha': and add above: case 'ssha256': $salt = rcube_utils::random_bytes(8); $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); $prefix = '{SSHA256}'; break; Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['password_driver'] = 'sql'; $config['password_algorithm'] = 'ssha256'; $config['password_algorithm_prefix'] = '{SSHA256}'; $config['password_query'] = \"UPDATE mailbox SET password = %P WHERE username = %u\"; Integrate CardDAV addressbooks in Roundcube \u00b6 Download the latest release of RCMCardDAV to the Roundcube plugin directory and extract it (here rc/plugins ): cd data/web/rc/plugins wget -O - https://github.com/mstilkerich/rcmcarddav/releases/download/v4.1.2/carddav-v4.1.2.tar.gz | tar xfvz - chown -R root: carddav/ Copy the file config.inc.php.dist to config.inc.php (here in rc/plugins/carddav ) and append the following preset to the end of the file - don't forget to replace mx.example.org with your own hostname: $prefs['SOGo'] = array( 'name' => 'SOGo', 'username' => '%u', 'password' => '%p', 'url' => 'https://mx.example.org/SOGo/dav/%u/', 'carddav_name_only' => true, 'use_categories' => true, 'active' => true, 'readonly' => false, 'refresh_time' => '02:00:00', 'fixed' => array( 'active', 'name', 'username', 'password', 'refresh_time' ), 'hide' => false, ); Please note, that this preset only integrates the default addressbook (the one that's named \"Personal Address Book\" and can't be deleted). Additional addressbooks are currently not automatically detected but can be manually added within the roundecube settings. Enable the plugin by adding carddav to $config['plugins'] in rc/config/config.inc.php . If you want to remove the default addressbooks (stored in the Roundcube database), so that only the CardDAV addressbooks are accessible, append $config['address_book_type'] = ''; to the config file data/web/rc/config/config.inc.php . Optionally, you can add Roundcube's link to the mailcow Apps list. To do this, open or create data/web/inc/vars.local.inc.php and add the following code-block: NOTE: Don't forget to add the 'SOGo', 'link' => '/SOGo/' ), array( 'name' => 'Roundcube', 'link' => '/rc/' ) ); ...","title":"Roundcube"},{"location":"third_party-roundcube/#configure-managesieve-filtering","text":"Open data/web/rc/plugins/managesieve/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['managesieve_port'] = 4190; $config['managesieve_host'] = 'tls://dovecot'; $config['managesieve_conn_options'] = array( 'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) ); // Enables separate management interface for vacation responses (out-of-office) // 0 - no separate section (default), // 1 - add Vacation section, // 2 - add Vacation section, but hide Filters section $config['managesieve_vacation'] = 1;","title":"Configure ManageSieve filtering"},{"location":"third_party-roundcube/#enable-change-password-function-in-roundcube","text":"Open data/web/rc/config/config.inc.php and enable the password plugin: ... $config['plugins'] = array( 'archive', 'password', ); ... Open data/web/rc/plugins/password/password.php , search for case 'ssha': and add above: case 'ssha256': $salt = rcube_utils::random_bytes(8); $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); $prefix = '{SSHA256}'; break; Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file): $config['password_driver'] = 'sql'; $config['password_algorithm'] = 'ssha256'; $config['password_algorithm_prefix'] = '{SSHA256}'; $config['password_query'] = \"UPDATE mailbox SET password = %P WHERE username = %u\";","title":"Enable change password function in Roundcube"},{"location":"third_party-roundcube/#integrate-carddav-addressbooks-in-roundcube","text":"Download the latest release of RCMCardDAV to the Roundcube plugin directory and extract it (here rc/plugins ): cd data/web/rc/plugins wget -O - https://github.com/mstilkerich/rcmcarddav/releases/download/v4.1.2/carddav-v4.1.2.tar.gz | tar xfvz - chown -R root: carddav/ Copy the file config.inc.php.dist to config.inc.php (here in rc/plugins/carddav ) and append the following preset to the end of the file - don't forget to replace mx.example.org with your own hostname: $prefs['SOGo'] = array( 'name' => 'SOGo', 'username' => '%u', 'password' => '%p', 'url' => 'https://mx.example.org/SOGo/dav/%u/', 'carddav_name_only' => true, 'use_categories' => true, 'active' => true, 'readonly' => false, 'refresh_time' => '02:00:00', 'fixed' => array( 'active', 'name', 'username', 'password', 'refresh_time' ), 'hide' => false, ); Please note, that this preset only integrates the default addressbook (the one that's named \"Personal Address Book\" and can't be deleted). Additional addressbooks are currently not automatically detected but can be manually added within the roundecube settings. Enable the plugin by adding carddav to $config['plugins'] in rc/config/config.inc.php . If you want to remove the default addressbooks (stored in the Roundcube database), so that only the CardDAV addressbooks are accessible, append $config['address_book_type'] = ''; to the config file data/web/rc/config/config.inc.php . Optionally, you can add Roundcube's link to the mailcow Apps list. To do this, open or create data/web/inc/vars.local.inc.php and add the following code-block: NOTE: Don't forget to add the 'SOGo', 'link' => '/SOGo/' ), array( 'name' => 'Roundcube', 'link' => '/rc/' ) ); ...","title":"Integrate CardDAV addressbooks in Roundcube"},{"location":"third_party-thunderbird/","text":"Build the SOGo Connector plugin \u00b6 Install GNU Make, tar, and ZIP if you don't already have them installed. On Debian/Ubuntu, this can be done using apt-get install make tar zip Next, go to data/web inside mailcow-dockerized. Place the file thunderbird-plugins.php into that directory. Create a new directory thunderbird-plugins and place the script build-plugins.sh into it. Finally, execute the script with your hostname as an argument and piping it the names of all domains that mailcow handles. All of this can be done using the following commands: cd data/web curl -LO https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/thunderbird-plugins.php mkdir thunderbird-plugins cd thunderbird-plugins curl -Lo build-plugins.sh https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/build-thunderbird-plugins.sh chmod +x build-plugins.sh echo example.com example.org | ./build-plugins.sh mailcow.example.com Install it in Thunderbird \u00b6 After you have set up your mailcow IMAP account in Thunderbird, download the SOGo Connector plugin for your domain, e.g. https://mailcow.example.com/thunderbird-plugins/sogo-connector-68.0.1-example.com.xpi (see data/web/thunderbird-plugins ), and install it into Thunderbird. All your address books and calendars will be configured automatically.","title":"SOGo Connector for Thunderbird"},{"location":"third_party-thunderbird/#build-the-sogo-connector-plugin","text":"Install GNU Make, tar, and ZIP if you don't already have them installed. On Debian/Ubuntu, this can be done using apt-get install make tar zip Next, go to data/web inside mailcow-dockerized. Place the file thunderbird-plugins.php into that directory. Create a new directory thunderbird-plugins and place the script build-plugins.sh into it. Finally, execute the script with your hostname as an argument and piping it the names of all domains that mailcow handles. All of this can be done using the following commands: cd data/web curl -LO https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/thunderbird-plugins.php mkdir thunderbird-plugins cd thunderbird-plugins curl -Lo build-plugins.sh https://github.com/mailcow/mailcow-dockerized-docs/raw/master/docs/download/build-thunderbird-plugins.sh chmod +x build-plugins.sh echo example.com example.org | ./build-plugins.sh mailcow.example.com","title":"Build the SOGo Connector plugin"},{"location":"third_party-thunderbird/#install-it-in-thunderbird","text":"After you have set up your mailcow IMAP account in Thunderbird, download the SOGo Connector plugin for your domain, e.g. https://mailcow.example.com/thunderbird-plugins/sogo-connector-68.0.1-example.com.xpi (see data/web/thunderbird-plugins ), and install it into Thunderbird. All your address books and calendars will be configured automatically.","title":"Install it in Thunderbird"},{"location":"u_e-80_to_443/","text":"Since February the 28th 2017 mailcow does come with port 80 and 443 enabled. Do not use the config below for reverse proxy setups , please see our reverse proxy guide for this, which includes a redirect from HTTP to HTTPS. Open mailcow.conf and set HTTP_BIND= - if not already set. Create a new file data/conf/nginx/redirect.conf and add the following server config to the file: server { root /web; listen 80 default_server; listen [::]:80 default_server; include /etc/nginx/conf.d/server_name.active; if ( $request_uri ~* \"%0A|%0D\" ) { return 403; } location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } location / { return 301 https://$host$uri$is_args$args; } } In case you changed the HTTP_BIND parameter, recreate the container: docker-compose up -d Otherwise restart Nginx: docker-compose restart nginx-mailcow","title":"Redirect HTTP to HTTPS"},{"location":"u_e-autodiscover_config/","text":"You do not need to change or create this file, autodiscover works out of the box . This guide is only meant for customizations to the autodiscover or autoconfig process. Newer Outlook clients (especially those delivered with O365) will not autodiscover mail profiles. Keep in mind, that ActiveSync should NOT be used with a desktop client . Open/create data/web/inc/vars.local.inc.php and add your changes to the configuration array. Changes will be merged with \"$autodiscover_config\" in data/web/inc/vars.inc.php ): 'activesync', // If autodiscoverType => activesync, also use ActiveSync (EAS) for Outlook desktop clients (>= Outlook 2013 on Windows) // Outlook for Mac does not support ActiveSync 'useEASforOutlook' => 'yes', // Please don't use STARTTLS-enabled service ports in the \"port\" variable. // The autodiscover service will always point to SMTPS and IMAPS (TLS-wrapped services). // The autoconfig service will additionally announce the STARTTLS-enabled ports, specified in the \"tlsport\" variable. 'imap' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('IMAPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('IMAP_PORT'))), ), 'pop3' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('POPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('POP_PORT'))), ), 'smtp' => array( 'server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('SMTPS_PORT'))), 'tlsport' => array_pop(explode(':', getenv('SUBMISSION_PORT'))), ), 'activesync' => array( 'url' => 'https://'.$mailcow_hostname.($https_port == 443 ? '' : ':'.$https_port).'/Microsoft-Server-ActiveSync', ), 'caldav' => array( 'server' => $mailcow_hostname, 'port' => $https_port, ), 'carddav' => array( 'server' => $mailcow_hostname, 'port' => $https_port, ), ); To always use IMAP and SMTP instead of EAS, set 'autodiscoverType' => 'imap' . Disable ActiveSync for Outlook desktop clients by setting \"useEASforOutlook\" to \"no\".","title":"Autodiscover / Autoconfig"},{"location":"u_e-backup_restore-maildir/","text":"Backup \u00b6 This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: cd /path/to/mailcow-dockerized docker run --rm -i -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar cvfz /backup/backup_vmail.tar.gz /vmail You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. Set the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz Restore \u00b6 cd /path/to/mailcow-dockerized docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar xvfz /backup/backup_vmail.tar.gz","title":"Maildir"},{"location":"u_e-backup_restore-maildir/#backup","text":"This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: cd /path/to/mailcow-dockerized docker run --rm -i -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar cvfz /backup/backup_vmail.tar.gz /vmail You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. Set the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz","title":"Backup"},{"location":"u_e-backup_restore-maildir/#restore","text":"cd /path/to/mailcow-dockerized docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \"/var/vmail\" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:stretch-slim tar xvfz /backup/backup_vmail.tar.gz","title":"Restore"},{"location":"u_e-backup_restore-mysql/","text":"Backup \u00b6 cd /path/to/mailcow-dockerized source mailcow.conf DATE=$(date +\"%Y%m%d_%H%M%S\") docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql Restore \u00b6 Warning You should redirect the SQL dump without docker-compose to prevent parsing errors. cd /path/to/mailcow-dockerized source mailcow.conf docker exec -i $(docker-compose ps -q mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql","title":"MySQL"},{"location":"u_e-backup_restore-mysql/#backup","text":"cd /path/to/mailcow-dockerized source mailcow.conf DATE=$(date +\"%Y%m%d_%H%M%S\") docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql","title":"Backup"},{"location":"u_e-backup_restore-mysql/#restore","text":"Warning You should redirect the SQL dump without docker-compose to prevent parsing errors. cd /path/to/mailcow-dockerized source mailcow.conf docker exec -i $(docker-compose ps -q mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql","title":"Restore"},{"location":"u_e-docker-cust_dockerfiles/","text":"You need to copy the override file with corresponding build tags to the mailcow: dockerized root folder (i.e. /opt/mailcow-dockerized ): cp helper-scripts/docker-compose.override.yml.d/BUILD_FLAGS/docker-compose.override.yml docker-compose.override.yml Make your changes in data/Dockerfiles/$service and build the image locally: docker build data/Dockerfiles/service -t mailcow/$service Now auto-recreate modified containers: docker-compose up -d","title":"Customize Dockerfiles"},{"location":"u_e-docker-dc_bash_compl/","text":"To get some sexy bash completion inside your containers simply execute the following: curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose","title":"Docker Compose Bash Completion"},{"location":"u_e-dovecot-any_acl/","text":"On August the 17th, we disabled the possibility to share with \"any\" or \"all authenticated users\" by default. This function can be re-enabled by setting ACL_ANYONE to allow in mailcow.conf: ACL_ANYONE=allow Apply the changes by running docker-compose up -d .","title":"Enable \"any\" ACL settings"},{"location":"u_e-dovecot-catchall_vacation/","text":"The Dovecot parameter sieve_vacation_dont_check_recipient - which was by default set to yes in mailcow configurations pre 21st July - allows for vacation replies even when a mail is sent to non-existent mailboxes like a catch-all addresses. We decided to switch this parameter back to no and allow a user to specify which recipient address triggers a vacation reply. The triggering recipients can also be configured in SOGos autoresponder feature.","title":"Vacation replies for catchall addresses"},{"location":"u_e-dovecot-expunge/","text":"If you want to delete old mails out of the .Junk or .Trash folders or maybe delete all read mails that are older than a certain amount of time you may use dovecot's tool doveadm man doveadm-expunge . The manual way \u00b6 That said, let's dive in: Delete a user's mails inside the junk folder that are read and older than 4 hours docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'Junk' SEEN not SINCE 4h Delete all user's mails in the junk folder that are older than 7 days docker-compose exec dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 7d Delete all mails (of all users) in all folders that are older than 52 weeks (internal date of the mail, not the date it was saved on the system => before instead of savedbefore ). Useful for deleting very old mails on all users and folders (thus especially useful for GDPR-compliance). docker-compose exec dovecot-mailcow doveadm expunge -A mailbox % before 52w Delete mails inside a custom folder inside a user's inbox that are not flagged and older than 2 weeks docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'INBOX/custom-folder' not FLAGGED not SINCE 2w Info For possible time spans or search keys have a look at man doveadm-search-query Job scheduler \u00b6 via the host system cron \u00b6 If you want to automate such a task you can create a cron job on your host that calls a script like the one below: #!/bin/bash # Path to mailcow-dockerized, e.g. /opt/mailcow-dockerized cd /path/to/your/mailcow-dockerized /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 2w /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' SEEN not SINCE 12h [...] To create a cron job you may execute crontab -e and insert something like the following to execute a script: # Execute everyday at 04:00 A.M. 0 4 * * * /path/to/your/expunge_mailboxes.sh via Docker job scheduler \u00b6 To archive this with a docker job scheduler use this docker-compose.override.yml with your mailcow: version: '2.1' services: ofelia: image: mcuadros/ofelia:latest restart: always command: daemon --docker volumes: - /var/run/docker.sock:/var/run/docker.sock:ro network_mode: none dovecot-mailcow: labels: - \"ofelia.enabled=true\" - \"ofelia.job-exec.dovecot-expunge-trash.schedule=0 4 * * *\" - \"ofelia.job-exec.dovecot-expunge-trash.command=doveadm expunge -A mailbox 'Junk' savedbefore 2w\" - \"ofelia.job-exec.dovecot-expunge-trash.tty=false\" The job controller just need access to the docker control socket to be able to emulate the behavior of \"exec\". Then we add a few label to our dovecot-container to activate the job scheduler and tell him in a cron compatible scheduling format when to run. If you struggle with that schedule string you can use crontab guru . This docker-compose.override.yml deletes all mails older then 2 weeks from the \"Junk\" folder every day at 4 am. To see if things ran proper, you can not only see in your mailbox but also check Ofelia's docker log if it looks something like this: common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Started - doveadm expunge -A mailbox 'Junk' savedbefore 2w, common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Finished in \"285.032291ms\", failed: false, skipped: false, error: none, If it failed it will say so and give you the output of the doveadm in the log to make it easy on you to debug. In case you want to add more jobs, ensure you change the \"dovecot-expunge-trash\" part after \"ofelia.job-exec.\" to something else, it defines the name of the job. Syntax of the labels you find at mcuadros/ofelia .","title":"Expunge a Users mails"},{"location":"u_e-dovecot-expunge/#the-manual-way","text":"That said, let's dive in: Delete a user's mails inside the junk folder that are read and older than 4 hours docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'Junk' SEEN not SINCE 4h Delete all user's mails in the junk folder that are older than 7 days docker-compose exec dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 7d Delete all mails (of all users) in all folders that are older than 52 weeks (internal date of the mail, not the date it was saved on the system => before instead of savedbefore ). Useful for deleting very old mails on all users and folders (thus especially useful for GDPR-compliance). docker-compose exec dovecot-mailcow doveadm expunge -A mailbox % before 52w Delete mails inside a custom folder inside a user's inbox that are not flagged and older than 2 weeks docker-compose exec dovecot-mailcow doveadm expunge -u 'mailbox@example.com' mailbox 'INBOX/custom-folder' not FLAGGED not SINCE 2w Info For possible time spans or search keys have a look at man doveadm-search-query","title":"The manual way"},{"location":"u_e-dovecot-expunge/#job-scheduler","text":"","title":"Job scheduler"},{"location":"u_e-dovecot-expunge/#via-the-host-system-cron","text":"If you want to automate such a task you can create a cron job on your host that calls a script like the one below: #!/bin/bash # Path to mailcow-dockerized, e.g. /opt/mailcow-dockerized cd /path/to/your/mailcow-dockerized /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' savedbefore 2w /usr/local/bin/docker-compose exec -T dovecot-mailcow doveadm expunge -A mailbox 'Junk' SEEN not SINCE 12h [...] To create a cron job you may execute crontab -e and insert something like the following to execute a script: # Execute everyday at 04:00 A.M. 0 4 * * * /path/to/your/expunge_mailboxes.sh","title":"via the host system cron"},{"location":"u_e-dovecot-expunge/#via-docker-job-scheduler","text":"To archive this with a docker job scheduler use this docker-compose.override.yml with your mailcow: version: '2.1' services: ofelia: image: mcuadros/ofelia:latest restart: always command: daemon --docker volumes: - /var/run/docker.sock:/var/run/docker.sock:ro network_mode: none dovecot-mailcow: labels: - \"ofelia.enabled=true\" - \"ofelia.job-exec.dovecot-expunge-trash.schedule=0 4 * * *\" - \"ofelia.job-exec.dovecot-expunge-trash.command=doveadm expunge -A mailbox 'Junk' savedbefore 2w\" - \"ofelia.job-exec.dovecot-expunge-trash.tty=false\" The job controller just need access to the docker control socket to be able to emulate the behavior of \"exec\". Then we add a few label to our dovecot-container to activate the job scheduler and tell him in a cron compatible scheduling format when to run. If you struggle with that schedule string you can use crontab guru . This docker-compose.override.yml deletes all mails older then 2 weeks from the \"Junk\" folder every day at 4 am. To see if things ran proper, you can not only see in your mailbox but also check Ofelia's docker log if it looks something like this: common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Started - doveadm expunge -A mailbox 'Junk' savedbefore 2w, common.go:124 \u25b6 NOTICE [Job \"dovecot-expunge-trash\" (8759567efa66)] Finished in \"285.032291ms\", failed: false, skipped: false, error: none, If it failed it will say so and give you the output of the doveadm in the log to make it easy on you to debug. In case you want to add more jobs, ensure you change the \"dovecot-expunge-trash\" part after \"ofelia.job-exec.\" to something else, it defines the name of the job. Syntax of the labels you find at mcuadros/ofelia .","title":"via Docker job scheduler"},{"location":"u_e-dovecot-extra_conf/","text":"Create a file data/conf/dovecot/extra.conf - if missing - and add your additional content here. Restart dovecot-mailcow to apply your changes: docker-compose restart dovecot-mailcow","title":"Customize/Expand dovecot.conf"},{"location":"u_e-dovecot-fts/","text":"Solr is used for setups with memory >= 3.5 GiB to provide full-text search in Dovecot. Please be aware that applications like Solr may need maintenance from time to time. Besides that, Solr will eat a lot of RAM, depending on the usage of your server. Please avoid it on machines with less than 3 GB RAM. The default heap size (1024 M) is defined in mailcow.conf. Since we run in Docker and create our containers with the \"restart: always\" flag, a oom situation will at least only trigger a restart of the container. FTS related Dovecot commands \u00b6 # single user docker-compose exec dovecot-mailcow doveadm fts rescan -u user@domain # all users docker-compose exec dovecot-mailcow doveadm fts rescan -A Dovecot Wiki: \"Scan what mails exist in the full text search index and compare those to what actually exist in mailboxes. This removes mails from the index that have already been expunged and makes sure that the next doveadm index will index all the missing mails (if any).\" This does not re-index a mailbox. It basically repairs a given index. If you want to re-index data immediately, you can run the followig command, where '*' can also be a mailbox mask like 'Sent'. You do not need to run these commands, but it will speed things up a bit: # single user docker-compose exec dovecot-mailcow doveadm index -u user@domain '*' # all users, but obviously slower and more dangerous docker-compose exec dovecot-mailcow doveadm index -A '*' This will take some time depending on your machine and Solr can run oom, monitor it! Because re-indexing is very sensible, we did not include it to mailcow UI. You will need to take care of any errors while re-indexing a mailbox. Delete mailbox data \u00b6 mailcow will purge index data of a user when deleting a mailbox.","title":"FTS (Solr)"},{"location":"u_e-dovecot-fts/#fts-related-dovecot-commands","text":"# single user docker-compose exec dovecot-mailcow doveadm fts rescan -u user@domain # all users docker-compose exec dovecot-mailcow doveadm fts rescan -A Dovecot Wiki: \"Scan what mails exist in the full text search index and compare those to what actually exist in mailboxes. This removes mails from the index that have already been expunged and makes sure that the next doveadm index will index all the missing mails (if any).\" This does not re-index a mailbox. It basically repairs a given index. If you want to re-index data immediately, you can run the followig command, where '*' can also be a mailbox mask like 'Sent'. You do not need to run these commands, but it will speed things up a bit: # single user docker-compose exec dovecot-mailcow doveadm index -u user@domain '*' # all users, but obviously slower and more dangerous docker-compose exec dovecot-mailcow doveadm index -A '*' This will take some time depending on your machine and Solr can run oom, monitor it! Because re-indexing is very sensible, we did not include it to mailcow UI. You will need to take care of any errors while re-indexing a mailbox.","title":"FTS related Dovecot commands"},{"location":"u_e-dovecot-fts/#delete-mailbox-data","text":"mailcow will purge index data of a user when deleting a mailbox.","title":"Delete mailbox data"},{"location":"u_e-dovecot-idle_interval/","text":"Changing the IMAP IDLE interval \u00b6 What is the IDLE interval? \u00b6 Per default, Dovecot sends a \"I'm still here\" notification to every client that has an open connection with Dovecot to get mails as quickly as possible without manually polling it (IMAP PUSH). This notification is controlled by the setting imap_idle_notify_interval , which defaults to 2 minutes. A short interval results in the client getting a lot of messages for this connection, which is bad for mobile devices, because every time the device receives this message, the mailing app has to wake up. This can result in unnecessary battery drain. Edit the value \u00b6 Change configuration \u00b6 Create a new file data/conf/dovecot/extra.conf (or edit it if it already exists). Insert the setting followed by the new value. For example, to set the interval to 5 minutes you could type: imap_idle_notify_interval = 5 mins 29 minutes is the maximum value allowed by the corresponding RFC . Warning This isn't a default setting in mailcow because we don't know how this setting changes the behavior of other clients. Be careful if you change this and monitor different behavior. Reload Dovecot \u00b6 Now reload Dovecot: docker-compose exec dovecot-mailcow dovecot reload Info You can check the value of this setting with docker-compose exec dovecot-mailcow dovecot -a | grep \"imap_idle_notify_interval\" If you didn't change it, it should be at 2m. If you did change it, you should see your new value.","title":"IMAP IDLE interval"},{"location":"u_e-dovecot-idle_interval/#changing-the-imap-idle-interval","text":"","title":"Changing the IMAP IDLE interval"},{"location":"u_e-dovecot-idle_interval/#what-is-the-idle-interval","text":"Per default, Dovecot sends a \"I'm still here\" notification to every client that has an open connection with Dovecot to get mails as quickly as possible without manually polling it (IMAP PUSH). This notification is controlled by the setting imap_idle_notify_interval , which defaults to 2 minutes. A short interval results in the client getting a lot of messages for this connection, which is bad for mobile devices, because every time the device receives this message, the mailing app has to wake up. This can result in unnecessary battery drain.","title":"What is the IDLE interval?"},{"location":"u_e-dovecot-idle_interval/#edit-the-value","text":"","title":"Edit the value"},{"location":"u_e-dovecot-idle_interval/#change-configuration","text":"Create a new file data/conf/dovecot/extra.conf (or edit it if it already exists). Insert the setting followed by the new value. For example, to set the interval to 5 minutes you could type: imap_idle_notify_interval = 5 mins 29 minutes is the maximum value allowed by the corresponding RFC . Warning This isn't a default setting in mailcow because we don't know how this setting changes the behavior of other clients. Be careful if you change this and monitor different behavior.","title":"Change configuration"},{"location":"u_e-dovecot-idle_interval/#reload-dovecot","text":"Now reload Dovecot: docker-compose exec dovecot-mailcow dovecot reload Info You can check the value of this setting with docker-compose exec dovecot-mailcow dovecot -a | grep \"imap_idle_notify_interval\" If you didn't change it, it should be at 2m. If you did change it, you should see your new value.","title":"Reload Dovecot"},{"location":"u_e-dovecot-mail-crypt/","text":"Mails are stored compressed (lz4) and encrypted. The key pair can be found in crypt-vol-1. If you want to decode/encode existing maildir files, you can use the following script at your own risk: Enter Dovecot by running docker-compose exec dovecot-mailcow /bin/bash in the mailcow-dockerized location. # Decrypt /var/vmail find /var/vmail/ -type f -regextype egrep -regex '.*S=.*W=.*' | while read -r file; do if [[ $(head -c7 \"$file\") == \"CRYPTED\" ]]; then doveadm fs get compress lz4:0:crypt:private_key_path=/mail_crypt/ecprivkey.pem:public_key_path=/mail_crypt/ecpubkey.pem:posix:prefix=/ \\ \"$file\" > \"/tmp/$(basename \"$file\")\" if [[ -s \"/tmp/$(basename \"$file\")\" ]]; then chmod 600 \"/tmp/$(basename \"$file\")\" chown 5000:5000 \"/tmp/$(basename \"$file\")\" mv \"/tmp/$(basename \"$file\")\" \"$file\" else rm \"/tmp/$(basename \"$file\")\" fi fi done # Encrypt /var/vmail find /var/vmail/ -type f -regextype egrep -regex '.*S=.*W=.*' | while read -r file; do if [[ $(head -c7 \"$file\") != \"CRYPTED\" ]]; then doveadm fs put crypt private_key_path=/mail_crypt/ecprivkey.pem:public_key_path=/mail_crypt/ecpubkey.pem:posix:prefix=/ \\ \"$file\" \"$file\" chmod 600 \"$file\" chown 5000:5000 \"$file\" fi done","title":"Mail crypt"},{"location":"u_e-dovecot-more/","text":"Here is just an unsorted list of useful doveadm commands that could be useful. doveadm quota \u00b6 The quota get and quota recalc 1 commands are used to display or recalculate the current user's quota usage. The reported values are in kilobytes . To list the current quota status for a user / mailbox, do: doveadm quota get -u 'mailbox@example.org' To list the quota storage value for all users, do: doveadm quota get -A |grep \"STORAGE\" Recalculate a single user's quota usage: doveadm quota recalc -u 'mailbox@example.org' doveadm search \u00b6 The doveadm search 2 command is used to find messages matching your query. It can return the username, mailbox-GUID / -UID and message-GUIDs / -UIDs. To view the number of messages, by user, in their .Trash folder: doveadm search -A mailbox 'Trash' | awk '{print $1}' | sort | uniq -c Show all messages in a user's inbox older then 90 days: doveadm search -u 'mailbox@example.org' mailbox 'INBOX' savedbefore 90d Show all messages in any folder that are older then 30 days for mailbox@example.org : doveadm search -u 'mailbox@example.org' mailbox \"*\" savedbefore 30d https://wiki.dovecot.org/Tools/Doveadm/Quota \u21a9 https://wiki.dovecot.org/Tools/Doveadm/Search \u21a9","title":"More Examples with DOVEADM"},{"location":"u_e-dovecot-more/#doveadm-quota","text":"The quota get and quota recalc 1 commands are used to display or recalculate the current user's quota usage. The reported values are in kilobytes . To list the current quota status for a user / mailbox, do: doveadm quota get -u 'mailbox@example.org' To list the quota storage value for all users, do: doveadm quota get -A |grep \"STORAGE\" Recalculate a single user's quota usage: doveadm quota recalc -u 'mailbox@example.org'","title":"doveadm quota"},{"location":"u_e-dovecot-more/#doveadm-search","text":"The doveadm search 2 command is used to find messages matching your query. It can return the username, mailbox-GUID / -UID and message-GUIDs / -UIDs. To view the number of messages, by user, in their .Trash folder: doveadm search -A mailbox 'Trash' | awk '{print $1}' | sort | uniq -c Show all messages in a user's inbox older then 90 days: doveadm search -u 'mailbox@example.org' mailbox 'INBOX' savedbefore 90d Show all messages in any folder that are older then 30 days for mailbox@example.org : doveadm search -u 'mailbox@example.org' mailbox \"*\" savedbefore 30d https://wiki.dovecot.org/Tools/Doveadm/Quota \u21a9 https://wiki.dovecot.org/Tools/Doveadm/Search \u21a9","title":"doveadm search"},{"location":"u_e-dovecot-public_folder/","text":"Create a new public namespace \"Public\" and a mailbox \"Develcow\" inside that namespace: Edit or create data/conf/dovecot/extra.conf , add: namespace { type = public separator = / prefix = Public/ location = maildir:/var/vmail/public:INDEXPVT=~/public subscriptions = yes mailbox \"Develcow\" { auto = subscribe } } :INDEXPVT=~/public can be omitted if per-user seen flags are not wanted. The new mailbox in the public namespace will be auto-subscribed by users. To allow all authenticated users access full to that new mailbox (not the whole namespace), run: docker-compose exec dovecot-mailcow doveadm acl set -A \"Public/Develcow\" \"authenticated\" lookup read write write-seen write-deleted insert post delete expunge create Adjust the command to your needs if you like to assign more granular rights per user (use -u user@domain instead of -A for example). Allow authenticated users access to the whole public namespace \u00b6 To allow all authenticated users access full access to the whole public namespace and its subfolders, create a new dovecot-acl file in the namespace root directory: Open/edit/create /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/public/dovecot-acl (adjust the path accordingly) to create the global ACL file with the following content: authenticated kxeilprwts kxeilprwts equals to lookup read write write-seen write-deleted insert post delete expunge create . You can use doveadm acl set -u user@domain \"Public/Develcow\" user=user@domain lookup read to limit access for a single user. You may also turn it around to limit access for all users to \"lr\" and grant only some users full access. See Dovecot ACL for further information about ACL.","title":"Public folders"},{"location":"u_e-dovecot-public_folder/#allow-authenticated-users-access-to-the-whole-public-namespace","text":"To allow all authenticated users access full access to the whole public namespace and its subfolders, create a new dovecot-acl file in the namespace root directory: Open/edit/create /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/public/dovecot-acl (adjust the path accordingly) to create the global ACL file with the following content: authenticated kxeilprwts kxeilprwts equals to lookup read write write-seen write-deleted insert post delete expunge create . You can use doveadm acl set -u user@domain \"Public/Develcow\" user=user@domain lookup read to limit access for a single user. You may also turn it around to limit access for all users to \"lr\" and grant only some users full access. See Dovecot ACL for further information about ACL.","title":"Allow authenticated users access to the whole public namespace"},{"location":"u_e-dovecot-static_master/","text":"Random master usernames and passwords are automatically created on every restart of dovecot-mailcow. That's recommended and should not be changed. If you need the user to be static anyway, please specify two variables in mailcow.conf . Both parameters must not be empty! DOVECOT_MASTER_USER=mymasteruser DOVECOT_MASTER_PASS=mysecretpass Run docker-compose up -d to apply your changes. The static master username will be expanded to DOVECOT_MASTER_USER@mailcow.local . To login as test@example.org this would equal to test@example.org*mymasteruser@mailcow.local with the specified password above. A login to SOGo is not possible with this username. A click-to-login function for SOGo is available for admins as described here No master user is required.","title":"Static master user"},{"location":"u_e-dovecot-vmail-volume/","text":"The \"new\" way \u00b6 WARNING : Newer Docker versions seem to complain about existing volumes. You can fix this temporarily by removing the existing volume and start mailcow with the override file. But it seems to be problematic after a reboot (needs to be confirmed). An easy, dirty, yet stable workaround is to stop mailcow ( docker-compose down ), remove /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data and create a new link to your remote filesystem location, for example: mv /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data_backup ln -s /mnt/volume-xy/vmail_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data Start mailcow afterwards. The \"old\" way \u00b6 If you want to use another folder for the vmail-volume, you can create a docker-compose.override.yml file and add the following content: version: '2.1' volumes: vmail-vol-1: driver_opts: type: none device: /data/mailcow/vmail o: bind Moving an existing vmail folder: \u00b6 Locate the current vmail folder by its \"Mountpoint\" attribute: docker volume inspect mailcowdockerized_vmail-vol-1 [ { \"CreatedAt\": \"2019-06-16T22:08:34+02:00\", \"Driver\": \"local\", \"Labels\": { \"com.docker.compose.project\": \"mailcowdockerized\", \"com.docker.compose.version\": \"1.23.2\", \"com.docker.compose.volume\": \"vmail-vol-1\" }, \"Mountpoint\": \"/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data\", \"Name\": \"mailcowdockerized_vmail-vol-1\", \"Options\": null, \"Scope\": \"local\" } ] Copy the content of the Mountpoint folder to the new location (e.g. /data/mailcow/vmail ) using cp -a , rsync -a or a similar non strcuture breaking copy command Stop mailcow by executing docker-compose down from within your mailcow root folder (e.g. /opt/mailcow-dockerized ) Create the file docker-compose.override.yml , edit the device path accordingly Delete the current vmail folder: docker volume rm mailcowdockerized_vmail-vol-1 Start mailcow by executing docker-compose up -d from within your mailcow root folder (e.g. /opt/mailcow-dockerized )","title":"Move Maildir (vmail)"},{"location":"u_e-dovecot-vmail-volume/#the-new-way","text":"WARNING : Newer Docker versions seem to complain about existing volumes. You can fix this temporarily by removing the existing volume and start mailcow with the override file. But it seems to be problematic after a reboot (needs to be confirmed). An easy, dirty, yet stable workaround is to stop mailcow ( docker-compose down ), remove /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data and create a new link to your remote filesystem location, for example: mv /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data_backup ln -s /mnt/volume-xy/vmail_data /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data Start mailcow afterwards.","title":"The \"new\" way"},{"location":"u_e-dovecot-vmail-volume/#the-old-way","text":"If you want to use another folder for the vmail-volume, you can create a docker-compose.override.yml file and add the following content: version: '2.1' volumes: vmail-vol-1: driver_opts: type: none device: /data/mailcow/vmail o: bind","title":"The \"old\" way"},{"location":"u_e-dovecot-vmail-volume/#moving-an-existing-vmail-folder","text":"Locate the current vmail folder by its \"Mountpoint\" attribute: docker volume inspect mailcowdockerized_vmail-vol-1 [ { \"CreatedAt\": \"2019-06-16T22:08:34+02:00\", \"Driver\": \"local\", \"Labels\": { \"com.docker.compose.project\": \"mailcowdockerized\", \"com.docker.compose.version\": \"1.23.2\", \"com.docker.compose.volume\": \"vmail-vol-1\" }, \"Mountpoint\": \"/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data\", \"Name\": \"mailcowdockerized_vmail-vol-1\", \"Options\": null, \"Scope\": \"local\" } ] Copy the content of the Mountpoint folder to the new location (e.g. /data/mailcow/vmail ) using cp -a , rsync -a or a similar non strcuture breaking copy command Stop mailcow by executing docker-compose down from within your mailcow root folder (e.g. /opt/mailcow-dockerized ) Create the file docker-compose.override.yml , edit the device path accordingly Delete the current vmail folder: docker volume rm mailcowdockerized_vmail-vol-1 Start mailcow by executing docker-compose up -d from within your mailcow root folder (e.g. /opt/mailcow-dockerized )","title":"Moving an existing vmail folder:"},{"location":"u_e-fido2/","text":"How is UV handled in mailcow? \u00b6 The UV flag (as in \"user verification\") enforces WebAuthn to verify the user before it allows access to the key (think of a PIN). We don't enforce UV to allow logins via iOS and NFC (YubiKey). Login and key processing \u00b6 mailcow uses client-side key processing . We ask the authenticator (i.e. YubiKey) to save the registration in its memory. A user does not need to enter a username. The available credentials - if any - will be shown to the user when selecting the \"key login\" via mailcow UI login. When calling the login process, the authenticator is not given any credential IDs. This will force it to lookup credentials in its own memory. Who can use WebAuthn to login to mailcow? \u00b6 As of today, only administrators and domain administrators are able to setup WebAuthn/FIDO2.","title":"WebAuthn / FIDO2"},{"location":"u_e-fido2/#how-is-uv-handled-in-mailcow","text":"The UV flag (as in \"user verification\") enforces WebAuthn to verify the user before it allows access to the key (think of a PIN). We don't enforce UV to allow logins via iOS and NFC (YubiKey).","title":"How is UV handled in mailcow?"},{"location":"u_e-fido2/#login-and-key-processing","text":"mailcow uses client-side key processing . We ask the authenticator (i.e. YubiKey) to save the registration in its memory. A user does not need to enter a username. The available credentials - if any - will be shown to the user when selecting the \"key login\" via mailcow UI login. When calling the login process, the authenticator is not given any credential IDs. This will force it to lookup credentials in its own memory.","title":"Login and key processing"},{"location":"u_e-fido2/#who-can-use-webauthn-to-login-to-mailcow","text":"As of today, only administrators and domain administrators are able to setup WebAuthn/FIDO2.","title":"Who can use WebAuthn to login to mailcow?"},{"location":"u_e-mailcow_ui-bl_wl/","text":"To add or edit an entry to your domain wide filter table, login to your mailcow UI as (domain) administrator. Info Be aware that a user may override this setting by setting his own black- and whitelist! There is also a global filter table in /admin to configure a server-wide filter for multiple Regex maps (Todo: Screenshots).","title":"Blacklist / Whitelist"},{"location":"u_e-mailcow_ui-config/","text":"Several configuration parameters of the mailcow UI can be changed by creating a file data/web/inc/vars.local.inc.php which overrides defaults settings found in data/web/inc/vars.inc.php . The local configuration file is persistent over updates of mailcow. Try not to change values inside data/web/inc/vars.inc.php , but use them as template for the local override. mailcow UI configuration parameters can be used to... ...change the default language 1 ...change the default bootstrap theme ...set a password complexity regex ...enable DKIM private key visibility ...set a pagination trigger size ...set default mailbox attributes ...change session lifetimes ...create fixed app menus (which cannot be changed in mailcow UI) ...set a default \"To\" field for relayhost tests ...set a timeout for Docker API requests ...toggle IP anonymization To change SOGos default language, you will need to edit data/conf/sogo/sogo.conf and replace \"English\" by your preferred language. \u21a9","title":"Configuration"},{"location":"u_e-mailcow_ui-css/","text":"For custom overrides of specific elements via CSS, use data/web/css/build/0081-custom-mailcow.css . The file is excluded from tracking and persists over updates.","title":"CSS overrides"},{"location":"u_e-mailcow_ui-pushover/","text":"Info Pushover makes it easy to get real-time notifications on your Android, iPhone, iPad, and Desktop You can use Pushover to get a push notification on every mail you receive for each mailbox where you enabled this feature. 1. As admin open your mailbox' settings and scroll down to the Pushover settings 2. Register yourself on Pushover 3. Put your 'User Key' in the 'User/Group Key' field in your mailbox settings 4. Create an Applications to get the API Token/Key which you also need to put in your mailbox settings 5. Optional you can edit the notification title/text and define certain sender email addresses where a push notification is triggered 6. Save everything and then you can verify your credentials If everything is done you can test sending a mail and you will receive a push message on your phone","title":"Pushover"},{"location":"u_e-mailcow_ui-spamalias/","text":"These temporary email aliases are mostly used for places where we need to provide an email address but don't want future correspondence with. They are also called spam alias. To create, delete or extend a temporary email aliases you need to login to mailcow's UI as a mailbox user and navigate to the tab Temporary email aliases :","title":"Temporary email aliases"},{"location":"u_e-mailcow_ui-spamfilter/","text":"A mailbox user may adjust the spam filter and black- / whitelist settings for his mailbox individually by navigating to the Spam filter tab in the users mailcow UI. Info For global adjustments on your spam filter please check our section on Rspamd . For a domain wide black- and whitelist please check our guide on Black / Whitelist","title":"Spamfilter"},{"location":"u_e-mailcow_ui-tagging/","text":"Mailbox users can tag their mail address like in me+facebook@example.org . They can control the tag handling in the users mailcow UI panel. Tagging is also known as 'sub-addressing' (RFC 5233) or 'plus addressing' Available Actions \u00b6 1. Move this message to a sub folder \"facebook\" (will be created lower case if not existing) 2. Prepend the tag to the subject: \"[facebook] Subject\" Please note: Uppercase tags are converted to lowercase except for the first letter. If you want to keep the tag as it is, please apply the following diff and restart mailcow: diff --git a/data/conf/dovecot/global_sieve_after b/data/conf/dovecot/global_sieve_after index e047136e..933c4137 100644 --- a/data/conf/dovecot/global_sieve_after +++ b/data/conf/dovecot/global_sieve_after @@ -15,7 +15,7 @@ if allof ( envelope :detail :matches \"to\" \"*\", header :contains \"X-Moo-Tag\" \"YES\" ) { - set :lower :upperfirst \"tag\" \"${1}\"; + set \"tag\" \"${1}\"; if mailboxexists \"INBOX/${1}\" { fileinto \"INBOX/${1}\"; } else {","title":"Tagging"},{"location":"u_e-mailcow_ui-tagging/#available-actions","text":"1. Move this message to a sub folder \"facebook\" (will be created lower case if not existing) 2. Prepend the tag to the subject: \"[facebook] Subject\" Please note: Uppercase tags are converted to lowercase except for the first letter. If you want to keep the tag as it is, please apply the following diff and restart mailcow: diff --git a/data/conf/dovecot/global_sieve_after b/data/conf/dovecot/global_sieve_after index e047136e..933c4137 100644 --- a/data/conf/dovecot/global_sieve_after +++ b/data/conf/dovecot/global_sieve_after @@ -15,7 +15,7 @@ if allof ( envelope :detail :matches \"to\" \"*\", header :contains \"X-Moo-Tag\" \"YES\" ) { - set :lower :upperfirst \"tag\" \"${1}\"; + set \"tag\" \"${1}\"; if mailboxexists \"INBOX/${1}\" { fileinto \"INBOX/${1}\"; } else {","title":"Available Actions"},{"location":"u_e-mailcow_ui-tfa/","text":"So far three methods for Two-Factor Authentication are implemented: U2F, Yubi OTP, and TOTP For U2F to work, you need an encrypted connection to the server (HTTPS) as well as a FIDO security key. Both U2F and Yubi OTP work well with the fantastic Yubikey . While Yubi OTP needs an active internet connection and an API ID + key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS. U2F and Yubi OTP support multiple keys per user. As the third TFA method mailcow uses TOTP: time-based one-time passwords. Those passwords can be generated with apps like \"Google Authenticator\" after initially scanning a QR code or entering the given secret manually. As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in. The key used to login will be displayed in green, while other keys remain grey. Information on how to remove 2FA can be found here . Yubi OTP \u00b6 The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret. U2F \u00b6 To use U2F, the browser must support this standard. The following desktop browsers support this authentication type: Edge (>=79) Firefox (>=47, enabled by default since version 67) Chrome (>=41) Safari (>=13) Opera (40, >=42, not 41) The following mobile browsers support this authentication type: Safari on iOS (>=13.3) Firefox on Android (>=68) Sources: caniuse.com , blog.mozilla.org U2F works without an internet connection. TOTP \u00b6 The best known TFA method mostly used with a smartphone.","title":"Two-Factor Authentication"},{"location":"u_e-mailcow_ui-tfa/#yubi-otp","text":"The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.","title":"Yubi OTP"},{"location":"u_e-mailcow_ui-tfa/#u2f","text":"To use U2F, the browser must support this standard. The following desktop browsers support this authentication type: Edge (>=79) Firefox (>=47, enabled by default since version 67) Chrome (>=41) Safari (>=13) Opera (40, >=42, not 41) The following mobile browsers support this authentication type: Safari on iOS (>=13.3) Firefox on Android (>=68) Sources: caniuse.com , blog.mozilla.org U2F works without an internet connection.","title":"U2F"},{"location":"u_e-mailcow_ui-tfa/#totp","text":"The best known TFA method mostly used with a smartphone.","title":"TOTP"},{"location":"u_e-nginx/","text":"SSL \u00b6 Please see Advanced SSL and explicitly check ADDITIONAL_SERVER_NAMES for SSL configuration. Please do not add ADDITIONAL_SERVER_NAMES when you plan to use a different web root. New site \u00b6 To create persistent (over updates) sites hosted by mailcow: dockerized, a new site configuration must be placed inside data/conf/nginx/ : A good template to begin with: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; # Location: data/web root /web; # Location: data/web/mysite.com #root /web/mysite.com include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name mysite.example.org; server_tokens off; # This allows acme to be validated even with a different web root location ^~ /.well-known/acme-challenge/ { default_type \"text/plain\"; rewrite /.well-known/acme-challenge/(.*) /$1 break; root /web/.well-known/acme-challenge/; } if ($scheme = http) { return 301 https://$server_name$request_uri; } } New site with proxy to a remote location \u00b6 Another example with a reverse proxy configuration: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name example.domain.tld; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } if ($scheme = http) { return 301 https://$host$request_uri; } location / { proxy_pass http://service:3000/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } } Config expansion in mailcows Nginx \u00b6 The filename used for a new site is not important, as long as the filename carries a .conf extension. It is also possible to extend the configuration of the default file site.conf file: nano data/conf/nginx/site.my_content.custom This filename does not need to have a \".conf\" extension but follows the pattern site.*.custom , where * is a custom name. If PHP is to be included in a custom site, please use the PHP-FPM listener on phpfpm:9002 or create a new listener in data/conf/phpfpm/php-fpm.d/pools.conf . Restart Nginx (and PHP-FPM, if a new listener was created): docker-compose restart nginx-mailcow docker-compose restart php-fpm-mailcow","title":"Custom sites"},{"location":"u_e-nginx/#ssl","text":"Please see Advanced SSL and explicitly check ADDITIONAL_SERVER_NAMES for SSL configuration. Please do not add ADDITIONAL_SERVER_NAMES when you plan to use a different web root.","title":"SSL"},{"location":"u_e-nginx/#new-site","text":"To create persistent (over updates) sites hosted by mailcow: dockerized, a new site configuration must be placed inside data/conf/nginx/ : A good template to begin with: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; # Location: data/web root /web; # Location: data/web/mysite.com #root /web/mysite.com include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name mysite.example.org; server_tokens off; # This allows acme to be validated even with a different web root location ^~ /.well-known/acme-challenge/ { default_type \"text/plain\"; rewrite /.well-known/acme-challenge/(.*) /$1 break; root /web/.well-known/acme-challenge/; } if ($scheme = http) { return 301 https://$server_name$request_uri; } }","title":"New site"},{"location":"u_e-nginx/#new-site-with-proxy-to-a-remote-location","text":"Another example with a reverse proxy configuration: nano data/conf/nginx/my_custom_site.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name example.domain.tld; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } if ($scheme = http) { return 301 https://$host$request_uri; } location / { proxy_pass http://service:3000/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 0; } }","title":"New site with proxy to a remote location"},{"location":"u_e-nginx/#config-expansion-in-mailcows-nginx","text":"The filename used for a new site is not important, as long as the filename carries a .conf extension. It is also possible to extend the configuration of the default file site.conf file: nano data/conf/nginx/site.my_content.custom This filename does not need to have a \".conf\" extension but follows the pattern site.*.custom , where * is a custom name. If PHP is to be included in a custom site, please use the PHP-FPM listener on phpfpm:9002 or create a new listener in data/conf/phpfpm/php-fpm.d/pools.conf . Restart Nginx (and PHP-FPM, if a new listener was created): docker-compose restart nginx-mailcow docker-compose restart php-fpm-mailcow","title":"Config expansion in mailcows Nginx"},{"location":"u_e-postfix-attachment_size/","text":"Open data/conf/postfix/extra.cf and set the message_size_limit accordingly in bytes. See main.cf for the default value. Restart Postfix: docker-compose restart postfix-mailcow","title":"Max. message size (attachment size)"},{"location":"u_e-postfix-custom_transport/","text":"For transport maps other than those to be configured in mailcow UI, please use data/conf/postfix/custom_transport.pcre to prevent existing maps or settings from being overwritten by updates. In most cases using this file is not necessary. Please make sure mailcow UI is not able to route your desired traffic properly before using that file. The file needs valid PCRE content and can break Postfix, if configured incorrectly.","title":"Custom transport maps"},{"location":"u_e-postfix-disable_sender_verification/","text":"New guide \u00b6 Edit a mailbox and select \"Allow to send as *\". For historical reasons we kept the old and deprecated guide below: Deprecated guide (DO NOT USE ON NEWER MAILCOWS!) \u00b6 This option is not best-practice and should only be implemented when there is no other option available to achieve whatever you are trying to do. Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail. user-to-allow-everything@example.com OK Open data/conf/postfix/main.cf and find smtpd_sender_restrictions . Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this: smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...] Run postmap on check_sasl_access: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access Restart the Postfix container.","title":"Disable Sender Addresses Verification"},{"location":"u_e-postfix-disable_sender_verification/#new-guide","text":"Edit a mailbox and select \"Allow to send as *\". For historical reasons we kept the old and deprecated guide below:","title":"New guide"},{"location":"u_e-postfix-disable_sender_verification/#deprecated-guide-do-not-use-on-newer-mailcows","text":"This option is not best-practice and should only be implemented when there is no other option available to achieve whatever you are trying to do. Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail. user-to-allow-everything@example.com OK Open data/conf/postfix/main.cf and find smtpd_sender_restrictions . Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this: smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...] Run postmap on check_sasl_access: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access Restart the Postfix container.","title":"Deprecated guide (DO NOT USE ON NEWER MAILCOWS!)"},{"location":"u_e-postfix-extra_cf/","text":"Please create a new file data/conf/postfix/extra.cf for overrides or additional content to main.cf . Postfix will complain about duplicate values once after starting postfix-mailcow, this is intended. Syslog-ng was configured to hide those warnings while Postfix is running, to not spam the log files with unnecessary information every time a service is used. Restart postfix-mailcow to apply your changes: docker-compose restart postfix-mailcow","title":"Customize/Expand main.cf"},{"location":"u_e-postfix-pflogsumm/","text":"To use pflogsumm with the default logging driver, we need to query postfix-mailcow via docker logs and pipe the output to pflogsumm: docker logs --since 24h $(docker ps -qf name=postfix-mailcow) | pflogsumm The above log output is limited to the past 24 hours. It's also possible to create a daily pflogsumm report via cron. Create the file /etc/cron.d/pflogsumm with the following content: SHELL=/bin/bash 59 23 * * * root docker logs --since 24h $(docker ps -qf name=postfix-mailcow) | /usr/sbin/pflogsumm -d today | mail -s \"Postfix Report of $(date)\" postmaster@example.net Based on the last 24h postfix logs, this example sends every day at 23:59:00 a pflogsumm report to postmaster@example.net .","title":"Statistics with pflogsumm"},{"location":"u_e-postfix-postscreen_whitelist/","text":"IPs can be removed from Postscreen and therefore also from RBL checks in data/conf/postfix/custom_postscreen_whitelist.cidr . Postscreen does multiple checks to identify malicious senders. In most cases you want to whitelist an IP to exclude it from blacklist lookups. The format of the file is as follows: CIDR ACTION Where CIDR is a single IP address or IP range in CIDR notation, and action is either \"permit\" or \"reject\". Example: # Rules are evaluated in the order as specified. # Blacklist 192.168.* except 192.168.0.1. 192.168.0.1 permit 192.168.0.0/16 reject The file is reloaded on the fly, postfix restart is not required.","title":"Whitelist IP in Postscreen"},{"location":"u_e-postfix-relayhost/","text":"As of September 12, 2018 you can setup relayhosts as admin by using the mailcow UI. This is useful if you want to relay outgoing emails for a specific domain to a third-party spam filter or a service like Mailgun or Sendgrid. This is also known as a smarthost . Add a new relayhost \u00b6 Go to the Routing tab of the Configuration and Details section of the admin UI. Here you will see a list of relayhosts currently setup. Scroll to the Add sender-dependent transport section. Under Host , add the host you want to relay to. Example: if you want to use Mailgun to send emails instead of your server IP, enter smtp.mailgun.org If the relay host requires a username and password to authenticate, enter them in the respective fields. Keep in mind the credentials will be stored in plain text. Test a relayhost \u00b6 To test that connectivity to the host works, click on Test from the list of relayhosts and enter a From: address. Then, run the test. You will then see the results of the SMTP transmission. If all went well, you should see SERVER -> CLIENT: 250 2.0.0 Ok: queued as A093B401D4 as one of the last lines. If not, review the error provided and resolve it. Note: Some hosts, especially those who do not require authentication, will deny connections from servers that have not been added to their system beforehand. Make sure you read the documentation of the relayhost to make sure you've added your domain and/or the server IP to their system. Tip: You can change the default test To: address the test uses from null@mailcow.email to any email address you choose by modifying the $RELAY_TO variable on the vars.inc.php file under /opt/mailcow-dockerized/data/web/inc This way you can check that the relay worked by checking the destination mailbox. Set the relayhost for a domain \u00b6 Go to the Domains tab of the Mail setup section of the admin UI. Edit the desired domain. Select the newly added host on the Sender-dependent transports dropdown and save changes. Send an email from a mailbox on that domain and you should see postfix handing the message over to the relayhost in the logs.","title":"Relayhosts"},{"location":"u_e-postfix-relayhost/#add-a-new-relayhost","text":"Go to the Routing tab of the Configuration and Details section of the admin UI. Here you will see a list of relayhosts currently setup. Scroll to the Add sender-dependent transport section. Under Host , add the host you want to relay to. Example: if you want to use Mailgun to send emails instead of your server IP, enter smtp.mailgun.org If the relay host requires a username and password to authenticate, enter them in the respective fields. Keep in mind the credentials will be stored in plain text.","title":"Add a new relayhost"},{"location":"u_e-postfix-relayhost/#test-a-relayhost","text":"To test that connectivity to the host works, click on Test from the list of relayhosts and enter a From: address. Then, run the test. You will then see the results of the SMTP transmission. If all went well, you should see SERVER -> CLIENT: 250 2.0.0 Ok: queued as A093B401D4 as one of the last lines. If not, review the error provided and resolve it. Note: Some hosts, especially those who do not require authentication, will deny connections from servers that have not been added to their system beforehand. Make sure you read the documentation of the relayhost to make sure you've added your domain and/or the server IP to their system. Tip: You can change the default test To: address the test uses from null@mailcow.email to any email address you choose by modifying the $RELAY_TO variable on the vars.inc.php file under /opt/mailcow-dockerized/data/web/inc This way you can check that the relay worked by checking the destination mailbox.","title":"Test a relayhost"},{"location":"u_e-postfix-relayhost/#set-the-relayhost-for-a-domain","text":"Go to the Domains tab of the Mail setup section of the admin UI. Edit the desired domain. Select the newly added host on the Sender-dependent transports dropdown and save changes. Send an email from a mailbox on that domain and you should see postfix handing the message over to the relayhost in the logs.","title":"Set the relayhost for a domain"},{"location":"u_e-postfix-trust_networks/","text":"By default mailcow considers all networks as untrusted excluding its own IPV4_NETWORK and IPV6_NETWORK scopes. Though it is reasonable in most cases, there may be circumstances that you need to loosen this restriction. By default mailcow uses mynetworks_style = subnet to determine internal subnets and leaves mynetworks unconfigured. If you decide to set mynetworks , Postfix ignores the mynetworks_style setting. This means you have to add the IPV4_NETWORK and IPV6_NETWORK scopes as well as loopback subnets manually! Unauthenticated relaying \u00b6 Warning Incorrect setup of mynetworks will allow your server to be used as an open relay. If abused, this will affect your ability to send emails and can take some time to be resolved. IPv4 hosts/subnets \u00b6 To add the subnet 192.168.2.0/24 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 192.168.2.0/24 Run docker-compose restart postfix-mailcow to apply your new settings. IPv6 hosts/subnets \u00b6 Adding IPv6 hosts is done the same as IPv4, however the subnet needs to be placed in brackets [] with the netmask appended. To add the subnet 2001:db8::/32 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 [2001:db8::]/32 Run docker-compose restart postfix-mailcow to apply your new settings. Info More information about mynetworks can be found in the Postfix documentation .","title":"Add trusted networks"},{"location":"u_e-postfix-trust_networks/#unauthenticated-relaying","text":"Warning Incorrect setup of mynetworks will allow your server to be used as an open relay. If abused, this will affect your ability to send emails and can take some time to be resolved.","title":"Unauthenticated relaying"},{"location":"u_e-postfix-trust_networks/#ipv4-hostssubnets","text":"To add the subnet 192.168.2.0/24 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 192.168.2.0/24 Run docker-compose restart postfix-mailcow to apply your new settings.","title":"IPv4 hosts/subnets"},{"location":"u_e-postfix-trust_networks/#ipv6-hostssubnets","text":"Adding IPv6 hosts is done the same as IPv4, however the subnet needs to be placed in brackets [] with the netmask appended. To add the subnet 2001:db8::/32 to the trusted networks you may use the following configuration, depending on your IPV4_NETWORK and IPV6_NETWORK scopes: Edit data/conf/postfix/extra.cf : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/10 172.22.1.0/24 [fd4d:6169:6c63:6f77::]/64 [2001:db8::]/32 Run docker-compose restart postfix-mailcow to apply your new settings. Info More information about mynetworks can be found in the Postfix documentation .","title":"IPv6 hosts/subnets"},{"location":"u_e-redis/","text":"Redis is used as a key-value store for rspamd's and (some of) mailcow's settings and data. If you are unfamiliar with redis please read the introduction to redis and maybe visit this wonderful guide on how to use it. Client \u00b6 To connect to the redis cli execute: docker-compose exec redis-mailcow redis-cli Debugging \u00b6 Here are some useful commands for the redis-cli for debugging: MONITOR \u00b6 Listens for all requests received by the server in real time: # docker-compose exec redis-mailcow redis-cli 127.0.0.1:6379> monitor OK 1494077286.401963 [0 172.22.1.253:41228] \"SMEMBERS\" \"BAYES_SPAM_keys\" 1494077288.292970 [0 172.22.1.253:41229] \"SMEMBERS\" \"BAYES_SPAM_keys\" [...] KEYS \u00b6 Get all keys matching your pattern: KEYS * PING \u00b6 Test a connection: 127.0.0.1:6379> PING PONG If you want to know more, here is a cheat sheet .","title":"Redis"},{"location":"u_e-redis/#client","text":"To connect to the redis cli execute: docker-compose exec redis-mailcow redis-cli","title":"Client"},{"location":"u_e-redis/#debugging","text":"Here are some useful commands for the redis-cli for debugging:","title":"Debugging"},{"location":"u_e-redis/#monitor","text":"Listens for all requests received by the server in real time: # docker-compose exec redis-mailcow redis-cli 127.0.0.1:6379> monitor OK 1494077286.401963 [0 172.22.1.253:41228] \"SMEMBERS\" \"BAYES_SPAM_keys\" 1494077288.292970 [0 172.22.1.253:41229] \"SMEMBERS\" \"BAYES_SPAM_keys\" [...]","title":"MONITOR"},{"location":"u_e-redis/#keys","text":"Get all keys matching your pattern: KEYS *","title":"KEYS"},{"location":"u_e-redis/#ping","text":"Test a connection: 127.0.0.1:6379> PING PONG If you want to know more, here is a cheat sheet .","title":"PING"},{"location":"u_e-reeanble-weak-protocols/","text":"On February the 12th 2020 we disabled the deprecated protocols TLS 1.0 and 1.1 in Dovecot (POP3, POP3S, IMAP, IMAPS) and Postfix (SMTPS, SUBMISSION). Unauthenticated mail via SMTP on port 25/tcp does still accept >= TLS 1.0 . It is better to accept a weak encryption than none at all. How to re-enable weak protocols? Edit data/conf/postfix/extra.cf : submission_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 Edit data/conf/dovecot/extra.conf : ssl_min_protocol = TLSv1 Restart the affected services: docker-compose restart postfix-mailcow dovecot-mailcow Hint: You can enable TLS 1.2 in Windows 7.","title":"Re-enable TLS 1.0 and TLS 1.1"},{"location":"u_e-rspamd/","text":"Rspamd is used for AV handling, DKIM signing and SPAM handling. It's a powerful and fast filter system. For a more in-depth documentation on Rspamd please visit its own documentation . Learn Spam & Ham \u00b6 Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash. This is achieved by using the Sieve plugin \"sieve_imapsieve\" and parser scripts. Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning ). We configured the plugin to keep a sane ratio between spam and ham learns. The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM . Besides bayes, a local fuzzy storage is used to learn recurring patterns in text or images that indicate ham or spam. You can also use Rspamd's web UI to learn ham and / or spam or to adjust certain settings of Rspamd. Learn Spam or Ham from existing directory \u00b6 You can use a one-liner to learn mail in plain-text (uncompressed) format: # Ham for file in /my/folder/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_ham < $file ; done # Spam for file in /my/folder/.Junk/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_spam < $file ; done Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example: for file in /data/old_mail/.Junk/cur/* ; do rspamc learn_spam < zcat $file ; done Reset learned data (Bayes, Neural) \u00b6 You need to delete keys in Redis to reset learned data, so create a copy of your Redis database now: Backup database # It is better to stop Redis before you copy the file. cp /var/lib/docker/volumes/mailcowdockerized_redis-vol-1/_data/dump.rdb /root/ Reset Bayes data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern BAYES_* | xargs redis-cli del' docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern RS* | xargs redis-cli del' Reset Neural data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern rn_* | xargs redis-cli del' Reset Fuzzy data # We need to enter the redis-cli first: docker-compose exec redis-mailcow redis-cli # In redis-cli: 127 .0.0.1:6379> EVAL \"for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end\" 0 fuzzy* Info If redis-cli complains about... (error) ERR wrong number of arguments for 'del' command ...the key pattern was not found and thus no data is available to delete - it is fine. CLI tools \u00b6 docker-compose exec rspamd-mailcow rspamc --help docker-compose exec rspamd-mailcow rspamadm --help Disable Greylisting \u00b6 Only messages with a higher score will be considered to be greylisted (soft rejected). It is bad practice to disable greylisting. You can disable greylisting server-wide by editing: {mailcow-dir}/data/conf/rspamd/local.d/greylist.conf Add the line: enabled = false ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Spam filter thresholds (global) \u00b6 Each user is able to change their spam rating individually . To define a new server-wide limit, edit data/conf/rspamd/local.d/actions.conf : reject = 15 ; add_header = 8 ; greylist = 7 ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Existing settings of users will not be overwritten! To reset custom defined thresholds, run: source mailcow.conf docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel';\" # or: # docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel' and object = 'only-this-mailbox@example.org';\" Custom reject messages \u00b6 The default spam reject message can be changed by adding a new file data/conf/rspamd/override.d/worker-proxy.custom.inc with the following content: reject_message = \"My custom reject message\"; Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . While the above works for rejected mails with a high spam score, prefilter reject actions will ignore this setting. For these maps, the multimap module in Rspamd needs to be adjusted: Find prefilet reject symbol for which you want change message, to do it run: grep -R \"SYMBOL_YOU_WANT_TO_ADJUST\" /opt/mailcow-dockerized/data/conf/rspamd/ Add your custom message as new line: GLOBAL_RCPT_BL { type = \"rcpt\"; map = \"${LOCAL_CONFDIR}/custom/global_rcpt_blacklist.map\"; regexp = true; prefilter = true; action = \"reject\"; message = \"Sending mail to this recipient is prohibited by postmaster@your.domain\"; } Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . Whitelist specific ClamAV signatures \u00b6 You may find that legitimate (clean) mail is being blocked by ClamAV (Rspamd will flag the mail with VIRUS_FOUND ). For instance, interactive PDF form attachments are blocked by default because the embedded Javascript code may be used for nefarious purposes. Confirm by looking at the clamd logs, e.g.: docker-compose logs clamd-mailcow | grep \"FOUND\" This line confirms that such was identified: clamd-mailcow_1 | Sat Sep 28 07:43:24 2019 -> instream(local): PUA.Pdf.Trojan.EmbeddedJavaScript-1(e887d2ac324ce90750768b86b63d0749:363325) FOUND To whitelist this particular signature (and enable sending this type of file attached), add it to the ClamAV signature whitelist file: echo 'PUA.Pdf.Trojan.EmbeddedJavaScript-1' >> data/conf/clamav/whitelist.ign2 Then restart the clamd-mailcow service container in the mailcow UI or using docker-compose: docker-compose restart clamd-mailcow Cleanup cached ClamAV results in Redis: # docker-compose exec redis-mailcow /bin/sh /data # redis-cli KEYS rs_cl* | xargs redis-cli DEL /data # exit Discard instead of reject \u00b6 If you want to silently drop a message, create or edit the file data/conf/rspamd/override.d/worker-proxy.custom.inc and add the following content: discard_on_reject = true; Restart Rspamd: docker-compose restart rspamd-mailcow Wipe all ratelimit keys \u00b6 If you don't want to use the UI and instead wipe all keys in the Redis database, you can use redis-cli for that task: docker-compose exec redis-mailcow sh # Unlink (available in Redis >=4.) will delete in the backgronud redis-cli --scan --pattern RL* | xargs redis-cli unlink Restart Rspamd: docker-compose exec redis-mailcow sh Trigger a resend of quarantine notifications \u00b6 Should be used for debugging only! docker-compose exec dovecot-mailcow bash mysql -umailcow -p$DBPASS mailcow -e \"update quarantine set notified = 0;\" redis-cli -h redis DEL Q_LAST_NOTIFIED quarantine_notify.py Increase history retention \u00b6 By default Rspamd keeps 1000 elements in the history. The history is stored compressed. It is recommended not to use a disproportionate high value here, try something along 5000 or 10000 and see how your server handles it: Edit data/conf/rspamd/local.d/history_redis.conf : nrows = 1000; # change this value Restart Rspamd afterwards: docker-compose restart rspamd-mailcow","title":"Rspamd"},{"location":"u_e-rspamd/#learn-spam-ham","text":"Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash. This is achieved by using the Sieve plugin \"sieve_imapsieve\" and parser scripts. Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning ). We configured the plugin to keep a sane ratio between spam and ham learns. The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM . Besides bayes, a local fuzzy storage is used to learn recurring patterns in text or images that indicate ham or spam. You can also use Rspamd's web UI to learn ham and / or spam or to adjust certain settings of Rspamd.","title":"Learn Spam & Ham"},{"location":"u_e-rspamd/#learn-spam-or-ham-from-existing-directory","text":"You can use a one-liner to learn mail in plain-text (uncompressed) format: # Ham for file in /my/folder/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_ham < $file ; done # Spam for file in /my/folder/.Junk/cur/* ; do docker exec -i $( docker-compose ps -q rspamd-mailcow ) rspamc learn_spam < $file ; done Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example: for file in /data/old_mail/.Junk/cur/* ; do rspamc learn_spam < zcat $file ; done","title":"Learn Spam or Ham from existing directory"},{"location":"u_e-rspamd/#reset-learned-data-bayes-neural","text":"You need to delete keys in Redis to reset learned data, so create a copy of your Redis database now: Backup database # It is better to stop Redis before you copy the file. cp /var/lib/docker/volumes/mailcowdockerized_redis-vol-1/_data/dump.rdb /root/ Reset Bayes data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern BAYES_* | xargs redis-cli del' docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern RS* | xargs redis-cli del' Reset Neural data docker-compose exec redis-mailcow sh -c 'redis-cli --scan --pattern rn_* | xargs redis-cli del' Reset Fuzzy data # We need to enter the redis-cli first: docker-compose exec redis-mailcow redis-cli # In redis-cli: 127 .0.0.1:6379> EVAL \"for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end\" 0 fuzzy* Info If redis-cli complains about... (error) ERR wrong number of arguments for 'del' command ...the key pattern was not found and thus no data is available to delete - it is fine.","title":"Reset learned data (Bayes, Neural)"},{"location":"u_e-rspamd/#cli-tools","text":"docker-compose exec rspamd-mailcow rspamc --help docker-compose exec rspamd-mailcow rspamadm --help","title":"CLI tools"},{"location":"u_e-rspamd/#disable-greylisting","text":"Only messages with a higher score will be considered to be greylisted (soft rejected). It is bad practice to disable greylisting. You can disable greylisting server-wide by editing: {mailcow-dir}/data/conf/rspamd/local.d/greylist.conf Add the line: enabled = false ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow","title":"Disable Greylisting"},{"location":"u_e-rspamd/#spam-filter-thresholds-global","text":"Each user is able to change their spam rating individually . To define a new server-wide limit, edit data/conf/rspamd/local.d/actions.conf : reject = 15 ; add_header = 8 ; greylist = 7 ; Save the file and restart \"rspamd-mailcow\": docker-compose restart rspamd-mailcow Existing settings of users will not be overwritten! To reset custom defined thresholds, run: source mailcow.conf docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel';\" # or: # docker-compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e \"delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel' and object = 'only-this-mailbox@example.org';\"","title":"Spam filter thresholds (global)"},{"location":"u_e-rspamd/#custom-reject-messages","text":"The default spam reject message can be changed by adding a new file data/conf/rspamd/override.d/worker-proxy.custom.inc with the following content: reject_message = \"My custom reject message\"; Save the file and restart Rspamd: docker-compose restart rspamd-mailcow . While the above works for rejected mails with a high spam score, prefilter reject actions will ignore this setting. For these maps, the multimap module in Rspamd needs to be adjusted: Find prefilet reject symbol for which you want change message, to do it run: grep -R \"SYMBOL_YOU_WANT_TO_ADJUST\" /opt/mailcow-dockerized/data/conf/rspamd/ Add your custom message as new line: GLOBAL_RCPT_BL { type = \"rcpt\"; map = \"${LOCAL_CONFDIR}/custom/global_rcpt_blacklist.map\"; regexp = true; prefilter = true; action = \"reject\"; message = \"Sending mail to this recipient is prohibited by postmaster@your.domain\"; } Save the file and restart Rspamd: docker-compose restart rspamd-mailcow .","title":"Custom reject messages"},{"location":"u_e-rspamd/#whitelist-specific-clamav-signatures","text":"You may find that legitimate (clean) mail is being blocked by ClamAV (Rspamd will flag the mail with VIRUS_FOUND ). For instance, interactive PDF form attachments are blocked by default because the embedded Javascript code may be used for nefarious purposes. Confirm by looking at the clamd logs, e.g.: docker-compose logs clamd-mailcow | grep \"FOUND\" This line confirms that such was identified: clamd-mailcow_1 | Sat Sep 28 07:43:24 2019 -> instream(local): PUA.Pdf.Trojan.EmbeddedJavaScript-1(e887d2ac324ce90750768b86b63d0749:363325) FOUND To whitelist this particular signature (and enable sending this type of file attached), add it to the ClamAV signature whitelist file: echo 'PUA.Pdf.Trojan.EmbeddedJavaScript-1' >> data/conf/clamav/whitelist.ign2 Then restart the clamd-mailcow service container in the mailcow UI or using docker-compose: docker-compose restart clamd-mailcow Cleanup cached ClamAV results in Redis: # docker-compose exec redis-mailcow /bin/sh /data # redis-cli KEYS rs_cl* | xargs redis-cli DEL /data # exit","title":"Whitelist specific ClamAV signatures"},{"location":"u_e-rspamd/#discard-instead-of-reject","text":"If you want to silently drop a message, create or edit the file data/conf/rspamd/override.d/worker-proxy.custom.inc and add the following content: discard_on_reject = true; Restart Rspamd: docker-compose restart rspamd-mailcow","title":"Discard instead of reject"},{"location":"u_e-rspamd/#wipe-all-ratelimit-keys","text":"If you don't want to use the UI and instead wipe all keys in the Redis database, you can use redis-cli for that task: docker-compose exec redis-mailcow sh # Unlink (available in Redis >=4.) will delete in the backgronud redis-cli --scan --pattern RL* | xargs redis-cli unlink Restart Rspamd: docker-compose exec redis-mailcow sh","title":"Wipe all ratelimit keys"},{"location":"u_e-rspamd/#trigger-a-resend-of-quarantine-notifications","text":"Should be used for debugging only! docker-compose exec dovecot-mailcow bash mysql -umailcow -p$DBPASS mailcow -e \"update quarantine set notified = 0;\" redis-cli -h redis DEL Q_LAST_NOTIFIED quarantine_notify.py","title":"Trigger a resend of quarantine notifications"},{"location":"u_e-rspamd/#increase-history-retention","text":"By default Rspamd keeps 1000 elements in the history. The history is stored compressed. It is recommended not to use a disproportionate high value here, try something along 5000 or 10000 and see how your server handles it: Edit data/conf/rspamd/local.d/history_redis.conf : nrows = 1000; # change this value Restart Rspamd afterwards: docker-compose restart rspamd-mailcow","title":"Increase history retention"},{"location":"u_e-sogo/","text":"SOGo is used for accessing your mails via a webbrowser, adding and sharing your contacts or calendars. For a more in-depth documentation on SOGo please visit its own documentation . Apply custom SOGo theme \u00b6 mailcow builds after 28 January 2021 can change SOGo's theme by editing data/conf/sogo/custom-theme.js . Please check the AngularJS Material intro and documentation as well as the material style guideline to learn how this works. You can use the provided custom-theme.js as an example starting point by removing the comments. After you modified data/conf/sogo/custom-theme.js and made changes to your new SOGo theme you need to edit data/conf/sogo/sogo.conf and append/set SOGoUIxDebugEnabled = YES; restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Reset to SOGo default theme \u00b6 Checkout data/conf/sogo/custom-theme.js by executing git fetch ; git checkout origin/master data/conf/sogo/custom-theme.js data/conf/sogo/custom-theme.js Find in data/conf/sogo/custom-theme.js : // Apply new palettes to the default theme, remap some of the hues $mdThemingProvider.theme('default') .primaryPalette('green-cow', { 'default': '400', // background color of top toolbars 'hue-1': '400', 'hue-2': '600', // background color of sidebar toolbar 'hue-3': 'A700' }) .accentPalette('green', { 'default': '600', // background color of fab buttons and login screen 'hue-1': '300', // background color of center list toolbar 'hue-2': '300', // highlight color for selected mail and current day calendar 'hue-3': 'A700' }) .backgroundPalette('frost-grey'); and replace with: $mdThemingProvider.theme('default'); Change favicon \u00b6 mailcow builds after 31 January 2021 can change SOGo's favicon by replacing data/conf/sogo/custom-favicon.ico for SOGo and data/web/favicon.png for mailcow UI. Note : You can use .png favicons for SOGo by renaming them to custom-favicon.ico . For both SOGo and mailcow UI favicons you need use one of the standard dimensions: 16x16, 32x32, 64x64, 128x128 and 256x256. After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Change logo \u00b6 mailcow builds after 21 December 2018 can change SOGo's logo by replacing or creating (if missing) data/conf/sogo/sogo-full.svg . After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow . Connect domains \u00b6 Domains are usually isolated from eachother. You can change that by modifying data/conf/sogo/sogo.conf : Search... // SOGoDomainsVisibility = ( // (domain1.tld, domain5.tld), // (domain3.tld, domain2.tld) // ); ...and replace it by - for example: SOGoDomainsVisibility = ( (example.org, example.com, example.net) ); Restart SOGo: docker-compose restart sogo-mailcow Disable password changing \u00b6 Edit data/conf/sogo/sogo.conf and change SOGoPasswordChangeEnabled to NO . Please do not add a new parameter. Run docker-compose restart memcached-mailcow sogo-mailcow to activate the changes.","title":"SOGo"},{"location":"u_e-sogo/#apply-custom-sogo-theme","text":"mailcow builds after 28 January 2021 can change SOGo's theme by editing data/conf/sogo/custom-theme.js . Please check the AngularJS Material intro and documentation as well as the material style guideline to learn how this works. You can use the provided custom-theme.js as an example starting point by removing the comments. After you modified data/conf/sogo/custom-theme.js and made changes to your new SOGo theme you need to edit data/conf/sogo/sogo.conf and append/set SOGoUIxDebugEnabled = YES; restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Apply custom SOGo theme"},{"location":"u_e-sogo/#reset-to-sogo-default-theme","text":"Checkout data/conf/sogo/custom-theme.js by executing git fetch ; git checkout origin/master data/conf/sogo/custom-theme.js data/conf/sogo/custom-theme.js Find in data/conf/sogo/custom-theme.js : // Apply new palettes to the default theme, remap some of the hues $mdThemingProvider.theme('default') .primaryPalette('green-cow', { 'default': '400', // background color of top toolbars 'hue-1': '400', 'hue-2': '600', // background color of sidebar toolbar 'hue-3': 'A700' }) .accentPalette('green', { 'default': '600', // background color of fab buttons and login screen 'hue-1': '300', // background color of center list toolbar 'hue-2': '300', // highlight color for selected mail and current day calendar 'hue-3': 'A700' }) .backgroundPalette('frost-grey'); and replace with: $mdThemingProvider.theme('default');","title":"Reset to SOGo default theme"},{"location":"u_e-sogo/#change-favicon","text":"mailcow builds after 31 January 2021 can change SOGo's favicon by replacing data/conf/sogo/custom-favicon.ico for SOGo and data/web/favicon.png for mailcow UI. Note : You can use .png favicons for SOGo by renaming them to custom-favicon.ico . For both SOGo and mailcow UI favicons you need use one of the standard dimensions: 16x16, 32x32, 64x64, 128x128 and 256x256. After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Change favicon"},{"location":"u_e-sogo/#change-logo","text":"mailcow builds after 21 December 2018 can change SOGo's logo by replacing or creating (if missing) data/conf/sogo/sogo-full.svg . After you replaced said file you need to restart SOGo and Memcached containers by executing docker-compose restart memcached-mailcow sogo-mailcow .","title":"Change logo"},{"location":"u_e-sogo/#connect-domains","text":"Domains are usually isolated from eachother. You can change that by modifying data/conf/sogo/sogo.conf : Search... // SOGoDomainsVisibility = ( // (domain1.tld, domain5.tld), // (domain3.tld, domain2.tld) // ); ...and replace it by - for example: SOGoDomainsVisibility = ( (example.org, example.com, example.net) ); Restart SOGo: docker-compose restart sogo-mailcow","title":"Connect domains"},{"location":"u_e-sogo/#disable-password-changing","text":"Edit data/conf/sogo/sogo.conf and change SOGoPasswordChangeEnabled to NO . Please do not add a new parameter. Run docker-compose restart memcached-mailcow sogo-mailcow to activate the changes.","title":"Disable password changing"},{"location":"u_e-unbound-fwd/","text":"If you want or have to use an external DNS service, you can either set a forwarder in Unbound or copy an override file to define external DNS servers: !!! warning Please do not use a public resolver like we did in the example above. Many - if not all - blacklist lookups will fail with public resolvers. Important : Only DNSSEC validating DNS services will work. Method A, Unbound \u00b6 Edit data/conf/unbound/unbound.conf and append the following parameters: forward-zone: name: \".\" forward-addr: 8.8.8.8 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE forward-addr: 8.8.4.4 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE Restart Unbound: docker-compose restart unbound-mailcow Method B, Override file \u00b6 cd /opt/mailcow-dockerized cp helper-scripts/docker-compose.override.yml.d/EXTERNAL_DNS/docker-compose.override.yml . Edit docker-compose.override.yml and adjust the IP. Run docker-compose down ; docker-compose up -d .","title":"Using an external DNS service"},{"location":"u_e-unbound-fwd/#method-a-unbound","text":"Edit data/conf/unbound/unbound.conf and append the following parameters: forward-zone: name: \".\" forward-addr: 8.8.8.8 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE forward-addr: 8.8.4.4 # DO NOT USE PUBLIC DNS SERVERS - JUST AN EXAMPLE Restart Unbound: docker-compose restart unbound-mailcow","title":"Method A, Unbound"},{"location":"u_e-unbound-fwd/#method-b-override-file","text":"cd /opt/mailcow-dockerized cp helper-scripts/docker-compose.override.yml.d/EXTERNAL_DNS/docker-compose.override.yml . Edit docker-compose.override.yml and adjust the IP. Run docker-compose down ; docker-compose up -d .","title":"Method B, Override file"},{"location":"u_e-update-hooks/","text":"It is possible to add pre- and post-update-hooks to the update.sh script that upgrades your whole mailcow installation. To do so, just add the corresponding bash script into your mailcows root directory: pre_update_hook.sh for commands that should run before the update post_uddate_hook.sh for commands that should run after the update is completed Keep in mind that pre_update_hook.sh runs every time you call update.sh and post_update_hook.sh will only run if the update was successful and the script doesn't have to be re-run. The scripts will be run by bash, an interpreter (e.g. #!/bin/bash ) as well as an execute permission flag (\"+x\") are not required.","title":"Run scripts before and after updates"},{"location":"u_e-webmail-site/","text":"IMPORTANT : This guide only applies to non SNI enabled configurations. The certificate path needs to be adjusted if SNI is enabled. Something like ssl_certificate,key /etc/ssl/mail/webmail.example.org/cert.pem,key.pem; will do. But : The certificate should be acquired first and only after the certificate exists a site config should be created. Nginx will fail to start if it cannot find the certificate and key. To create a subdomain webmail.example.org and redirect it to SOGo, you need to create a new Nginx site. Take care of \"CHANGE_TO_MAILCOW_HOSTNAME\"! nano data/conf/nginx/webmail.conf server { ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; index index.php index.html; client_max_body_size 0; root /web; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; server_name webmail.example.org; server_tokens off; location ^~ /.well-known/acme-challenge/ { allow all; default_type \"text/plain\"; } location / { return 301 https://CHANGE_TO_MAILCOW_HOSTNAME/SOGo; } } Save and restart Nginx: docker-compose restart nginx-mailcow . Now open mailcow.conf and find ADDITIONAL_SAN . Add webmail.example.org to this array, don't use quotes! ADDITIONAL_SAN=webmail.example.org Run docker-compose up -d . See \"acme-mailcow\" and \"nginx-mailcow\" logs if anything fails.","title":"Create subdomain webmail.example.org"},{"location":"u_e-why_unbound/","text":"For DNS blacklist lookups and DNSSEC. Most systems use either a public or a local caching DNS resolver. That's a very bad idea when it comes to filter spam using DNS-based black hole lists (DNSBL) or similar technics. Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service. Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.","title":"Why unbound?"},{"location":"client/client-android/","text":"Open the Email app. If this is your first email account, tap Add Account ; if not, tap More and Settings and then Add account . Select Microsoft Exchange ActiveSync . Enter your email address ( ) and password. Tap Sign in .","title":"Android"},{"location":"client/client-apple/","text":"Method 1 via Mobileconfig \u00b6 Email, contacts and calendars can be configured automatically on Apple devices by installing a profile. To download a profile you must login to the mailcow UI first. Method 1.1: IMAP, SMTP and Cal/CardDAV \u00b6 This method configures IMAP, CardDAV and CalDAV. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password three times when prompted. Method 1.2: IMAP, SMTP (no DAV) \u00b6 This method configures IMAP and SMTP only. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php?only_email mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password when prompted. Method 2 (Exchange ActiveSync emulation) \u00b6 On iOS, Exchange ActiveSync is also supported as an alternative to the procedure above. It has the advantage of supporting push email (i.e. you are immediately notified of incoming messages), but has some limitations, e.g. it does not support more than three email addresses per contact in your address book. Follow the steps below if you decide to use Exchange instead. Open the Settings app, tap Mail , tap Accounts , tap Add Acccount , select Exchange . Enter your email address ( ) and tap Next . Enter your password, tap Next again. Finally, tap Save .","title":"Apple macOS / iOS"},{"location":"client/client-apple/#method-1-via-mobileconfig","text":"Email, contacts and calendars can be configured automatically on Apple devices by installing a profile. To download a profile you must login to the mailcow UI first.","title":"Method 1 via Mobileconfig"},{"location":"client/client-apple/#method-11-imap-smtp-and-calcarddav","text":"This method configures IMAP, CardDAV and CalDAV. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password three times when prompted.","title":"Method 1.1: IMAP, SMTP and Cal/CardDAV"},{"location":"client/client-apple/#method-12-imap-smtp-no-dav","text":"This method configures IMAP and SMTP only. Download and open the file from https://${MAILCOW_HOSTNAME}/mobileconfig.php?only_email mailcow.mobileconfig . Enter the unlock code (iPhone) or computer password (Mac). Enter your email password when prompted.","title":"Method 1.2: IMAP, SMTP (no DAV)"},{"location":"client/client-apple/#method-2-exchange-activesync-emulation","text":"On iOS, Exchange ActiveSync is also supported as an alternative to the procedure above. It has the advantage of supporting push email (i.e. you are immediately notified of incoming messages), but has some limitations, e.g. it does not support more than three email addresses per contact in your address book. Follow the steps below if you decide to use Exchange instead. Open the Settings app, tap Mail , tap Accounts , tap Add Acccount , select Exchange . Enter your email address ( ) and tap Next . Enter your password, tap Next again. Finally, tap Save .","title":"Method 2 (Exchange ActiveSync emulation)"},{"location":"client/client-emclient/","text":"Launch eM Client. If this is the first time you launched eM Client, it asks you to set up your account. Proceed to step 4. Go to Menu at the top, select Tools and Accounts . Enter your email address ( ) and click Start Now . Enter your password and click Continue . Enter your name ( ) and click Next . Click Finish .","title":"eM Client"},{"location":"client/client-kontact/","text":"Launch Kontact. If this is the first time you launched Kontact or KMail, it asks you to set up your account. Proceed to step 4. Go to Mail in the sidebar. Go to the Tools menu and select Account Wizard . Enter your name ( ) , email address ( ) and your password. Click Next . Click Create Account . If prompted, re-enter your password and click OK . Close the window by clicking Finish . Go to Calendar in the sidebar. Go to the Settings menu and select Configure KOrganizer . Go to the Calendars tab and click the Add button. Choose DAV groupware resource and click OK . Enter your email address ( ) and your password. Click Next . Select ScalableOGo from the dropdown menu and click Next . Enter your mailcow hostname into the Host field and click Next . Click Test Connection and then Finish . Finally, click OK twice. Once you have set up Kontact, you can also use KMail, KOrganizer and KAddressBook individually.","title":"KDE Kontact"},{"location":"client/client-manual/","text":"These instructions are valid for unchanged port bindings only! Email \u00b6 Service Encryption Host Port IMAP STARTTLS mailcow hostname 143 IMAPS SSL mailcow hostname 993 POP3 STARTTLS mailcow hostname 110 POP3S SSL mailcow hostname 995 SMTP STARTTLS mailcow hostname 587 SMTPS SSL mailcow hostname 465 Please use \"plain\" as authentication mechanisms. Contrary to the assumption no passwords will be transferred plain text, as no authentication is allowed to take place without TLS. Contacts and calendars \u00b6 SOGos default calendar (CalDAV) and contacts (CardDAV) URLs: CalDAV - https://mail.example.com/SOGo/dav/user@example.com/Calendar/personal/ CardDAV - https://mail.example.com/SOGo/dav/user@example.com/Contacts/personal/ Some applications may require you to use https://mail.example.com/SOGo/dav/ or the full path to your calendar, which can be found and copied from within SOGo.","title":"Manual configuration"},{"location":"client/client-manual/#email","text":"Service Encryption Host Port IMAP STARTTLS mailcow hostname 143 IMAPS SSL mailcow hostname 993 POP3 STARTTLS mailcow hostname 110 POP3S SSL mailcow hostname 995 SMTP STARTTLS mailcow hostname 587 SMTPS SSL mailcow hostname 465 Please use \"plain\" as authentication mechanisms. Contrary to the assumption no passwords will be transferred plain text, as no authentication is allowed to take place without TLS.","title":"Email"},{"location":"client/client-manual/#contacts-and-calendars","text":"SOGos default calendar (CalDAV) and contacts (CardDAV) URLs: CalDAV - https://mail.example.com/SOGo/dav/user@example.com/Calendar/personal/ CardDAV - https://mail.example.com/SOGo/dav/user@example.com/Contacts/personal/ Some applications may require you to use https://mail.example.com/SOGo/dav/ or the full path to your calendar, which can be found and copied from within SOGo.","title":"Contacts and calendars"},{"location":"client/client-outlook/","text":"Outlook 2016 or higher from Office 365 on Windows \u00b6 This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Outlook 2016 has an issue with autodiscover . Only Outlook from Office 365 is affected. If you installed Outlook from another source, please follow the guide for Outlook 2013 or higher. For EAS you must use the old assistant by launching C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\OLCFG.EXE . If this application opens, you can go to step 4 of the guide for Outlook 2013 below. If it does not open, you can completely disable the new account creation wizard and follow the guide for Outlook 2013 below. Outlook 2013 or higher on Windows \u00b6 This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 4. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . When prompted, enter your password again, check Remember my credentials and click OK . Click the Allow button. Click Finish . Outlook 2007 or 2010 on Windows \u00b6 Outlook 2007 or higher on Windows \u00b6 Download and install Outlook CalDav Synchronizer . Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 5. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . Click Finish . Go to the CalDav Synchronizer ribbon and click Synchronization Profiles . Click the second button at top ( Add multiple profiles ), select Sogo , click Ok . Click the Get IMAP/POP3 account settings button. Click Discover resources and assign to Outlook folders . In the Select Resource window that pops up, select your main calendar (usually Personal Calendar ), click the ... button, assign it to Calendar , and click OK . Go to the Address Books and Tasks tabs and repeat repeat the process accordingly. Do not assign multiple calendars, address books or task lists! Close all windows with the OK buttons. Outlook 2011 or higher on macOS \u00b6 The Mac version of Outlook does not synchronize calendars and contacts and therefore is not supported.","title":"Microsoft Outlook"},{"location":"client/client-outlook/#outlook-2016-or-higher-from-office-365-on-windows","text":"This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Outlook 2016 has an issue with autodiscover . Only Outlook from Office 365 is affected. If you installed Outlook from another source, please follow the guide for Outlook 2013 or higher. For EAS you must use the old assistant by launching C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\OLCFG.EXE . If this application opens, you can go to step 4 of the guide for Outlook 2013 below. If it does not open, you can completely disable the new account creation wizard and follow the guide for Outlook 2013 below.","title":"Outlook 2016 or higher from Office 365 on Windows"},{"location":"client/client-outlook/#outlook-2013-or-higher-on-windows","text":"This is only applicable if your server administrator has not disabled EAS for Outlook. If it is disabled, please follow the guide for Outlook 2007 instead. Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 4. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . When prompted, enter your password again, check Remember my credentials and click OK . Click the Allow button. Click Finish .","title":"Outlook 2013 or higher on Windows"},{"location":"client/client-outlook/#outlook-2007-or-2010-on-windows","text":"","title":"Outlook 2007 or 2010 on Windows"},{"location":"client/client-outlook/#outlook-2007-or-higher-on-windows","text":"Download and install Outlook CalDav Synchronizer . Launch Outlook. If this is the first time you launched Outlook, it asks you to set up your account. Proceed to step 5. Go to the File menu and click Add Account . Enter your name ( ) , email address ( ) and your password. Click Next . Click Finish . Go to the CalDav Synchronizer ribbon and click Synchronization Profiles . Click the second button at top ( Add multiple profiles ), select Sogo , click Ok . Click the Get IMAP/POP3 account settings button. Click Discover resources and assign to Outlook folders . In the Select Resource window that pops up, select your main calendar (usually Personal Calendar ), click the ... button, assign it to Calendar , and click OK . Go to the Address Books and Tasks tabs and repeat repeat the process accordingly. Do not assign multiple calendars, address books or task lists! Close all windows with the OK buttons.","title":"Outlook 2007 or higher on Windows"},{"location":"client/client-outlook/#outlook-2011-or-higher-on-macos","text":"The Mac version of Outlook does not synchronize calendars and contacts and therefore is not supported.","title":"Outlook 2011 or higher on macOS"},{"location":"client/client-thunderbird/","text":"Launch Thunderbird. If this is the first time you launched Thunderbird, it asks you whether you would like a new email address. Click Skip this and use my existing email and proceed to step 4. Go to the Tools menu and select Account Settings . Click the Account Actions dropdown menu at the bottom left and select Add Mail Account . Enter your name ( ) , email address ( ) and your password. Make sure the Remember password checkbox is selected and click Continue . Once the configuration has been automatically detected, click Done . If you already had other accounts configured in Thunderbird, select the new one ( ) on the left, click the Account Actions dropdown and select Set as Default . Close the account settings window with the OK button. In your web browser, download SOGo Connector SOGo Connector . Back in Thunderbird, go to the Tools menu and select Add-ons . Click Extensions on the left and ensure that the Lightning add-on is already installed. It is installed by default in the Windows and macOS versions of Thunderbird, but if you are running Linux and installed Thunderbird through your distribution's package manager, Lightning might be available as a separate package (e.g. xul-ext-lightning on Ubuntu). Click Extensions on the left, click the little gear icon at the top and select Install Add-on From File . Select the file you downloaded in step 9, click Open and, after waiting for a few seconds, Install Now . Click the Restart Now button at the top that appears. Thunderbird briefly shows a message that it is updating extensions, then restarts automatically once more. When you are prompted to authenticate for , enter your email address and password, check Use Password Manager and click OK . Automatic configuration of calendars and address books in Thunderbird is not currently supported. You can ask your server administrator to enable SOGo Connector if you need it. Automatic configuration of calendars and address books (from step 9 onward) in Thunderbird is only supported if your server administrator has enabled SOGo Connector . Different method of connecting Cal-/CardDAV in Thunderbird with automatic detection of address books and calendars \u00b6 Instead of using SOGo Connector you can use a combination of https://addons.thunderbird.net/de/thunderbird/addon/tbsync/ and https://addons.thunderbird.net/de/thunderbird/addon/dav-4-tbsync/ To add your Cal-/CardDAV accounts go to Tools and find TbSync You can add new accounts via the CalDAV & CardDAV provider: Choose \"Automatic Configuration\". Use your mail address as account and username. Use your mail password as DAV password. The server URL is your MAILCOW_HOSTNAME (specifying any protocol is not necessary, just enter the full domain). Now tick the checkbox for \"Enable and synchronize this account\" in the synchronization status tab: Several available resources should appear in the same window area now. Tick all checkboxes of the resources (address books and calendars) that you want to sync. Choose a synchronization period (in minutes) in the same window area before clicking on \"Synchronize now\". If you leave the sync at \"0\" it will only sync manually so choose at least 30 minutes for periodic synchronization. If you want to manually synchronize you can find this option under \"Account actions\" - the dropdown-menu where you added the Cal-/CardDAV account (step 2).","title":"Mozilla Thunderbird"},{"location":"client/client-thunderbird/#different-method-of-connecting-cal-carddav-in-thunderbird-with-automatic-detection-of-address-books-and-calendars","text":"Instead of using SOGo Connector you can use a combination of https://addons.thunderbird.net/de/thunderbird/addon/tbsync/ and https://addons.thunderbird.net/de/thunderbird/addon/dav-4-tbsync/ To add your Cal-/CardDAV accounts go to Tools and find TbSync You can add new accounts via the CalDAV & CardDAV provider: Choose \"Automatic Configuration\". Use your mail address as account and username. Use your mail password as DAV password. The server URL is your MAILCOW_HOSTNAME (specifying any protocol is not necessary, just enter the full domain). Now tick the checkbox for \"Enable and synchronize this account\" in the synchronization status tab: Several available resources should appear in the same window area now. Tick all checkboxes of the resources (address books and calendars) that you want to sync. Choose a synchronization period (in minutes) in the same window area before clicking on \"Synchronize now\". If you leave the sync at \"0\" it will only sync manually so choose at least 30 minutes for periodic synchronization. If you want to manually synchronize you can find this option under \"Account actions\" - the dropdown-menu where you added the Cal-/CardDAV account (step 2).","title":"Different method of connecting Cal-/CardDAV in Thunderbird with automatic detection of address books and calendars"},{"location":"client/client-windows/","text":"Windows 8 and higher support email, contacts and calendar via Exchange ActiveSync. Open the Mail app. If you have not previously used Mail, you can click Add Account in the main window. Proceed to step 4. Click Accounts in the sidebar on the left, then click Add Account on the far right. Select Exchange . Enter your email address ( ) and click Next . Enter your password and click Log in . Once you have set up the Mail app, you can also use the People and Calendar apps.","title":"Windows Mail"},{"location":"client/client-windowsphone/","text":"Open the Settings app. Select email + accounts and tap add an account . Tap Exchange . Enter your email address ( ) and your password. Tap Sign in . Tap done .","title":"Windows Phone"}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 87af5f78eb021aef1e1b36864b0d4b74b2651d07..353de90ad1e0e773cb46318b4d50c9cda8d2265d 100644 GIT binary patch delta 15 WcmbQrF_nW&zMF$%@ve<*{44+>Y6Kzx delta 15 WcmbQrF_nW&zMF$XecMJheii^9ZUdqK diff --git a/third_party-borgmatic/index.html b/third_party-borgmatic/index.html index e9d551d3b..3ce4fafa3 100644 --- a/third_party-borgmatic/index.html +++ b/third_party-borgmatic/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2242,6 +2242,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2307,20 +2321,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2790,10 +2790,10 @@ repository, so a manual backup isn't as essential.

    - + - + diff --git a/third_party-exchange_onprem/index.html b/third_party-exchange_onprem/index.html index a43ddc854..734dc2563 100644 --- a/third_party-exchange_onprem/index.html +++ b/third_party-exchange_onprem/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2135,6 +2135,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2200,20 +2214,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2447,10 +2447,10 @@
    - + - + diff --git a/third_party-gitea/index.html b/third_party-gitea/index.html index 9183f7b44..709dc37e1 100644 --- a/third_party-gitea/index.html +++ b/third_party-gitea/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2313,10 +2313,10 @@ ROOT_URL = https://mx.example.org/gitea/
    - + - + diff --git a/third_party-gogs/index.html b/third_party-gogs/index.html index 3d6ae0d83..dbe0b0688 100644 --- a/third_party-gogs/index.html +++ b/third_party-gogs/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2249,13 +2249,13 @@ ROOT_URL = https://mx.example.org/gogs/ - @@ -2451,10 +2451,10 @@
    - + - + diff --git a/third_party-nextcloud/index.html b/third_party-nextcloud/index.html index 5165a0a6a..8abd294f8 100644 --- a/third_party-nextcloud/index.html +++ b/third_party-nextcloud/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2065,6 +2065,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2187,20 +2201,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2455,10 +2455,10 @@ It shows which commands have to be executed, these have to be placed in the php-
    - + - + diff --git a/third_party-portainer/index.html b/third_party-portainer/index.html index 4c90dfa98..48152548d 100644 --- a/third_party-portainer/index.html +++ b/third_party-portainer/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2060,6 +2060,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2328,10 +2328,10 @@ map $http_upgrade $connection_upgrade {
    - + - + diff --git a/third_party-roundcube/index.html b/third_party-roundcube/index.html index 1c503a560..cf9eb0fef 100644 --- a/third_party-roundcube/index.html +++ b/third_party-roundcube/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2065,6 +2065,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2180,20 +2194,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2494,10 +2494,10 @@ $MAILCOW_APPS = array(
    - + - + diff --git a/third_party-thunderbird/index.html b/third_party-thunderbird/index.html index a8dff975b..1c480f967 100644 --- a/third_party-thunderbird/index.html +++ b/third_party-thunderbird/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2065,6 +2065,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2140,20 +2154,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2253,21 +2253,6 @@ All your address books and calendars will be configured automatically.

    - - - - + - + diff --git a/u_e-mailcow_ui-tagging/index.html b/u_e-mailcow_ui-tagging/index.html index 98726d1f9..7bdbcbf18 100644 --- a/u_e-mailcow_ui-tagging/index.html +++ b/u_e-mailcow_ui-tagging/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2103,6 +2103,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2168,20 +2182,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2361,10 +2361,10 @@ index e047136e..933c4137 100644
    - + - + diff --git a/u_e-mailcow_ui-tfa/index.html b/u_e-mailcow_ui-tfa/index.html index 2a79b0dc9..33e2350d5 100644 --- a/u_e-mailcow_ui-tfa/index.html +++ b/u_e-mailcow_ui-tfa/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2117,6 +2117,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2182,20 +2196,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2401,10 +2401,10 @@ The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are
    - + - + diff --git a/u_e-nginx/index.html b/u_e-nginx/index.html index 2afc2a504..b3444d055 100644 --- a/u_e-nginx/index.html +++ b/u_e-nginx/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2124,6 +2124,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2189,20 +2203,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2475,10 +2475,10 @@ docker-compose restart php-fpm-mailcow
    - + - + diff --git a/u_e-postfix-attachment_size/index.html b/u_e-postfix-attachment_size/index.html index 7adecbf9c..abf1a3352 100644 --- a/u_e-postfix-attachment_size/index.html +++ b/u_e-postfix-attachment_size/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2281,10 +2281,10 @@
    - + - + diff --git a/u_e-postfix-custom_transport/index.html b/u_e-postfix-custom_transport/index.html index 6ff315bb5..e51ce422d 100644 --- a/u_e-postfix-custom_transport/index.html +++ b/u_e-postfix-custom_transport/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2280,10 +2280,10 @@
    - + - + diff --git a/u_e-postfix-disable_sender_verification/index.html b/u_e-postfix-disable_sender_verification/index.html index a6207e332..04d3d64e8 100644 --- a/u_e-postfix-disable_sender_verification/index.html +++ b/u_e-postfix-disable_sender_verification/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2110,6 +2110,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2175,20 +2189,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2369,10 +2369,10 @@
    - + - + diff --git a/u_e-postfix-extra_cf/index.html b/u_e-postfix-extra_cf/index.html index 9bbb6d83f..ecff503b9 100644 --- a/u_e-postfix-extra_cf/index.html +++ b/u_e-postfix-extra_cf/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2283,10 +2283,10 @@
    - + - + diff --git a/u_e-postfix-pflogsumm/index.html b/u_e-postfix-pflogsumm/index.html index 1d01b0f6f..9814d935b 100644 --- a/u_e-postfix-pflogsumm/index.html +++ b/u_e-postfix-pflogsumm/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2286,10 +2286,10 @@
    - + - + diff --git a/u_e-postfix-postscreen_whitelist/index.html b/u_e-postfix-postscreen_whitelist/index.html index 3d09876a4..0c9c3351a 100644 --- a/u_e-postfix-postscreen_whitelist/index.html +++ b/u_e-postfix-postscreen_whitelist/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2289,10 +2289,10 @@
    - + - + diff --git a/u_e-postfix-relayhost/index.html b/u_e-postfix-relayhost/index.html index 6014beefe..08095f650 100644 --- a/u_e-postfix-relayhost/index.html +++ b/u_e-postfix-relayhost/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2123,6 +2123,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2188,20 +2202,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2402,10 +2402,10 @@ Keep in mind the credentials will be stored in plain text.

    - + - + diff --git a/u_e-postfix-trust_networks/index.html b/u_e-postfix-trust_networks/index.html index 386d4bd78..48cb31a67 100644 --- a/u_e-postfix-trust_networks/index.html +++ b/u_e-postfix-trust_networks/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2123,6 +2123,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2188,20 +2202,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2405,10 +2405,10 @@
    - + - + diff --git a/u_e-redis/index.html b/u_e-redis/index.html index 1b80f4d13..6d31610e4 100644 --- a/u_e-redis/index.html +++ b/u_e-redis/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2141,6 +2141,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2206,20 +2220,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2444,10 +2444,10 @@ PONG
    - + - + diff --git a/u_e-reeanble-weak-protocols/index.html b/u_e-reeanble-weak-protocols/index.html index a841d7100..c08b8dd6f 100644 --- a/u_e-reeanble-weak-protocols/index.html +++ b/u_e-reeanble-weak-protocols/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2289,10 +2289,10 @@ smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
    - + - + diff --git a/u_e-rspamd/index.html b/u_e-rspamd/index.html index 124e5c34d..4a3ddc408 100644 --- a/u_e-rspamd/index.html +++ b/u_e-rspamd/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2184,6 +2184,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2249,20 +2263,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2647,10 +2647,10 @@ quarantine_notify.py
    - + - + diff --git a/u_e-sogo/index.html b/u_e-sogo/index.html index 7846e77ca..f58aa242d 100644 --- a/u_e-sogo/index.html +++ b/u_e-sogo/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2136,6 +2136,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2201,20 +2215,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2466,10 +2466,10 @@ After you replaced said file you need to restart SOGo and Memcached containers b
    - + - + diff --git a/u_e-unbound-fwd/index.html b/u_e-unbound-fwd/index.html index e8a5c69b6..7202ef43f 100644 --- a/u_e-unbound-fwd/index.html +++ b/u_e-unbound-fwd/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2110,6 +2110,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2175,20 +2189,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2374,10 +2374,10 @@ cp helper-scripts/docker-compose.override.yml.d/EXTERNAL_DNS/docker-compose.over
    - + - + diff --git a/u_e-update-hooks/index.html b/u_e-update-hooks/index.html index 4de885443..65890a50d 100644 --- a/u_e-update-hooks/index.html +++ b/u_e-update-hooks/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2283,10 +2283,10 @@
    - + - + diff --git a/u_e-webmail-site/index.html b/u_e-webmail-site/index.html index d45f403aa..837689524 100644 --- a/u_e-webmail-site/index.html +++ b/u_e-webmail-site/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2070,6 +2070,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2135,20 +2149,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2306,10 +2306,10 @@ Add webmail.example.org to this array, don't use quotes!

    - + - + diff --git a/u_e-why_unbound/index.html b/u_e-why_unbound/index.html index 7a0657f30..f4741948a 100644 --- a/u_e-why_unbound/index.html +++ b/u_e-why_unbound/index.html @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + @@ -2068,6 +2068,20 @@ +
  • + + Mailman3 + +
  • + + + + + + + + +
  • Mailpiler Integration @@ -2133,20 +2147,6 @@ - - - - - -
  • - - Mailman3 - -
  • - - - - @@ -2280,10 +2280,10 @@ Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolv
    - + - +