diff --git a/anchors.css b/anchors.css
index 569b2286..99cd0429 100644
--- a/anchors.css
+++ b/anchors.css
@@ -31,4 +31,4 @@
left: -1em;
}
-/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLzxpbnB1dCBjc3MgMT4iLCJhbmNob3JzLmxlc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsbURBQW1EO0FDRW5EO0VBQ0UsbUJBQUE7RUFDQSxXQUFBO0VBQ0EsaUJBQUE7Q0RBRDtBQ0VDO0VBQ0UsY0FBQTtDREFIO0FDRUc7OztFQUdFLFlBQUE7RUFDQSxzQkFBQTtDREFMO0FDR0c7RUFDRSxlQUFBO0NEREw7QUNLQztFQUVJLHNCQUFBO0NESkw7QUNTRztFQUNFLGtCQUFBO0NEUEw7QUNLQztFQU1JLG1CQUFBO0VBQ0EsV0FBQTtFQUNBLFNBQUE7RUFDQSxvQ0FBQTtVQUFBLDRCQUFBO0VBQ0EsV0FBQTtDRFJMIiwiZmlsZSI6ImFuY2hvcnMuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyogPT09PT09PT09PSBjb2xvcnMudGhlbWUgLSDln7rnoYDpopzoibLlj5jph4/pm4blkIggPT09PT09PT09PSAqL1xuLm91dGxpbmUtaGVhZGluZyB7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgei1pbmRleDogMTtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG4ub3V0bGluZS1oZWFkaW5nX19hbmNob3I6bGluayxcbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjp2aXNpdGVkLFxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOmhvdmVyIHtcbiAgY29sb3I6ICM5OTk7XG4gIHRleHQtZGVjb3JhdGlvbjogbm9uZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjpob3ZlciB7XG4gIGNvbG9yOiAjMWY4ZGQ2O1xufVxuLm91dGxpbmUtaGVhZGluZzpob3ZlciAub3V0bGluZS1oZWFkaW5nX19hbmNob3Ige1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG59XG4ub3V0bGluZS1oZWFkaW5nX3N0YXJ0OmhvdmVyIHtcbiAgb3ZlcmZsb3c6IHZpc2libGU7XG59XG4ub3V0bGluZS1oZWFkaW5nX3N0YXJ0IC5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgei1pbmRleDogMjtcbiAgdG9wOiA1MCU7XG4gIHRyYW5zZm9ybTogdHJhbnNsYXRlWSgtNTAlKTtcbiAgbGVmdDogLTFlbTtcbn1cbiIsIkBpbXBvcnQgJy4vY29sb3JzJztcclxuXHJcbi5vdXRsaW5lLWhlYWRpbmcge1xyXG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcclxuICB6LWluZGV4OiAxO1xyXG4gIG92ZXJmbG93OiBoaWRkZW47XHJcblxyXG4gICZfX2FuY2hvciB7XHJcbiAgICBkaXNwbGF5OiBub25lO1xyXG5cclxuICAgICY6bGluayxcclxuICAgICY6dmlzaXRlZCxcclxuICAgICY6aG92ZXIge1xyXG4gICAgICBjb2xvcjogQGZvdXJ0aF90ZXh0X2NvbG9yO1xyXG4gICAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7XHJcbiAgICB9XHJcblxyXG4gICAgJjpob3ZlciB7XHJcbiAgICAgIGNvbG9yOiBAcHJpbWFyeV9jb2xvcjtcclxuICAgIH1cclxuICB9XHJcblxyXG4gICY6aG92ZXIge1xyXG4gICAgLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcclxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgJl9zdGFydCB7XHJcbiAgICAmOmhvdmVyIHtcclxuICAgICAgb3ZlcmZsb3c6IHZpc2libGU7XHJcbiAgICB9XHJcblxyXG4gICAgLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcclxuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xyXG4gICAgICB6LWluZGV4OiAyO1xyXG4gICAgICB0b3A6IDUwJTtcclxuICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC01MCUpO1xyXG4gICAgICBsZWZ0OiAtMWVtO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iXX0= */
+/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLzxpbnB1dCBjc3MgMT4iLCJhbmNob3JzLmxlc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsbURBQW1EO0FDRW5EO0VBQ0UsbUJBQUE7RUFDQSxXQUFBO0VBQ0EsaUJBQUE7Q0RBRDtBQ0VDO0VBQ0UsY0FBQTtDREFIO0FDRUc7OztFQUdFLFlBQUE7RUFDQSxzQkFBQTtDREFMO0FDR0c7RUFDRSxlQUFBO0NEREw7QUNLQztFQUVJLHNCQUFBO0NESkw7QUNTRztFQUNFLGtCQUFBO0NEUEw7QUNLQztFQU1JLG1CQUFBO0VBQ0EsV0FBQTtFQUNBLFNBQUE7RUFDQSxvQ0FBQTtVQUFBLDRCQUFBO0VBQ0EsV0FBQTtDRFJMIiwiZmlsZSI6ImFuY2hvcnMuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyogPT09PT09PT09PSBjb2xvcnMudGhlbWUgLSDln7rnoYDpopzoibLlj5jph4/pm4blkIggPT09PT09PT09PSAqL1xuLm91dGxpbmUtaGVhZGluZyB7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgei1pbmRleDogMTtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG4ub3V0bGluZS1oZWFkaW5nX19hbmNob3I6bGluayxcbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjp2aXNpdGVkLFxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOmhvdmVyIHtcbiAgY29sb3I6ICM5OTk7XG4gIHRleHQtZGVjb3JhdGlvbjogbm9uZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjpob3ZlciB7XG4gIGNvbG9yOiAjMWY4ZGQ2O1xufVxuLm91dGxpbmUtaGVhZGluZzpob3ZlciAub3V0bGluZS1oZWFkaW5nX19hbmNob3Ige1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG59XG4ub3V0bGluZS1oZWFkaW5nX3N0YXJ0OmhvdmVyIHtcbiAgb3ZlcmZsb3c6IHZpc2libGU7XG59XG4ub3V0bGluZS1oZWFkaW5nX3N0YXJ0IC5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgei1pbmRleDogMjtcbiAgdG9wOiA1MCU7XG4gIHRyYW5zZm9ybTogdHJhbnNsYXRlWSgtNTAlKTtcbiAgbGVmdDogLTFlbTtcbn1cbiIsIkBpbXBvcnQgJy4vY29sb3JzJztcblxuLm91dGxpbmUtaGVhZGluZyB7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgei1pbmRleDogMTtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcblxuICAmX19hbmNob3Ige1xuICAgIGRpc3BsYXk6IG5vbmU7XG5cbiAgICAmOmxpbmssXG4gICAgJjp2aXNpdGVkLFxuICAgICY6aG92ZXIge1xuICAgICAgY29sb3I6IEBmb3VydGhfdGV4dF9jb2xvcjtcbiAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTtcbiAgICB9XG5cbiAgICAmOmhvdmVyIHtcbiAgICAgIGNvbG9yOiBAcHJpbWFyeV9jb2xvcjtcbiAgICB9XG4gIH1cblxuICAmOmhvdmVyIHtcbiAgICAub3V0bGluZS1oZWFkaW5nX19hbmNob3Ige1xuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgIH1cbiAgfVxuXG4gICZfc3RhcnQge1xuICAgICY6aG92ZXIge1xuICAgICAgb3ZlcmZsb3c6IHZpc2libGU7XG4gICAgfVxuXG4gICAgLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcbiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgIHotaW5kZXg6IDI7XG4gICAgICB0b3A6IDUwJTtcbiAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlWSgtNTAlKTtcbiAgICAgIGxlZnQ6IC0xZW07XG4gICAgfVxuICB9XG59XG4iXX0= */
diff --git a/anchors.min.css b/anchors.min.css
index 9b072ff7..9837c2c1 100644
--- a/anchors.min.css
+++ b/anchors.min.css
@@ -1,2 +1,2 @@
.outline-heading{position:relative;z-index:1;overflow:hidden}.outline-heading__anchor{display:none}.outline-heading__anchor:hover,.outline-heading__anchor:link,.outline-heading__anchor:visited{color:#999;text-decoration:none}.outline-heading__anchor:hover{color:#1f8dd6}.outline-heading:hover .outline-heading__anchor{display:inline-block}.outline-heading_start:hover{overflow:visible}.outline-heading_start .outline-heading__anchor{position:absolute;z-index:2;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);left:-1em}
-/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJhbmNob3JzLmNzcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiA9PT09PT09PT09IGNvbG9ycy50aGVtZSAtIOWfuuehgOminOiJsuWPmOmHj+mbhuWQiCA9PT09PT09PT09ICovXG4ub3V0bGluZS1oZWFkaW5nIHtcbiAgcG9zaXRpb246IHJlbGF0aXZlO1xuICB6LWluZGV4OiAxO1xuICBvdmVyZmxvdzogaGlkZGVuO1xufVxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcbiAgZGlzcGxheTogbm9uZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjpsaW5rLFxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOnZpc2l0ZWQsXG4ub3V0bGluZS1oZWFkaW5nX19hbmNob3I6aG92ZXIge1xuICBjb2xvcjogIzk5OTtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOmhvdmVyIHtcbiAgY29sb3I6ICMxZjhkZDY7XG59XG4ub3V0bGluZS1oZWFkaW5nOmhvdmVyIC5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbn1cbi5vdXRsaW5lLWhlYWRpbmdfc3RhcnQ6aG92ZXIge1xuICBvdmVyZmxvdzogdmlzaWJsZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfc3RhcnQgLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB6LWluZGV4OiAyO1xuICB0b3A6IDUwJTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHRyYW5zbGF0ZVkoLTUwJSk7XG4gICAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC01MCUpO1xuICBsZWZ0OiAtMWVtO1xufVxuXG4vKiMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldD11dGY4O2Jhc2U2NCxleUoyWlhKemFXOXVJam96TENKemIzVnlZMlZ6SWpwYklpNHVMeTR1THp4cGJuQjFkQ0JqYzNNZ01UNGlMQ0poYm1Ob2IzSnpMbXhsYzNNaVhTd2libUZ0WlhNaU9sdGRMQ0p0WVhCd2FXNW5jeUk2SWtGQlFVRXNiVVJCUVcxRU8wRkRSVzVFTzBWQlEwVXNiVUpCUVVFN1JVRkRRU3hYUVVGQk8wVkJRMEVzYVVKQlFVRTdRMFJCUkR0QlEwVkRPMFZCUTBVc1kwRkJRVHREUkVGSU8wRkRSVWM3T3p0RlFVZEZMRmxCUVVFN1JVRkRRU3h6UWtGQlFUdERSRUZNTzBGRFIwYzdSVUZEUlN4bFFVRkJPME5FUkV3N1FVTkxRenRGUVVWSkxITkNRVUZCTzBORVNrdzdRVU5UUnp0RlFVTkZMR3RDUVVGQk8wTkVVRXc3UVVOTFF6dEZRVTFKTEcxQ1FVRkJPMFZCUTBFc1YwRkJRVHRGUVVOQkxGTkJRVUU3UlVGRFFTeHZRMEZCUVR0VlFVRkJMRFJDUVVGQk8wVkJRMEVzVjBGQlFUdERSRkpNSWl3aVptbHNaU0k2SW1GdVkyaHZjbk11WTNOeklpd2ljMjkxY21ObGMwTnZiblJsYm5RaU9sc2lMeW9nUFQwOVBUMDlQVDA5UFNCamIyeHZjbk11ZEdobGJXVWdMU0Rsbjdybm9ZRHBvcHpvaWJMbGo1anBoNC9wbTRibGtJZ2dQVDA5UFQwOVBUMDlQU0FxTDF4dUxtOTFkR3hwYm1VdGFHVmhaR2x1WnlCN1hHNGdJSEJ2YzJsMGFXOXVPaUJ5Wld4aGRHbDJaVHRjYmlBZ2VpMXBibVJsZURvZ01UdGNiaUFnYjNabGNtWnNiM2M2SUdocFpHUmxianRjYm4xY2JpNXZkWFJzYVc1bExXaGxZV1JwYm1kZlgyRnVZMmh2Y2lCN1hHNGdJR1JwYzNCc1lYazZJRzV2Ym1VN1hHNTlYRzR1YjNWMGJHbHVaUzFvWldGa2FXNW5YMTloYm1Ob2IzSTZiR2x1YXl4Y2JpNXZkWFJzYVc1bExXaGxZV1JwYm1kZlgyRnVZMmh2Y2pwMmFYTnBkR1ZrTEZ4dUxtOTFkR3hwYm1VdGFHVmhaR2x1WjE5ZllXNWphRzl5T21odmRtVnlJSHRjYmlBZ1kyOXNiM0k2SUNNNU9UazdYRzRnSUhSbGVIUXRaR1ZqYjNKaGRHbHZiam9nYm05dVpUdGNibjFjYmk1dmRYUnNhVzVsTFdobFlXUnBibWRmWDJGdVkyaHZjanBvYjNabGNpQjdYRzRnSUdOdmJHOXlPaUFqTVdZNFpHUTJPMXh1ZlZ4dUxtOTFkR3hwYm1VdGFHVmhaR2x1Wnpwb2IzWmxjaUF1YjNWMGJHbHVaUzFvWldGa2FXNW5YMTloYm1Ob2IzSWdlMXh1SUNCa2FYTndiR0Y1T2lCcGJteHBibVV0WW14dlkyczdYRzU5WEc0dWIzVjBiR2x1WlMxb1pXRmthVzVuWDNOMFlYSjBPbWh2ZG1WeUlIdGNiaUFnYjNabGNtWnNiM2M2SUhacGMybGliR1U3WEc1OVhHNHViM1YwYkdsdVpTMW9aV0ZrYVc1blgzTjBZWEowSUM1dmRYUnNhVzVsTFdobFlXUnBibWRmWDJGdVkyaHZjaUI3WEc0Z0lIQnZjMmwwYVc5dU9pQmhZbk52YkhWMFpUdGNiaUFnZWkxcGJtUmxlRG9nTWp0Y2JpQWdkRzl3T2lBMU1DVTdYRzRnSUhSeVlXNXpabTl5YlRvZ2RISmhibk5zWVhSbFdTZ3ROVEFsS1R0Y2JpQWdiR1ZtZERvZ0xURmxiVHRjYm4xY2JpSXNJa0JwYlhCdmNuUWdKeTR2WTI5c2IzSnpKenRjY2x4dVhISmNiaTV2ZFhSc2FXNWxMV2hsWVdScGJtY2dlMXh5WEc0Z0lIQnZjMmwwYVc5dU9pQnlaV3hoZEdsMlpUdGNjbHh1SUNCNkxXbHVaR1Y0T2lBeE8xeHlYRzRnSUc5MlpYSm1iRzkzT2lCb2FXUmtaVzQ3WEhKY2JseHlYRzRnSUNaZlgyRnVZMmh2Y2lCN1hISmNiaUFnSUNCa2FYTndiR0Y1T2lCdWIyNWxPMXh5WEc1Y2NseHVJQ0FnSUNZNmJHbHVheXhjY2x4dUlDQWdJQ1k2ZG1semFYUmxaQ3hjY2x4dUlDQWdJQ1k2YUc5MlpYSWdlMXh5WEc0Z0lDQWdJQ0JqYjJ4dmNqb2dRR1p2ZFhKMGFGOTBaWGgwWDJOdmJHOXlPMXh5WEc0Z0lDQWdJQ0IwWlhoMExXUmxZMjl5WVhScGIyNDZJRzV2Ym1VN1hISmNiaUFnSUNCOVhISmNibHh5WEc0Z0lDQWdKanBvYjNabGNpQjdYSEpjYmlBZ0lDQWdJR052Ykc5eU9pQkFjSEpwYldGeWVWOWpiMnh2Y2p0Y2NseHVJQ0FnSUgxY2NseHVJQ0I5WEhKY2JseHlYRzRnSUNZNmFHOTJaWElnZTF4eVhHNGdJQ0FnTG05MWRHeHBibVV0YUdWaFpHbHVaMTlmWVc1amFHOXlJSHRjY2x4dUlDQWdJQ0FnWkdsemNHeGhlVG9nYVc1c2FXNWxMV0pzYjJOck8xeHlYRzRnSUNBZ2ZWeHlYRzRnSUgxY2NseHVYSEpjYmlBZ0psOXpkR0Z5ZENCN1hISmNiaUFnSUNBbU9taHZkbVZ5SUh0Y2NseHVJQ0FnSUNBZ2IzWmxjbVpzYjNjNklIWnBjMmxpYkdVN1hISmNiaUFnSUNCOVhISmNibHh5WEc0Z0lDQWdMbTkxZEd4cGJtVXRhR1ZoWkdsdVoxOWZZVzVqYUc5eUlIdGNjbHh1SUNBZ0lDQWdjRzl6YVhScGIyNDZJR0ZpYzI5c2RYUmxPMXh5WEc0Z0lDQWdJQ0I2TFdsdVpHVjRPaUF5TzF4eVhHNGdJQ0FnSUNCMGIzQTZJRFV3SlR0Y2NseHVJQ0FnSUNBZ2RISmhibk5tYjNKdE9pQjBjbUZ1YzJ4aGRHVlpLQzAxTUNVcE8xeHlYRzRnSUNBZ0lDQnNaV1owT2lBdE1XVnRPMXh5WEc0Z0lDQWdmVnh5WEc0Z0lIMWNjbHh1ZlZ4eVhHNGlYWDA9ICovXG4iXSwiZmlsZSI6ImFuY2hvcnMubWluLmNzcyJ9 */
+/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJhbmNob3JzLmNzcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiA9PT09PT09PT09IGNvbG9ycy50aGVtZSAtIOWfuuehgOminOiJsuWPmOmHj+mbhuWQiCA9PT09PT09PT09ICovXG4ub3V0bGluZS1oZWFkaW5nIHtcbiAgcG9zaXRpb246IHJlbGF0aXZlO1xuICB6LWluZGV4OiAxO1xuICBvdmVyZmxvdzogaGlkZGVuO1xufVxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcbiAgZGlzcGxheTogbm9uZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvcjpsaW5rLFxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOnZpc2l0ZWQsXG4ub3V0bGluZS1oZWFkaW5nX19hbmNob3I6aG92ZXIge1xuICBjb2xvcjogIzk5OTtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yOmhvdmVyIHtcbiAgY29sb3I6ICMxZjhkZDY7XG59XG4ub3V0bGluZS1oZWFkaW5nOmhvdmVyIC5vdXRsaW5lLWhlYWRpbmdfX2FuY2hvciB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbn1cbi5vdXRsaW5lLWhlYWRpbmdfc3RhcnQ6aG92ZXIge1xuICBvdmVyZmxvdzogdmlzaWJsZTtcbn1cbi5vdXRsaW5lLWhlYWRpbmdfc3RhcnQgLm91dGxpbmUtaGVhZGluZ19fYW5jaG9yIHtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB6LWluZGV4OiAyO1xuICB0b3A6IDUwJTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHRyYW5zbGF0ZVkoLTUwJSk7XG4gICAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC01MCUpO1xuICBsZWZ0OiAtMWVtO1xufVxuXG4vKiMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247Y2hhcnNldD11dGY4O2Jhc2U2NCxleUoyWlhKemFXOXVJam96TENKemIzVnlZMlZ6SWpwYklpNHVMeTR1THp4cGJuQjFkQ0JqYzNNZ01UNGlMQ0poYm1Ob2IzSnpMbXhsYzNNaVhTd2libUZ0WlhNaU9sdGRMQ0p0WVhCd2FXNW5jeUk2SWtGQlFVRXNiVVJCUVcxRU8wRkRSVzVFTzBWQlEwVXNiVUpCUVVFN1JVRkRRU3hYUVVGQk8wVkJRMEVzYVVKQlFVRTdRMFJCUkR0QlEwVkRPMFZCUTBVc1kwRkJRVHREUkVGSU8wRkRSVWM3T3p0RlFVZEZMRmxCUVVFN1JVRkRRU3h6UWtGQlFUdERSRUZNTzBGRFIwYzdSVUZEUlN4bFFVRkJPME5FUkV3N1FVTkxRenRGUVVWSkxITkNRVUZCTzBORVNrdzdRVU5UUnp0RlFVTkZMR3RDUVVGQk8wTkVVRXc3UVVOTFF6dEZRVTFKTEcxQ1FVRkJPMFZCUTBFc1YwRkJRVHRGUVVOQkxGTkJRVUU3UlVGRFFTeHZRMEZCUVR0VlFVRkJMRFJDUVVGQk8wVkJRMEVzVjBGQlFUdERSRkpNSWl3aVptbHNaU0k2SW1GdVkyaHZjbk11WTNOeklpd2ljMjkxY21ObGMwTnZiblJsYm5RaU9sc2lMeW9nUFQwOVBUMDlQVDA5UFNCamIyeHZjbk11ZEdobGJXVWdMU0Rsbjdybm9ZRHBvcHpvaWJMbGo1anBoNC9wbTRibGtJZ2dQVDA5UFQwOVBUMDlQU0FxTDF4dUxtOTFkR3hwYm1VdGFHVmhaR2x1WnlCN1hHNGdJSEJ2YzJsMGFXOXVPaUJ5Wld4aGRHbDJaVHRjYmlBZ2VpMXBibVJsZURvZ01UdGNiaUFnYjNabGNtWnNiM2M2SUdocFpHUmxianRjYm4xY2JpNXZkWFJzYVc1bExXaGxZV1JwYm1kZlgyRnVZMmh2Y2lCN1hHNGdJR1JwYzNCc1lYazZJRzV2Ym1VN1hHNTlYRzR1YjNWMGJHbHVaUzFvWldGa2FXNW5YMTloYm1Ob2IzSTZiR2x1YXl4Y2JpNXZkWFJzYVc1bExXaGxZV1JwYm1kZlgyRnVZMmh2Y2pwMmFYTnBkR1ZrTEZ4dUxtOTFkR3hwYm1VdGFHVmhaR2x1WjE5ZllXNWphRzl5T21odmRtVnlJSHRjYmlBZ1kyOXNiM0k2SUNNNU9UazdYRzRnSUhSbGVIUXRaR1ZqYjNKaGRHbHZiam9nYm05dVpUdGNibjFjYmk1dmRYUnNhVzVsTFdobFlXUnBibWRmWDJGdVkyaHZjanBvYjNabGNpQjdYRzRnSUdOdmJHOXlPaUFqTVdZNFpHUTJPMXh1ZlZ4dUxtOTFkR3hwYm1VdGFHVmhaR2x1Wnpwb2IzWmxjaUF1YjNWMGJHbHVaUzFvWldGa2FXNW5YMTloYm1Ob2IzSWdlMXh1SUNCa2FYTndiR0Y1T2lCcGJteHBibVV0WW14dlkyczdYRzU5WEc0dWIzVjBiR2x1WlMxb1pXRmthVzVuWDNOMFlYSjBPbWh2ZG1WeUlIdGNiaUFnYjNabGNtWnNiM2M2SUhacGMybGliR1U3WEc1OVhHNHViM1YwYkdsdVpTMW9aV0ZrYVc1blgzTjBZWEowSUM1dmRYUnNhVzVsTFdobFlXUnBibWRmWDJGdVkyaHZjaUI3WEc0Z0lIQnZjMmwwYVc5dU9pQmhZbk52YkhWMFpUdGNiaUFnZWkxcGJtUmxlRG9nTWp0Y2JpQWdkRzl3T2lBMU1DVTdYRzRnSUhSeVlXNXpabTl5YlRvZ2RISmhibk5zWVhSbFdTZ3ROVEFsS1R0Y2JpQWdiR1ZtZERvZ0xURmxiVHRjYm4xY2JpSXNJa0JwYlhCdmNuUWdKeTR2WTI5c2IzSnpKenRjYmx4dUxtOTFkR3hwYm1VdGFHVmhaR2x1WnlCN1hHNGdJSEJ2YzJsMGFXOXVPaUJ5Wld4aGRHbDJaVHRjYmlBZ2VpMXBibVJsZURvZ01UdGNiaUFnYjNabGNtWnNiM2M2SUdocFpHUmxianRjYmx4dUlDQW1YMTloYm1Ob2IzSWdlMXh1SUNBZ0lHUnBjM0JzWVhrNklHNXZibVU3WEc1Y2JpQWdJQ0FtT214cGJtc3NYRzRnSUNBZ0pqcDJhWE5wZEdWa0xGeHVJQ0FnSUNZNmFHOTJaWElnZTF4dUlDQWdJQ0FnWTI5c2IzSTZJRUJtYjNWeWRHaGZkR1Y0ZEY5amIyeHZjanRjYmlBZ0lDQWdJSFJsZUhRdFpHVmpiM0poZEdsdmJqb2dibTl1WlR0Y2JpQWdJQ0I5WEc1Y2JpQWdJQ0FtT21odmRtVnlJSHRjYmlBZ0lDQWdJR052Ykc5eU9pQkFjSEpwYldGeWVWOWpiMnh2Y2p0Y2JpQWdJQ0I5WEc0Z0lIMWNibHh1SUNBbU9taHZkbVZ5SUh0Y2JpQWdJQ0F1YjNWMGJHbHVaUzFvWldGa2FXNW5YMTloYm1Ob2IzSWdlMXh1SUNBZ0lDQWdaR2x6Y0d4aGVUb2dhVzVzYVc1bExXSnNiMk5yTzF4dUlDQWdJSDFjYmlBZ2ZWeHVYRzRnSUNaZmMzUmhjblFnZTF4dUlDQWdJQ1k2YUc5MlpYSWdlMXh1SUNBZ0lDQWdiM1psY21ac2IzYzZJSFpwYzJsaWJHVTdYRzRnSUNBZ2ZWeHVYRzRnSUNBZ0xtOTFkR3hwYm1VdGFHVmhaR2x1WjE5ZllXNWphRzl5SUh0Y2JpQWdJQ0FnSUhCdmMybDBhVzl1T2lCaFluTnZiSFYwWlR0Y2JpQWdJQ0FnSUhvdGFXNWtaWGc2SURJN1hHNGdJQ0FnSUNCMGIzQTZJRFV3SlR0Y2JpQWdJQ0FnSUhSeVlXNXpabTl5YlRvZ2RISmhibk5zWVhSbFdTZ3ROVEFsS1R0Y2JpQWdJQ0FnSUd4bFpuUTZJQzB4WlcwN1hHNGdJQ0FnZlZ4dUlDQjlYRzU5WEc0aVhYMD0gKi9cbiJdLCJmaWxlIjoiYW5jaG9ycy5taW4uY3NzIn0= */
diff --git a/anchors.min.js.map b/anchors.min.js.map
index 84cc3f4f..644b10fa 100644
--- a/anchors.min.js.map
+++ b/anchors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"anchors.min.js","sources":["src/utils/types/isString.js","src/utils/lang/hasOwn.js","src/utils/lang/toString.js","src/utils/types/isFunction.js","src/utils/types/isObject.js","src/base.js","src/utils/lang/extend.js","src/utils/types/isElement.js","src/utils/lang/easeInQuad.js","src/utils/dom/_getScrollElement.js","src/utils/dom/offsetTop.js","src/utils/dom/matches.js","src/utils/dom/getParentOrHost.js","src/utils/event/enum.js","src/utils/event/_off.js","src/utils/event/_delete.js","src/utils/event/purgeElement.js","src/utils/event/getListeners.js","src/utils/event/off.js","src/utils/event/on.js","src/utils/event/getTarget.js","src/utils/dom/resolveTextNode.js","src/utils/dom/closest.js","src/utils/observer/_subscribers.js","src/utils/observer/_hasDirectSubscribersFor.js","src/utils/observer/has.js","src/utils/observer/_hasSubscribers.js","src/utils/observer/emit.js","src/utils/types/isTypedArray.js","src/utils/types/isArray.js","src/utils/icons/symbols.js","src/utils/icons/defaults.js","src/utils/icons/getSymbols.js","src/utils/icons/getSymbol.js","src/utils/icons/paint.js","src/utils/icons/add.js","src/utils/lang/trim.js","src/utils/types/isDOM.js","src/utils/types/isHTMLCollection.js","src/utils/types/isFragment.js","src/utils/types/isTextNode.js","src/utils/dom/setAttribute.js","src/utils/types/isSVG.js","src/utils/icons/icon.js","src/utils/icons/createElement.js","src/_updateHeading.js","src/utils/dom/createElement.js","src/utils/dom/removeClass.js","src/utils/dom/hasClass.js","src/_resetHeading.js","src/utils/types/isEmpty.js","src/getChapters.js","src/utils/lang/stripTags.js","src/_getChapterParentIdByDiffer.js","src/_getChaptersWithCode.js","src/anchors.js","src/utils/lang/toTree.js","src/utils/dom/scrollTo.js","src/utils/lang/later.js","src/utils/event/stop.js"],"sourcesContent":["/**\r\n * 检测数据是否为 String 类型\r\n * ========================================================================\r\n * @method isArray\r\n * @param {*} str\r\n * @returns {boolean}\r\n */\r\nconst isString = (str) => {\r\n return typeof str === 'string'\r\n}\r\n\r\nexport default isString\r\n","/**\r\n * 检测对象自身属性中是否具有指定的属性。\r\n * ========================================================================\r\n * @method hasOwn\r\n * @param {Object} obj - (必须)检测的目标对象\r\n * @param {String} prop - (必须)属性名\r\n * @returns {Boolean}\r\n */\r\nconst hasOwn = (obj, prop) => {\r\n const hasOwnProperty = Object.prototype.hasOwnProperty\r\n return obj && hasOwnProperty.call(obj, prop)\r\n}\r\n\r\nexport default hasOwn\r\n","/**\r\n * Object 对象原型上的 toString 方法\r\n * ========================================================================\r\n * @method toString\r\n * @param {*} val\r\n * @returns {string}\r\n */\r\nconst toString = (val) => {\r\n return Object.prototype.toString.apply(val)\r\n}\r\n\r\nexport default toString\r\n","import toString from '../lang/toString'\r\n\r\n/**\r\n * 检测测试数据是否为 Function 类型\r\n * ========================================================================\r\n * @method isFunction\r\n * @param {*} val - (必须)待检测的数据\r\n * @returns {boolean} 'val' 是 Function 类型返回 true,否则返回 false\r\n */\r\nconst isFunction = (val) => {\r\n return typeof val === 'function' || toString(val) === '[object Function]'\r\n}\r\n\r\nexport default isFunction\r\n","import toString from '../lang/toString'\r\nimport isFunction from '../types/isFunction'\r\n\r\n/**\r\n * 检测数据是否为 Object 类型\r\n * ========================================================================\r\n * @method isObject\r\n * @param {*} o\r\n * @returns {boolean}\r\n */\r\nconst isObject = (o) => {\r\n return (\r\n (toString(o) === '[object Object]' ||\r\n typeof o === 'object' ||\r\n isFunction(o)) &&\r\n o !== null\r\n )\r\n}\r\n\r\nexport default isObject\r\n","import isString from './utils/types/isString'\r\nimport hasOwn from './utils/lang/hasOwn'\r\nimport isObject from './utils/types/isObject'\r\nimport extend from './utils/lang/extend'\r\n\r\nclass Base {\r\n constructor(options) {\r\n this.attrs = {}\r\n\r\n if (options) {\r\n this.initialize(options)\r\n }\r\n }\r\n\r\n initialize(options) {\r\n this.attr(options).render().addListeners()\r\n return this\r\n }\r\n\r\n attr(prop, value) {\r\n const attrs = this.attrs\r\n\r\n if (isString(prop)) {\r\n // 只能扩展 attrs 中已有的属性\r\n if (value && hasOwn(attrs, prop)) {\r\n // 更新单个配置信息\r\n attrs[prop] = value\r\n return this\r\n }\r\n\r\n // 只传递 prop 参数,则返回对应的属性值\r\n return attrs[prop]\r\n } else if (isObject(prop)) {\r\n // 批量更新配置信息\r\n extend(attrs, prop)\r\n\r\n return this\r\n } else if (arguments.length === 0) {\r\n // 不传递参数,直接返回整个\r\n return attrs\r\n }\r\n\r\n return this\r\n }\r\n\r\n render() {\r\n return this\r\n }\r\n\r\n destroy() {\r\n this.removeListeners()\r\n return this\r\n }\r\n\r\n reload(options) {\r\n this.destroy().initialize(this.attr(options))\r\n return this\r\n }\r\n\r\n addListeners() {\r\n return this\r\n }\r\n\r\n removeListeners() {\r\n return this\r\n }\r\n}\r\n\r\nexport default Base\r\n","import hasOwn from './hasOwn'\r\n\r\n/**\r\n * 扩展对象\r\n * ========================================================================\r\n * @method extend\r\n * @param {Object} origin\r\n * @param {Object} source\r\n */\r\nconst extend = (origin, source) => {\r\n const keys = Object.keys(source)\r\n\r\n keys.forEach((prop) => {\r\n if (hasOwn(source, prop)) {\r\n origin[prop] = source[prop]\r\n }\r\n })\r\n}\r\n\r\nexport default extend\r\n","import isObject from './isObject'\r\n\r\n/**\r\n * 检测数据是否为 HTMLElement DOM 节点\r\n * ========================================================================\r\n * @method isElement\r\n * @param {*} o\r\n * @returns {boolean}\r\n */\r\nconst isElement = (o) => {\r\n return !!(isObject(o) && o.nodeName && o.tagName && o.nodeType === 1)\r\n}\r\n\r\nexport default isElement\r\n","/**\r\n * 返回给定值的平方值\r\n * ========================================================================\r\n * @method easeInQuad\r\n * @param {Number} x\r\n * @returns {number}\r\n */\r\nconst easeInQuad = (x) => {\r\n return x * x\r\n}\r\n\r\nexport default easeInQuad\r\n","import isString from '../types/isString'\r\nimport isElement from '../types/isElement'\r\n\r\n/**\r\n * 通过给的 scrollElement 参数,获取滚动 DOM 元素\r\n * ========================================================================\r\n * @method _getScrollElement\r\n * @param {String|HTMLElement} scrollElement\r\n * @returns {Element}\r\n * @private\r\n */\r\nconst _getScrollElement = (scrollElement = null) => {\r\n let $rootElements\r\n let $scrollElement\r\n\r\n if (!scrollElement) {\r\n $rootElements = document.querySelectorAll('html,body')\r\n $scrollElement =\r\n $rootElements[0].scrollTop - $rootElements[1].scrollTop >= 0\r\n ? $rootElements[0]\r\n : $rootElements[1]\r\n } else {\r\n if (isString(scrollElement)) {\r\n $scrollElement = document.querySelector(scrollElement)\r\n } else if (isElement(scrollElement)) {\r\n $scrollElement = scrollElement\r\n }\r\n }\r\n\r\n return $scrollElement\r\n}\r\n\r\nexport default _getScrollElement\r\n","/**\r\n * 获取 DOM 节点相对于窗口的 left (纵坐标)值\r\n * ========================================================================\r\n * @method offsetTop\r\n * @param {HTMLElement} el - DOM 节点\r\n * @returns {Number}\r\n */\r\nconst offsetTop = (el) => {\r\n let top = el.offsetTop\r\n\r\n if (el.offsetParent !== null) {\r\n top += offsetTop(el.offsetParent)\r\n }\r\n\r\n return top\r\n}\r\n\r\nexport default offsetTop\r\n","/**\r\n * 获取 options 节点下匹配 selector 选择器的 DOM 节点\r\n * ========================================================================\r\n * Element.matches() 方法可以用来判断 DOM 元素是否与给定的选择器匹配,事件代理判断是\r\n * 否触发绑定的代理事件回调函数,关键就是使用 Element.matches() 辨别当前事件触发的目\r\n * 标 DOM 元素是否为事件代理所期望触发的目标。\r\n * ========================================================================\r\n * @method matches\r\n * @see https://developer.mozilla.org/en-US/docs/web/api/element/matches\r\n * @param {HTMLElement} el - (必须)DOM 元素\r\n * @param {String} selector - (必须)匹配 DOM 元素的选择器\r\n * @returns {Boolean}\r\n */\r\nconst matches = (el, selector = '') => {\r\n const sel = selector.replace(/^>/i, '')\r\n\r\n if (!selector || !sel || !el) {\r\n return false\r\n }\r\n\r\n /* istanbul ignore else */\r\n if (el.matches) {\r\n return el.matches(sel)\r\n } else if (el.msMatchesSelector) {\r\n return el.msMatchesSelector(sel)\r\n } else {\r\n return false\r\n }\r\n}\r\n\r\nexport default matches\r\n","/**\r\n * 获取 DOM 元素的父节点\r\n * ========================================================================\r\n * @method getParentOrHost\r\n * @param {*|HTMLElement} el - (必须)要获取父节点的 DOM 元素\r\n * @returns {*|HTMLElement}\r\n */\r\nconst getParentOrHost = (el) => {\r\n return el.host && el !== document && el.host.nodeType\r\n ? el.host\r\n : el.parentNode\r\n}\r\n\r\nexport default getParentOrHost\r\n","export const CAPTURE_EVENTS = [\r\n 'focusout',\r\n 'blur',\r\n 'focusin',\r\n 'focus',\r\n 'load',\r\n 'unload',\r\n 'mouseenter',\r\n 'mouseleave'\r\n]\r\n","import { CAPTURE_EVENTS } from './enum'\r\nimport _delete from './_delete'\r\n\r\n/**\r\n * (私有方法)取消 type 类型的代理事件绑定\r\n * ========================================================================\r\n * 如果没有设置 handler,则销毁 this.$options 绑定的所有符合 type 事件类型的事件绑定\r\n * ========================================================================\r\n * @method _off\r\n * @param {HTMLElement} el - (必须)取消事件绑定的 DOM 元素\r\n * @param {String} type - (必须)事件类型\r\n * @param {Function} fn - (必须)事件处理器回调函数\r\n * @private\r\n */\r\nconst _off = (el, type, fn) => {\r\n const capture = CAPTURE_EVENTS.indexOf(type) > -1\r\n\r\n /* istanbul ignore else */\r\n if (fn._delegateListener) {\r\n fn = fn._delegateListener\r\n delete fn._delegateListener\r\n }\r\n\r\n // 移除缓存的 _listeners 数据\r\n _delete(el, type, fn)\r\n\r\n el.removeEventListener(type, fn, capture)\r\n}\r\n\r\nexport default _off\r\n","/**\r\n * 删除 DOM 元素缓存的 _listeners 数据\r\n * ========================================================================\r\n * @method _delete\r\n * @param {HTMLElement} el - 要删除 listener 的 DOM 元素\r\n * @param {String} type - 事件类型(名称)\r\n * @param {Function} [fn] - 事件处理器回调函数\r\n */\r\nconst _delete = function (el, type, fn) {\r\n const listeners = el._listeners\r\n let index = -1\r\n\r\n if (listeners.length < 1) {\r\n return false\r\n }\r\n\r\n // 移除缓存的 _listeners 数据\r\n listeners.forEach((listener, i) => {\r\n const handler = listener.fn\r\n\r\n if (type === listener.type) {\r\n index = i\r\n\r\n if (handler === fn) {\r\n index = i\r\n }\r\n }\r\n })\r\n\r\n /* istanbul ignore else */\r\n if (index > -1) {\r\n listeners.splice(index, 1)\r\n }\r\n}\r\n\r\nexport default _delete\r\n","import isString from '../types/isString'\r\nimport isElement from '../types/isElement'\r\nimport getListeners from './getListeners'\r\nimport _off from './_off'\r\n\r\n/**\r\n * 销毁(type 类型的)代理事件绑定\r\n * ========================================================================\r\n * 1. 设置了事件类型 type,则销毁指定类型的事件绑定,否则清除所有代理事件绑定\r\n * 2. recurse 设置为 true,递归销毁子节点全部事件绑定\r\n * ========================================================================\r\n * @method purgeElement\r\n * @param {HTMLElement|String} el - (必须)DOM 元素或者其选择器\r\n * @param {String|Boolean} type - (必须)事件类型\r\n * @param {Boolean} [recurse] - (可选)是否递归销毁子节点所有事件绑定\r\n */\r\nconst purgeElement = function (el, type, recurse = false) {\r\n const $element = isString(el) ? document.querySelector(el) : el\r\n const $children = $element.childNodes\r\n const listeners = getListeners($element, type)\r\n\r\n listeners.forEach((listener) => {\r\n _off($element, listener.type, listener.fn)\r\n })\r\n\r\n if (\r\n (recurse || type === true || arguments.length === 1) &&\r\n $element &&\r\n $children\r\n ) {\r\n $children.forEach(($child) => {\r\n if (isElement($child)) {\r\n purgeElement($child, type, recurse)\r\n }\r\n })\r\n }\r\n}\r\n\r\nexport default purgeElement\r\n","import isString from '../types/isString'\r\n\r\n/**\r\n * 获取 DOM 元素(type 事件类型)事件绑定信息\r\n * ========================================================================\r\n * 如果设置了事件类型 type, 则返回指定类型的事件绑定信息,否则返回所有事件绑定信息\r\n * ========================================================================\r\n * @methods getListeners\r\n * @param {HTMLElement} el - (必须)要获取事件绑定信息的 DOM 元素\r\n * @param {String} [type] - (可选)事件类型\r\n * @returns {Array} - 已绑定的事件信息\r\n */\r\nconst getListeners = (el, type) => {\r\n let listeners = el._listeners || []\r\n\r\n if (isString(type) && type) {\r\n listeners = listeners.filter((listener) => {\r\n return listener.type === type\r\n })\r\n }\r\n\r\n return listeners\r\n}\r\n\r\nexport default getListeners\r\n","import purgeElement from './purgeElement'\r\nimport isFunction from '../types/isFunction'\r\nimport _off from './_off'\r\n\r\n/**\r\n * 取消 type 类型的代理事件绑定\r\n * ========================================================================\r\n * 如果没有设置 handler,则销毁 this.$options 绑定的所有符合 type 事件类型的事件绑定\r\n * ========================================================================\r\n * @method off\r\n * @param {HTMLElement} el - (必须)取消事件绑定的 DOM 元素\r\n * @param {String} type - (必须)事件类型\r\n * @param {Function} [fn] - (可选)事件处理器回调函数\r\n */\r\nconst off = (el, type, fn) => {\r\n // 如果不设置 fn 参数,默认清除 el 元素上绑定的所有事件处理器\r\n if (!isFunction(fn)) {\r\n return purgeElement(el, type)\r\n }\r\n\r\n _off(el, type, fn)\r\n}\r\n\r\nexport default off\r\n","import closest from '../dom/closest'\r\nimport off from './off'\r\nimport getTarget from './getTarget'\r\n\r\nimport { CAPTURE_EVENTS } from './enum'\r\n\r\n/**\r\n * 绑定代理事件\r\n * ========================================================================\r\n * @method on\r\n * @param {HTMLElement|String|Object} el - (必须)绑定代理事件的 DOM 节点\r\n * @param {String} selector - (必须)事件代理目标 DOM 元素的选择器\r\n * @param {String|Function} type - (必须)事件类型或者事件处理器回调函数\r\n * @param {Function|Object} fn - (可选) 事件处理器回调函数或者传递给事件处理器回调函数的数据对象\r\n * @param {Object|Boolean} [data] - (可选)传递给事件处理器回调函数的数据对象或者事件处理器回调函数的 this 上下文指向,\r\n * @param {Object|Boolean} [context] - (可选)事件处理器回调函数的 this 上下文指向,或者是否仅触发一次\r\n * 当设置为 true 时,则事件处理器回调函数的 this 上下文指向为 data 对象\r\n * @param {Boolean} once - (可选)是否仅触发一次\r\n */\r\nconst on = (el, selector, type, fn, data, context, once = false) => {\r\n // CAPTURE_EVENTS 中的特殊事件,采用事件捕获模型\r\n const capture = CAPTURE_EVENTS.indexOf(type) > -1\r\n\r\n const listener = function (evt) {\r\n const target = getTarget(evt)\r\n // 通过 Element.matches 方法获得点击的目标元素\r\n const delegateTarget = closest(target, selector, el)\r\n let overrideContext = context || el\r\n\r\n evt.delegateTarget = delegateTarget\r\n\r\n // 当设置为 true 时,则事件处理器回调函数的\r\n // this 上下文指向为 data 对象\r\n if (context === true) {\r\n overrideContext = data\r\n }\r\n\r\n /* istanbul ignore else */\r\n if (delegateTarget) {\r\n // 仅触发一次\r\n /* istanbul ignore else */\r\n if (once === true) {\r\n off(el, type, listener)\r\n }\r\n\r\n fn.call(overrideContext, evt, data)\r\n }\r\n }\r\n\r\n if (!el._listeners) {\r\n el._listeners = []\r\n }\r\n\r\n // 缓存 options 元素绑定的事件处理器\r\n el._listeners.push({\r\n el,\r\n selector,\r\n type,\r\n fn: listener,\r\n data,\r\n context,\r\n capture\r\n })\r\n\r\n // 缓存包装后的事件处理器\r\n fn._delegateListener = listener\r\n\r\n el.addEventListener(type, listener, capture)\r\n}\r\n\r\nexport default on\r\n","import resolveTextNode from '../dom/resolveTextNode'\r\n\r\n/**\r\n * 返回触发事件的 target DOM 元素\r\n * ========================================================================\r\n * @method getTarget\r\n * @param {Event} evt - Event 对象\r\n * @return {HTMLElement} - Event 对象的 target DOM 元素\r\n */\r\nconst getTarget = function (evt) {\r\n const target = evt.target\r\n\r\n return resolveTextNode(target)\r\n}\r\n\r\nexport default getTarget\r\n","/**\r\n * 在某些情况下,某些浏览器(例如:Safari 浏览器)会返回实际的目标元素内部的文本节点。\r\n * resolveTextNode() 方法则会返回实际的目标节点。\r\n * ========================================================================\r\n * @method resolveTextNode\r\n * @param {HTMLElement|Text} el - 要解析的节点\r\n * @return {*|HTMLElement} - 实际的目标 DOM 节点\r\n */\r\nconst resolveTextNode = function (el) {\r\n if (el && el.nodeType === 3) {\r\n return el.parentNode\r\n }\r\n\r\n return el\r\n}\r\n\r\nexport default resolveTextNode\r\n","import matches from './matches'\r\nimport getParentOrHost from './getParentOrHost'\r\n\r\n/**\r\n * 获取 options 元素父元素最近的包含 selector 选择器的元素\r\n * ========================================================================\r\n * @method closest\r\n * @param {HTMLElement} el - (必须)DOM 元素\r\n * @param {String} selector - (必须)DOM 元素的选择其\r\n * @param {HTMLElement} [ctx] - (必须)比对的 DOM 元素\r\n * @param {Boolean} [includeCTX] - (必须)是否包含 context DOM 元素\r\n * @returns {null|HTMLElement} - 返回最接近的 DOM 元素\r\n */\r\nconst closest = (el, selector, ctx, includeCTX) => {\r\n const context = ctx || document\r\n\r\n if (!el) {\r\n return null\r\n }\r\n\r\n do {\r\n /* istanbul ignore else */\r\n if (\r\n (selector != null &&\r\n (selector.startsWith('>')\r\n ? el.parentNode === context && matches(el, selector)\r\n : matches(el, selector))) ||\r\n (includeCTX && el === context)\r\n ) {\r\n return el\r\n }\r\n\r\n /* istanbul ignore else */\r\n if (el === context) {\r\n break\r\n }\r\n\r\n /* jshint boss:true */\r\n } while ((el = getParentOrHost(el)))\r\n}\r\n\r\nexport default closest\r\n","/**\r\n * 存储订阅者(主题和处理器的)私有对象\r\n * ========================================================================\r\n * @type {{}}\r\n * @private\r\n */\r\nconst _subscribers = {}\r\n\r\nexport default _subscribers\r\n","import _subscribers from './_subscribers'\r\nimport hasOwn from '../lang/hasOwn'\r\n\r\n/**\r\n * 判断是否存在与给定 topic 完全匹配的订阅者信息\r\n * ========================================================================\r\n * @method _hasDirectSubscribersFor\r\n * @param {String} topic - (必须)订阅主题字符串\r\n * @returns {Boolean}\r\n */\r\nconst _hasDirectSubscribersFor = (topic) => {\r\n return hasOwn(_subscribers, topic) && _subscribers[topic].length > 0\r\n}\r\n\r\nexport default _hasDirectSubscribersFor\r\n","import _hasDirectSubscribersFor from './_hasDirectSubscribersFor'\r\nimport _hasSubscribers from './_hasSubscribers'\r\n\r\n/**\r\n * 判断是否存在包含 topic 指定的订阅者信息\r\n * ========================================================================\r\n * @method has\r\n * @param {String} topic - (必须)主题名称\r\n * @param {Boolean} [isDirect] - (可选)是否为直接的主题,默认值:true\r\n * @returns {Boolean}\r\n */\r\nconst has = (topic, isDirect = true) => {\r\n return isDirect ? _hasDirectSubscribersFor(topic) : _hasSubscribers(topic)\r\n}\r\n\r\nexport default has\r\n","import _hasDirectSubscribersFor from './_hasDirectSubscribersFor'\r\n\r\n/**\r\n * 判断是否存在包含给定 topic 相关的订阅者信息\r\n * ========================================================================\r\n * @method _hasSubscribers\r\n * @param {String} topic - (必须)订阅主题字符串\r\n * @returns {Boolean}\r\n */\r\nconst _hasSubscribers = (topic) => {\r\n let found = _hasDirectSubscribersFor(topic)\r\n let position = topic.lastIndexOf('.')\r\n\r\n while (!found && position !== -1) {\r\n topic = topic.substring(0, position)\r\n position = topic.lastIndexOf('.')\r\n found = _hasDirectSubscribersFor(topic)\r\n }\r\n\r\n return found\r\n}\r\n\r\nexport default _hasSubscribers\r\n","import isTypedArray from '../types/isTypedArray'\r\nimport _subscribers from './_subscribers'\r\nimport has from './has'\r\nimport _hasDirectSubscribersFor from './_hasDirectSubscribersFor'\r\n\r\n/**\r\n * (异步)发布订阅主题信息\r\n * ========================================================================\r\n * 主题默认是异步发布的。确保在消费者处理主题时,主题的发起者不会被阻止。\r\n * ========================================================================\r\n * @method emit\r\n * @param {String} topic - (必须)主题名称\r\n * @param {Object} data - (必须)数据对象\r\n * @param {Boolean} async - (可选) 是否异步发布\r\n */\r\nconst emit = (topic, data, async = true) => {\r\n const execute = (topic) => {\r\n if (!_hasDirectSubscribersFor(topic)) {\r\n return false\r\n }\r\n\r\n _subscribers[topic].forEach((subscriber) => {\r\n // 针对 mqtt 消息服务返回的 Uint8Array 类似的 typed arrays 格式的数据\r\n // 采用 toString() 方法转化为普通(JSON)字符串\r\n const message = isTypedArray(data) ? data.toString() : data\r\n\r\n subscriber.callback.call(subscriber.context || subscriber, message)\r\n })\r\n }\r\n const deliver = () => {\r\n let subscriber = topic\r\n let position = topic.lastIndexOf('.')\r\n\r\n while (position !== -1) {\r\n subscriber = subscriber.substring(0, position)\r\n position = subscriber.lastIndexOf('.')\r\n\r\n execute(subscriber)\r\n }\r\n\r\n // 执行 topic 对应的处理器\r\n execute(topic)\r\n // 执行特殊 topic:'*'(监听全部消息的发布)\r\n execute('*')\r\n }\r\n\r\n if (!has(topic)) {\r\n return false\r\n }\r\n\r\n if (async) {\r\n setTimeout(deliver, 10)\r\n } else {\r\n deliver()\r\n }\r\n}\r\n\r\nexport default emit\r\n","import toString from '../lang/toString'\r\n/**\r\n * 判断检测数据是否为 Typed Arrays 类型的数据\r\n * ========================================================================\r\n * @param {*} val\r\n * @returns {boolean}\r\n */\r\nconst isTypedArray = (val) => {\r\n const TYPES = [\r\n '[object Int8Array]',\r\n '[object Uint8Array]',\r\n '[object Uint8ClampedArray]',\r\n '[object Int16Array]',\r\n '[object Uint16Array]',\r\n '[object Int32Array]',\r\n '[object Uint32Array]',\r\n '[object Float32Array]',\r\n '[object Float64Array]',\r\n '[object BigInt64Array]',\r\n '[object BigUint64Array]'\r\n ]\r\n\r\n return TYPES.indexOf(toString(val)) > -1\r\n}\r\n\r\nexport default isTypedArray\r\n","import toString from '../lang/toString'\r\n\r\n/**\r\n * 检测数据是否为 Array 类型\r\n * ========================================================================\r\n * @method isArray\r\n * @param {*} o\r\n * @returns {boolean}\r\n */\r\nconst isArray = (o) => {\r\n if (Array.isArray) {\r\n return Array.isArray(o)\r\n } else {\r\n return toString(o) === '[object Array]'\r\n }\r\n}\r\n\r\nexport default isArray\r\n","import DEFAULTS from './defaults'\r\n\r\nconst SYMBOLS = [...DEFAULTS]\r\n\r\nexport default SYMBOLS\r\n","const DEFAULTS = [\r\n '',\r\n '',\r\n '',\r\n '',\r\n '',\r\n '',\r\n '',\r\n '',\r\n ''\r\n]\r\n\r\nexport default DEFAULTS\r\n","import isString from '../types/isString'\r\nimport getSymbol from './getSymbol'\r\nimport SYMBOLS from './symbols'\r\n\r\n/**\r\n *\r\n * @method getSymbols\r\n * @param {String} [name]\r\n * @param {String} [iconSet]\r\n * @returns {string[]|*}\r\n */\r\nconst getSymbols = (name, iconSet = 'icon') => {\r\n if (isString(name)) {\r\n return getSymbol(name, iconSet)\r\n }\r\n\r\n return [...SYMBOLS]\r\n}\r\n\r\nexport default getSymbols\r\n","import SYMBOLS from './symbols'\r\n\r\n/**\r\n * @method getSymbol\r\n * @param {String} name\r\n * @param {String} [iconSet]\r\n * @returns {String}\r\n */\r\nconst getSymbol = (name, iconSet = 'icon') => {\r\n const patternName = /id=\"(.*?)\"/\r\n const patternSet = /^(\\w+)-/\r\n const symbols = SYMBOLS\r\n\r\n return symbols.find((symbol) => {\r\n const names = patternName.exec(symbol)\r\n const fullName = names[1]\r\n const sets = patternSet.exec(fullName)\r\n const setName = sets[1]\r\n const iconName =\r\n iconSet === 'icon' ? `${iconSet}-${name}` : `${iconSet}-icon-${name}`\r\n\r\n return setName === iconSet && fullName === iconName\r\n })\r\n}\r\n\r\nexport default getSymbol\r\n","import add from './add'\r\nimport getSymbols from './getSymbols'\r\n\r\n/**\r\n * 绘制 SVG 图标集\r\n * ========================================================================\r\n * @method paint\r\n * @param {String|Array} symbol\r\n */\r\nconst paint = (symbol = '') => {\r\n const $body = document.body\r\n let $icons = document.querySelector('#outline-icons')\r\n let symbols = []\r\n\r\n add(symbol)\r\n symbols = getSymbols()\r\n\r\n if ($icons) {\r\n $icons.innerHTML = symbols.join('')\r\n } else {\r\n $icons = document.createElement('div')\r\n $icons.innerHTML =\r\n ``\r\n $body.insertBefore($icons.firstChild, $body.firstChild)\r\n }\r\n}\r\n\r\nexport default paint\r\n","import isArray from '../types/isArray'\r\nimport isString from '../types/isString'\r\nimport SYMBOLS from './symbols'\r\n\r\n/**\r\n * @method add\r\n * @param {Array|String} symbols\r\n * @return {Boolean}\r\n */\r\nconst add = (symbols) => {\r\n if (!symbols) {\r\n return false\r\n }\r\n\r\n if (isArray(symbols) && symbols.length > 0) {\r\n symbols.forEach((symbol) => {\r\n /* istanbul ignore else */\r\n if (SYMBOLS.indexOf(symbol) === -1 && isString(symbol)) {\r\n SYMBOLS.push(symbol)\r\n }\r\n })\r\n } else {\r\n /* istanbul ignore else */\r\n if (isString(symbols)) {\r\n SYMBOLS.push(symbols)\r\n }\r\n }\r\n}\r\n\r\nexport default add\r\n","import isString from '../types/isString'\r\n\r\n/**\r\n * 清楚字符串起始位置所有的空格\r\n * ========================================================================\r\n * @method trim\r\n * @param {string} str\r\n * @returns {string|Boolean}\r\n */\r\nconst trim = (str) => {\r\n if (!isString(str)) {\r\n return false\r\n }\r\n return str.replace(/(^\\s+)|(\\s+$)/g, '')\r\n}\r\n\r\nexport default trim\r\n","import isObject from './isObject'\r\nimport isElement from './isElement'\r\nimport isHTMLCollection from './isHTMLCollection'\r\nimport isFragment from './isFragment'\r\nimport isTextNode from './isTextNode'\r\n\r\nconst isDOM = (el) => {\r\n return !!(\r\n isObject(el) &&\r\n (isElement(el) || isHTMLCollection(el) || isFragment(el) || isTextNode(el))\r\n )\r\n}\r\n\r\nexport default isDOM\r\n","import toString from '../lang/toString'\r\nimport isObject from './isObject'\r\n\r\nconst isHTMLCollection = (el) => {\r\n return !!(isObject(el) && toString(el) === '[object NodeList]')\r\n}\r\n\r\nexport default isHTMLCollection\r\n","import toString from '../lang/toString'\r\nimport isObject from './isObject'\r\n\r\nconst isFragment = (fragment) => {\r\n return !!(\r\n isObject(fragment) && toString(fragment) === '[object DocumentFragment]'\r\n )\r\n}\r\n\r\nexport default isFragment\r\n","import toString from '../lang/toString'\r\nimport isObject from './isObject'\r\n\r\nconst isTextNode = (el) => {\r\n return !!(\r\n isObject(el) &&\r\n (toString(el) === '[object Text]' || (el.tagName && el.nodeType === 3))\r\n )\r\n}\r\n\r\nexport default isTextNode\r\n","/**\r\n * 给 DOM 节点设置属性/值\r\n * ========================================================================\r\n * @method setAttribute\r\n * @param {HTMLElement} el - DOM 节点\r\n * @param {String} attr - 属性名称\r\n * @param {String|Number|Boolean} value - 属性值\r\n */\r\nconst setAttribute = (el, attr, value) => {\r\n let tagName = el.tagName.toLowerCase()\r\n\r\n switch (attr) {\r\n case 'style':\r\n el.style.cssText = value\r\n break\r\n case 'value':\r\n if (tagName === 'input' || tagName === 'textarea') {\r\n el.value = value\r\n } else {\r\n el.setAttribute(attr, value)\r\n }\r\n break\r\n case 'className':\r\n el.className = value\r\n break\r\n default:\r\n el.setAttribute(attr, value)\r\n break\r\n }\r\n}\r\n\r\nexport default setAttribute\r\n","import isString from './isString'\r\n\r\nconst isSVG = (str) => {\r\n const declaration = '(?:<\\\\?xml[^>]*>\\\\s*)?'\r\n const doctype =\r\n '(?:<\\\\!doctype svg[^>]*\\\\s*(?:\\\\[?(?:\\\\s*]*>\\\\s*)*\\\\]?)*[^>]*>\\\\s*)?'\r\n const content = '