`s.
+
+.nav {
+ display: flex;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: $nav-link-padding-y $nav-link-padding-x;
+ @include font-size($nav-link-font-size);
+ font-weight: $nav-link-font-weight;
+ color: $nav-link-color;
+ text-decoration: if($link-decoration == none, null, none);
+ @include transition($nav-link-transition);
+
+ &:hover,
+ &:focus {
+ color: $nav-link-hover-color;
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ }
+
+ // Disabled state lightens text
+ &.disabled {
+ color: $nav-link-disabled-color;
+ pointer-events: none;
+ cursor: default;
+ }
+}
+
+//
+// Tabs
+//
+
+.nav-tabs {
+ border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
+
+ .nav-link {
+ margin-bottom: -$nav-tabs-border-width;
+ background: none;
+ border: $nav-tabs-border-width solid transparent;
+ @include border-top-radius($nav-tabs-border-radius);
+
+ &:hover,
+ &:focus {
+ border-color: $nav-tabs-link-hover-border-color;
+ // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
+ isolation: isolate;
+ }
+
+ &.disabled {
+ color: $nav-link-disabled-color;
+ background-color: transparent;
+ border-color: transparent;
+ }
+ }
+
+ .nav-link.active,
+ .nav-item.show .nav-link {
+ color: $nav-tabs-link-active-color;
+ background-color: $nav-tabs-link-active-bg;
+ border-color: $nav-tabs-link-active-border-color;
+ }
+
+ .dropdown-menu {
+ // Make dropdown border overlap tab border
+ margin-top: -$nav-tabs-border-width;
+ // Remove the top rounded corners here since there is a hard edge above the menu
+ @include border-top-radius(0);
+ }
+}
+
+
+//
+// Pills
+//
+
+.nav-pills {
+ .nav-link {
+ background: none;
+ border: 0;
+ @include border-radius($nav-pills-border-radius);
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ color: $nav-pills-link-active-color;
+ @include gradient-bg($nav-pills-link-active-bg);
+ }
+}
+
+
+//
+// Justified variants
+//
+
+.nav-fill {
+ > .nav-link,
+ .nav-item {
+ flex: 1 1 auto;
+ text-align: center;
+ }
+}
+
+.nav-justified {
+ > .nav-link,
+ .nav-item {
+ flex-basis: 0;
+ flex-grow: 1;
+ text-align: center;
+ }
+}
+
+.nav-fill,
+.nav-justified {
+ .nav-item .nav-link {
+ width: 100%; // Make sure button will grow
+ }
+}
+
+
+// Tabbable tabs
+//
+// Hide tabbable panes to start, show them when `.active`
+
+.tab-content {
+ > .tab-pane {
+ display: none;
+ }
+ > .active {
+ display: block;
+ }
+}
diff --git a/node_modules/bootstrap/scss/_navbar.scss b/node_modules/bootstrap/scss/_navbar.scss
new file mode 100644
index 00000000..2ccef11b
--- /dev/null
+++ b/node_modules/bootstrap/scss/_navbar.scss
@@ -0,0 +1,306 @@
+// Contents
+//
+// Navbar
+// Navbar brand
+// Navbar nav
+// Navbar text
+// Responsive navbar
+// Navbar position
+// Navbar themes
+
+
+// Navbar
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // allow us to do the line break for collapsing content
+ align-items: center;
+ justify-content: space-between; // space out brand from logo
+ padding-top: $navbar-padding-y;
+ padding-right: $navbar-padding-x; // default: null
+ padding-bottom: $navbar-padding-y;
+ padding-left: $navbar-padding-x; // default: null
+ @include gradient-bg();
+
+ // Because flex properties aren't inherited, we need to redeclare these first
+ // few properties so that content nested within behave properly.
+ // The `flex-wrap` property is inherited to simplify the expanded navbars
+ %container-flex-properties {
+ display: flex;
+ flex-wrap: inherit;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ > .container,
+ > .container-fluid {
+ @extend %container-flex-properties;
+ }
+
+ @each $breakpoint, $container-max-width in $container-max-widths {
+ > .container#{breakpoint-infix($breakpoint, $container-max-widths)} {
+ @extend %container-flex-properties;
+ }
+ }
+}
+
+
+// Navbar brand
+//
+// Used for brand, project, or site names.
+
+.navbar-brand {
+ padding-top: $navbar-brand-padding-y;
+ padding-bottom: $navbar-brand-padding-y;
+ margin-right: $navbar-brand-margin-end;
+ @include font-size($navbar-brand-font-size);
+ text-decoration: if($link-decoration == none, null, none);
+ white-space: nowrap;
+
+ &:hover,
+ &:focus {
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ }
+}
+
+
+// Navbar nav
+//
+// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
+
+.navbar-nav {
+ display: flex;
+ flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+
+ .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ .dropdown-menu {
+ position: static;
+ }
+}
+
+
+// Navbar text
+//
+//
+
+.navbar-text {
+ padding-top: $nav-link-padding-y;
+ padding-bottom: $nav-link-padding-y;
+}
+
+
+// Responsive navbar
+//
+// Custom styles for responsive collapsing and toggling of navbar contents.
+// Powered by the collapse Bootstrap JavaScript plugin.
+
+// When collapsed, prevent the toggleable navbar contents from appearing in
+// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
+// on the `.navbar` parent.
+.navbar-collapse {
+ flex-basis: 100%;
+ flex-grow: 1;
+ // For always expanded or extra full navbars, ensure content aligns itself
+ // properly vertically. Can be easily overridden with flex utilities.
+ align-items: center;
+}
+
+// Button for toggling the navbar when in its collapsed state
+.navbar-toggler {
+ padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
+ @include font-size($navbar-toggler-font-size);
+ line-height: 1;
+ background-color: transparent; // remove default button style
+ border: $border-width solid transparent; // remove default button style
+ @include border-radius($navbar-toggler-border-radius);
+ @include transition($navbar-toggler-transition);
+
+ &:hover {
+ text-decoration: none;
+ }
+
+ &:focus {
+ text-decoration: none;
+ outline: 0;
+ box-shadow: 0 0 0 $navbar-toggler-focus-width;
+ }
+}
+
+// Keep as a separate element so folks can easily override it with another icon
+// or image file as needed.
+.navbar-toggler-icon {
+ display: inline-block;
+ width: 1.5em;
+ height: 1.5em;
+ vertical-align: middle;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 100%;
+}
+
+.navbar-nav-scroll {
+ max-height: var(--#{$variable-prefix}scroll-height, 75vh);
+ overflow-y: auto;
+}
+
+// scss-docs-start navbar-expand-loop
+// Generate series of `.navbar-expand-*` responsive classes for configuring
+// where your navbar collapses.
+.navbar-expand {
+ @each $breakpoint in map-keys($grid-breakpoints) {
+ $next: breakpoint-next($breakpoint, $grid-breakpoints);
+ $infix: breakpoint-infix($next, $grid-breakpoints);
+
+ // stylelint-disable-next-line scss/selector-no-union-class-name
+ {$infix} {
+ @include media-breakpoint-up($next) {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+
+ .navbar-nav {
+ flex-direction: row;
+
+ .dropdown-menu {
+ position: absolute;
+ }
+
+ .nav-link {
+ padding-right: $navbar-nav-link-padding-x;
+ padding-left: $navbar-nav-link-padding-x;
+ }
+ }
+
+ .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-collapse {
+ display: flex !important; // stylelint-disable-line declaration-no-important
+ flex-basis: auto;
+ }
+
+ .navbar-toggler {
+ display: none;
+ }
+ }
+ }
+ }
+}
+// scss-docs-end navbar-expand-loop
+
+
+// Navbar themes
+//
+// Styles for switching between navbars with light or dark background.
+
+// Dark links against a light background
+.navbar-light {
+ .navbar-brand {
+ color: $navbar-light-brand-color;
+
+ &:hover,
+ &:focus {
+ color: $navbar-light-brand-hover-color;
+ }
+ }
+
+ .navbar-nav {
+ .nav-link {
+ color: $navbar-light-color;
+
+ &:hover,
+ &:focus {
+ color: $navbar-light-hover-color;
+ }
+
+ &.disabled {
+ color: $navbar-light-disabled-color;
+ }
+ }
+
+ .show > .nav-link,
+ .nav-link.active {
+ color: $navbar-light-active-color;
+ }
+ }
+
+ .navbar-toggler {
+ color: $navbar-light-color;
+ border-color: $navbar-light-toggler-border-color;
+ }
+
+ .navbar-toggler-icon {
+ background-image: escape-svg($navbar-light-toggler-icon-bg);
+ }
+
+ .navbar-text {
+ color: $navbar-light-color;
+
+ a,
+ a:hover,
+ a:focus {
+ color: $navbar-light-active-color;
+ }
+ }
+}
+
+// White links against a dark background
+.navbar-dark {
+ .navbar-brand {
+ color: $navbar-dark-brand-color;
+
+ &:hover,
+ &:focus {
+ color: $navbar-dark-brand-hover-color;
+ }
+ }
+
+ .navbar-nav {
+ .nav-link {
+ color: $navbar-dark-color;
+
+ &:hover,
+ &:focus {
+ color: $navbar-dark-hover-color;
+ }
+
+ &.disabled {
+ color: $navbar-dark-disabled-color;
+ }
+ }
+
+ .show > .nav-link,
+ .nav-link.active {
+ color: $navbar-dark-active-color;
+ }
+ }
+
+ .navbar-toggler {
+ color: $navbar-dark-color;
+ border-color: $navbar-dark-toggler-border-color;
+ }
+
+ .navbar-toggler-icon {
+ background-image: escape-svg($navbar-dark-toggler-icon-bg);
+ }
+
+ .navbar-text {
+ color: $navbar-dark-color;
+ a,
+ a:hover,
+ a:focus {
+ color: $navbar-dark-active-color;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/_offcanvas.scss b/node_modules/bootstrap/scss/_offcanvas.scss
new file mode 100644
index 00000000..f1d99456
--- /dev/null
+++ b/node_modules/bootstrap/scss/_offcanvas.scss
@@ -0,0 +1,77 @@
+.offcanvas {
+ position: fixed;
+ bottom: 0;
+ z-index: $zindex-offcanvas;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: $offcanvas-color;
+ visibility: hidden;
+ background-color: $offcanvas-bg-color;
+ background-clip: padding-box;
+ outline: 0;
+ @include box-shadow($offcanvas-box-shadow);
+ @include transition(transform $offcanvas-transition-duration ease-in-out);
+}
+
+.offcanvas-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: $offcanvas-padding-y $offcanvas-padding-x;
+
+ .btn-close {
+ padding: ($offcanvas-padding-y / 2) ($offcanvas-padding-x / 2);
+ margin: ($offcanvas-padding-y / -2) ($offcanvas-padding-x / -2) ($offcanvas-padding-y / -2) auto;
+ }
+}
+
+.offcanvas-title {
+ margin-bottom: 0;
+ line-height: $offcanvas-title-line-height;
+}
+
+.offcanvas-body {
+ flex-grow: 1;
+ padding: $offcanvas-padding-y $offcanvas-padding-x;
+ overflow-y: auto;
+}
+
+.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: $offcanvas-horizontal-width;
+ border-right: $offcanvas-border-width solid $offcanvas-border-color;
+ transform: translateX(-100%);
+}
+
+.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: $offcanvas-horizontal-width;
+ border-left: $offcanvas-border-width solid $offcanvas-border-color;
+ transform: translateX(100%);
+}
+
+.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: $offcanvas-vertical-height;
+ max-height: 100%;
+ border-bottom: $offcanvas-border-width solid $offcanvas-border-color;
+ transform: translateY(-100%);
+}
+
+.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: $offcanvas-vertical-height;
+ max-height: 100%;
+ border-top: $offcanvas-border-width solid $offcanvas-border-color;
+ transform: translateY(100%);
+}
+
+.offcanvas.show {
+ transform: none;
+}
diff --git a/node_modules/bootstrap/scss/_pagination.scss b/node_modules/bootstrap/scss/_pagination.scss
new file mode 100644
index 00000000..9c466662
--- /dev/null
+++ b/node_modules/bootstrap/scss/_pagination.scss
@@ -0,0 +1,64 @@
+.pagination {
+ display: flex;
+ @include list-unstyled();
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ color: $pagination-color;
+ text-decoration: if($link-decoration == none, null, none);
+ background-color: $pagination-bg;
+ border: $pagination-border-width solid $pagination-border-color;
+ @include transition($pagination-transition);
+
+ &:hover {
+ z-index: 2;
+ color: $pagination-hover-color;
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ background-color: $pagination-hover-bg;
+ border-color: $pagination-hover-border-color;
+ }
+
+ &:focus {
+ z-index: 3;
+ color: $pagination-focus-color;
+ background-color: $pagination-focus-bg;
+ outline: $pagination-focus-outline;
+ box-shadow: $pagination-focus-box-shadow;
+ }
+}
+
+.page-item {
+ &:not(:first-child) .page-link {
+ margin-left: $pagination-margin-start;
+ }
+
+ &.active .page-link {
+ z-index: 3;
+ color: $pagination-active-color;
+ @include gradient-bg($pagination-active-bg);
+ border-color: $pagination-active-border-color;
+ }
+
+ &.disabled .page-link {
+ color: $pagination-disabled-color;
+ pointer-events: none;
+ background-color: $pagination-disabled-bg;
+ border-color: $pagination-disabled-border-color;
+ }
+}
+
+
+//
+// Sizing
+//
+@include pagination-size($pagination-padding-y, $pagination-padding-x, null, $pagination-border-radius);
+
+.pagination-lg {
+ @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $pagination-border-radius-lg);
+}
+
+.pagination-sm {
+ @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $pagination-border-radius-sm);
+}
diff --git a/node_modules/bootstrap/scss/_popover.scss b/node_modules/bootstrap/scss/_popover.scss
new file mode 100644
index 00000000..a55555e2
--- /dev/null
+++ b/node_modules/bootstrap/scss/_popover.scss
@@ -0,0 +1,158 @@
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0 #{"/* rtl:ignore */"};
+ z-index: $zindex-popover;
+ display: block;
+ max-width: $popover-max-width;
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ @include reset-text();
+ @include font-size($popover-font-size);
+ // Allow breaking very long words so they don't overflow the popover's bounds
+ word-wrap: break-word;
+ background-color: $popover-bg;
+ background-clip: padding-box;
+ border: $popover-border-width solid $popover-border-color;
+ @include border-radius($popover-border-radius);
+ @include box-shadow($popover-box-shadow);
+
+ .popover-arrow {
+ position: absolute;
+ display: block;
+ width: $popover-arrow-width;
+ height: $popover-arrow-height;
+
+ &::before,
+ &::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ }
+ }
+}
+
+.bs-popover-top {
+ > .popover-arrow {
+ bottom: subtract(-$popover-arrow-height, $popover-border-width);
+
+ &::before {
+ bottom: 0;
+ border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
+ border-top-color: $popover-arrow-outer-color;
+ }
+
+ &::after {
+ bottom: $popover-border-width;
+ border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
+ border-top-color: $popover-arrow-color;
+ }
+ }
+}
+
+.bs-popover-end {
+ > .popover-arrow {
+ left: subtract(-$popover-arrow-height, $popover-border-width);
+ width: $popover-arrow-height;
+ height: $popover-arrow-width;
+
+ &::before {
+ left: 0;
+ border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
+ border-right-color: $popover-arrow-outer-color;
+ }
+
+ &::after {
+ left: $popover-border-width;
+ border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
+ border-right-color: $popover-arrow-color;
+ }
+ }
+}
+
+.bs-popover-bottom {
+ > .popover-arrow {
+ top: subtract(-$popover-arrow-height, $popover-border-width);
+
+ &::before {
+ top: 0;
+ border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
+ border-bottom-color: $popover-arrow-outer-color;
+ }
+
+ &::after {
+ top: $popover-border-width;
+ border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
+ border-bottom-color: $popover-arrow-color;
+ }
+ }
+
+ // This will remove the popover-header's border just below the arrow
+ .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: $popover-arrow-width;
+ margin-left: -$popover-arrow-width / 2;
+ content: "";
+ border-bottom: $popover-border-width solid $popover-header-bg;
+ }
+}
+
+.bs-popover-start {
+ > .popover-arrow {
+ right: subtract(-$popover-arrow-height, $popover-border-width);
+ width: $popover-arrow-height;
+ height: $popover-arrow-width;
+
+ &::before {
+ right: 0;
+ border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
+ border-left-color: $popover-arrow-outer-color;
+ }
+
+ &::after {
+ right: $popover-border-width;
+ border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
+ border-left-color: $popover-arrow-color;
+ }
+ }
+}
+
+.bs-popover-auto {
+ &[data-popper-placement^="top"] {
+ @extend .bs-popover-top;
+ }
+ &[data-popper-placement^="right"] {
+ @extend .bs-popover-end;
+ }
+ &[data-popper-placement^="bottom"] {
+ @extend .bs-popover-bottom;
+ }
+ &[data-popper-placement^="left"] {
+ @extend .bs-popover-start;
+ }
+}
+
+// Offset the popover to account for the popover arrow
+.popover-header {
+ padding: $popover-header-padding-y $popover-header-padding-x;
+ margin-bottom: 0; // Reset the default from Reboot
+ @include font-size($font-size-base);
+ color: $popover-header-color;
+ background-color: $popover-header-bg;
+ border-bottom: $popover-border-width solid shade-color($popover-header-bg, 10%);
+ @include border-top-radius($popover-inner-border-radius);
+
+ &:empty {
+ display: none;
+ }
+}
+
+.popover-body {
+ padding: $popover-body-padding-y $popover-body-padding-x;
+ color: $popover-body-color;
+}
diff --git a/node_modules/bootstrap/scss/_progress.scss b/node_modules/bootstrap/scss/_progress.scss
new file mode 100644
index 00000000..5715ab00
--- /dev/null
+++ b/node_modules/bootstrap/scss/_progress.scss
@@ -0,0 +1,48 @@
+// Disable animation if transitions are disabled
+
+// scss-docs-start progress-keyframes
+@if $enable-transitions {
+ @keyframes progress-bar-stripes {
+ 0% { background-position-x: $progress-height; }
+ }
+}
+// scss-docs-end progress-keyframes
+
+.progress {
+ display: flex;
+ height: $progress-height;
+ overflow: hidden; // force rounded corners by cropping it
+ @include font-size($progress-font-size);
+ background-color: $progress-bg;
+ @include border-radius($progress-border-radius);
+ @include box-shadow($progress-box-shadow);
+}
+
+.progress-bar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ overflow: hidden;
+ color: $progress-bar-color;
+ text-align: center;
+ white-space: nowrap;
+ background-color: $progress-bar-bg;
+ @include transition($progress-bar-transition);
+}
+
+.progress-bar-striped {
+ @include gradient-striped();
+ background-size: $progress-height $progress-height;
+}
+
+@if $enable-transitions {
+ .progress-bar-animated {
+ animation: $progress-bar-animation-timing progress-bar-stripes;
+
+ @if $enable-reduced-motion {
+ @media (prefers-reduced-motion: reduce) {
+ animation: none;
+ }
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/_reboot.scss b/node_modules/bootstrap/scss/_reboot.scss
new file mode 100644
index 00000000..35204698
--- /dev/null
+++ b/node_modules/bootstrap/scss/_reboot.scss
@@ -0,0 +1,621 @@
+// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+
+
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+//
+// Normalize is licensed MIT. https://github.com/necolas/normalize.css
+
+
+// Document
+//
+// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+
+// Root
+//
+// Ability to the value of the root font sizes, affecting the value of `rem`.
+// null by default, thus nothing is generated.
+
+:root {
+ font-size: $font-size-root;
+
+ @if $enable-smooth-scroll {
+ @media (prefers-reduced-motion: no-preference) {
+ scroll-behavior: smooth;
+ }
+ }
+}
+
+
+// Body
+//
+// 1. Remove the margin in all browsers.
+// 2. As a best practice, apply a default `background-color`.
+// 3. Prevent adjustments of font size after orientation changes in iOS.
+// 4. Change the default tap highlight to be completely transparent in iOS.
+
+body {
+ margin: 0; // 1
+ font-family: $font-family-base;
+ @include font-size($font-size-base);
+ font-weight: $font-weight-base;
+ line-height: $line-height-base;
+ color: $body-color;
+ text-align: $body-text-align;
+ background-color: $body-bg; // 2
+ -webkit-text-size-adjust: 100%; // 3
+ -webkit-tap-highlight-color: rgba($black, 0); // 4
+}
+
+
+// Content grouping
+//
+// 1. Reset Firefox's gray color
+// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field
+
+hr {
+ margin: $hr-margin-y 0;
+ color: $hr-color; // 1
+ background-color: currentColor;
+ border: 0;
+ opacity: $hr-opacity;
+}
+
+hr:not([size]) {
+ height: $hr-height; // 2
+}
+
+
+// Typography
+//
+// 1. Remove top margins from headings
+// By default, ``-`` all receive top and bottom margins. We nuke the top
+// margin for easier control within type scales as it avoids margin collapsing.
+
+%heading {
+ margin-top: 0; // 1
+ margin-bottom: $headings-margin-bottom;
+ font-family: $headings-font-family;
+ font-style: $headings-font-style;
+ font-weight: $headings-font-weight;
+ line-height: $headings-line-height;
+ color: $headings-color;
+}
+
+h1 {
+ @extend %heading;
+ @include font-size($h1-font-size);
+}
+
+h2 {
+ @extend %heading;
+ @include font-size($h2-font-size);
+}
+
+h3 {
+ @extend %heading;
+ @include font-size($h3-font-size);
+}
+
+h4 {
+ @extend %heading;
+ @include font-size($h4-font-size);
+}
+
+h5 {
+ @extend %heading;
+ @include font-size($h5-font-size);
+}
+
+h6 {
+ @extend %heading;
+ @include font-size($h6-font-size);
+}
+
+
+// Reset margins on paragraphs
+//
+// Similarly, the top margin on ` `s get reset. However, we also reset the
+// bottom margin to use `rem` units instead of `em`.
+
+p {
+ margin-top: 0;
+ margin-bottom: $paragraph-margin-bottom;
+}
+
+
+// Abbreviations
+//
+// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin
+// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
+// 3. Add explicit cursor to indicate changed behavior.
+// 4. Prevent the text-decoration to be skipped.
+
+abbr[title],
+abbr[data-bs-original-title] { // 1
+ text-decoration: underline dotted; // 2
+ cursor: help; // 3
+ text-decoration-skip-ink: none; // 4
+}
+
+
+// Address
+
+address {
+ margin-bottom: 1rem;
+ font-style: normal;
+ line-height: inherit;
+}
+
+
+// Lists
+
+ol,
+ul {
+ padding-left: 2rem;
+}
+
+ol,
+ul,
+dl {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+ margin-bottom: 0;
+}
+
+dt {
+ font-weight: $dt-font-weight;
+}
+
+// 1. Undo browser default
+
+dd {
+ margin-bottom: .5rem;
+ margin-left: 0; // 1
+}
+
+
+// Blockquote
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+
+// Strong
+//
+// Add the correct font weight in Chrome, Edge, and Safari
+
+b,
+strong {
+ font-weight: $font-weight-bolder;
+}
+
+
+// Small
+//
+// Add the correct font size in all browsers
+
+small {
+ @include font-size($small-font-size);
+}
+
+
+// Mark
+
+mark {
+ padding: $mark-padding;
+ background-color: $mark-bg;
+}
+
+
+// Sub and Sup
+//
+// Prevent `sub` and `sup` elements from affecting the line height in
+// all browsers.
+
+sub,
+sup {
+ position: relative;
+ @include font-size($sub-sup-font-size);
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub { bottom: -.25em; }
+sup { top: -.5em; }
+
+
+// Links
+
+a {
+ color: $link-color;
+ text-decoration: $link-decoration;
+
+ &:hover {
+ color: $link-hover-color;
+ text-decoration: $link-hover-decoration;
+ }
+}
+
+// And undo these styles for placeholder links/named anchors (without href).
+// It would be more straightforward to just use a[href] in previous block, but that
+// causes specificity issues in many other styles that are too complex to fix.
+// See https://github.com/twbs/bootstrap/issues/19402
+
+a:not([href]):not([class]) {
+ &,
+ &:hover {
+ color: inherit;
+ text-decoration: none;
+ }
+}
+
+
+// Code
+
+pre,
+code,
+kbd,
+samp {
+ font-family: $font-family-code;
+ @include font-size(1em); // Correct the odd `em` font sizing in all browsers.
+ direction: ltr #{"/* rtl:ignore */"};
+ unicode-bidi: bidi-override;
+}
+
+// 1. Remove browser default top margin
+// 2. Reset browser default of `1em` to use `rem`s
+// 3. Don't allow content to break outside
+
+pre {
+ display: block;
+ margin-top: 0; // 1
+ margin-bottom: 1rem; // 2
+ overflow: auto; // 3
+ @include font-size($code-font-size);
+ color: $pre-color;
+
+ // Account for some code outputs that place code tags in pre tags
+ code {
+ @include font-size(inherit);
+ color: inherit;
+ word-break: normal;
+ }
+}
+
+code {
+ @include font-size($code-font-size);
+ color: $code-color;
+ word-wrap: break-word;
+
+ // Streamline the style when inside anchors to avoid broken underline and more
+ a > & {
+ color: inherit;
+ }
+}
+
+kbd {
+ padding: $kbd-padding-y $kbd-padding-x;
+ @include font-size($kbd-font-size);
+ color: $kbd-color;
+ background-color: $kbd-bg;
+ @include border-radius($border-radius-sm);
+
+ kbd {
+ padding: 0;
+ @include font-size(1em);
+ font-weight: $nested-kbd-font-weight;
+ }
+}
+
+
+// Figures
+//
+// Apply a consistent margin strategy (matches our type styles).
+
+figure {
+ margin: 0 0 1rem;
+}
+
+
+// Images and content
+
+img,
+svg {
+ vertical-align: middle;
+}
+
+
+// Tables
+//
+// Prevent double borders
+
+table {
+ caption-side: bottom;
+ border-collapse: collapse;
+}
+
+caption {
+ padding-top: $table-cell-padding-y;
+ padding-bottom: $table-cell-padding-y;
+ color: $table-caption-color;
+ text-align: left;
+}
+
+// 1. Removes font-weight bold by inheriting
+// 2. Matches default `
` alignment by inheriting `text-align`.
+// 3. Fix alignment for Safari
+
+th {
+ font-weight: $table-th-font-weight; // 1
+ text-align: inherit; // 2
+ text-align: -webkit-match-parent; // 3
+}
+
+thead,
+tbody,
+tfoot,
+tr,
+td,
+th {
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+}
+
+
+// Forms
+//
+// 1. Allow labels to use `margin` for spacing.
+
+label {
+ display: inline-block; // 1
+}
+
+// Remove the default `border-radius` that macOS Chrome adds.
+// See https://github.com/twbs/bootstrap/issues/24093
+
+button {
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 0;
+}
+
+// Explicitly remove focus outline in Chromium when it shouldn't be
+// visible (e.g. as result of mouse click or touch tap). It already
+// should be doing this automatically, but seems to currently be
+// confused and applies its very visible two-tone outline anyway.
+
+button:focus:not(:focus-visible) {
+ outline: 0;
+}
+
+// 1. Remove the margin in Firefox and Safari
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0; // 1
+ font-family: inherit;
+ @include font-size(inherit);
+ line-height: inherit;
+}
+
+// Remove the inheritance of text transform in Firefox
+button,
+select {
+ text-transform: none;
+}
+// Set the cursor for non-`` buttons
+//
+// Details at https://github.com/twbs/bootstrap/pull/30562
+[role="button"] {
+ cursor: pointer;
+}
+
+select {
+ // Remove the inheritance of word-wrap in Safari.
+ // See https://github.com/twbs/bootstrap/issues/24990
+ word-wrap: normal;
+
+ // Undo the opacity change from Chrome
+ &:disabled {
+ opacity: 1;
+ }
+}
+
+// Remove the dropdown arrow in Chrome from inputs built with datalists.
+// See https://stackoverflow.com/a/54997118
+
+[list]::-webkit-calendar-picker-indicator {
+ display: none;
+}
+
+// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+// controls in Android 4.
+// 2. Correct the inability to style clickable types in iOS and Safari.
+// 3. Opinionated: add "hand" cursor to non-disabled button elements.
+
+button,
+[type="button"], // 1
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button; // 2
+
+ @if $enable-button-pointers {
+ &:not(:disabled) {
+ cursor: pointer; // 3
+ }
+ }
+}
+
+// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
+
+::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+}
+
+// 1. Textareas should really only resize vertically so they don't break their (horizontal) containers.
+
+textarea {
+ resize: vertical; // 1
+}
+
+// 1. Browsers set a default `min-width: min-content;` on fieldsets,
+// unlike e.g. ``s, which have `min-width: 0;` by default.
+// So we reset that to ensure fieldsets behave more like a standard block element.
+// See https://github.com/twbs/bootstrap/issues/12359
+// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
+// 2. Reset the default outline behavior of fieldsets so they don't affect page layout.
+
+fieldset {
+ min-width: 0; // 1
+ padding: 0; // 2
+ margin: 0; // 2
+ border: 0; // 2
+}
+
+// 1. By using `float: left`, the legend will behave like a block element.
+// This way the border of a fieldset wraps around the legend if present.
+// 2. Fix wrapping bug.
+// See https://github.com/twbs/bootstrap/issues/29712
+
+legend {
+ float: left; // 1
+ width: 100%;
+ padding: 0;
+ margin-bottom: $legend-margin-bottom;
+ @include font-size($legend-font-size);
+ font-weight: $legend-font-weight;
+ line-height: inherit;
+
+ + * {
+ clear: left; // 2
+ }
+}
+
+// Fix height of inputs with a type of datetime-local, date, month, week, or time
+// See https://github.com/twbs/bootstrap/issues/18842
+
+::-webkit-datetime-edit-fields-wrapper,
+::-webkit-datetime-edit-text,
+::-webkit-datetime-edit-minute,
+::-webkit-datetime-edit-hour-field,
+::-webkit-datetime-edit-day-field,
+::-webkit-datetime-edit-month-field,
+::-webkit-datetime-edit-year-field {
+ padding: 0;
+}
+
+::-webkit-inner-spin-button {
+ height: auto;
+}
+
+// 1. Correct the outline style in Safari.
+// 2. This overrides the extra rounded corners on search inputs in iOS so that our
+// `.form-control` class can properly style them. Note that this cannot simply
+// be added to `.form-control` as it's not specific enough. For details, see
+// https://github.com/twbs/bootstrap/issues/11586.
+
+[type="search"] {
+ outline-offset: -2px; // 1
+ -webkit-appearance: textfield; // 2
+}
+
+// 1. A few input types should stay LTR
+// See https://rtlstyling.com/posts/rtl-styling#form-inputs
+// 2. RTL only output
+// See https://rtlcss.com/learn/usage-guide/control-directives/#raw
+
+/* rtl:raw:
+[type="tel"],
+[type="url"],
+[type="email"],
+[type="number"] {
+ direction: ltr;
+}
+*/
+
+// Remove the inner padding in Chrome and Safari on macOS.
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+// Remove padding around color pickers in webkit browsers
+
+::-webkit-color-swatch-wrapper {
+ padding: 0;
+}
+
+
+// Inherit font family and line height for file input buttons
+
+::file-selector-button {
+ font: inherit;
+}
+
+// 1. Change font properties to `inherit`
+// 2. Correct the inability to style clickable types in iOS and Safari.
+
+::-webkit-file-upload-button {
+ font: inherit; // 1
+ -webkit-appearance: button; // 2
+}
+
+// Correct element displays
+
+output {
+ display: inline-block;
+}
+
+// Remove border from iframe
+
+iframe {
+ border: 0;
+}
+
+// Summary
+//
+// 1. Add the correct display in all browsers
+
+summary {
+ display: list-item; // 1
+ cursor: pointer;
+}
+
+
+// Progress
+//
+// Add the correct vertical alignment in Chrome, Firefox, and Opera.
+
+progress {
+ vertical-align: baseline;
+}
+
+
+// Hidden attribute
+//
+// Always hide an element with the `hidden` HTML attribute.
+
+[hidden] {
+ display: none !important;
+}
diff --git a/node_modules/bootstrap/scss/_root.scss b/node_modules/bootstrap/scss/_root.scss
new file mode 100644
index 00000000..768d6343
--- /dev/null
+++ b/node_modules/bootstrap/scss/_root.scss
@@ -0,0 +1,16 @@
+:root {
+ // Custom variable values only support SassScript inside `#{}`.
+ @each $color, $value in $colors {
+ --#{$variable-prefix}#{$color}: #{$value};
+ }
+
+ @each $color, $value in $theme-colors {
+ --#{$variable-prefix}#{$color}: #{$value};
+ }
+
+ // Use `inspect` for lists so that quoted items keep the quotes.
+ // See https://github.com/sass/sass/issues/2383#issuecomment-336349172
+ --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};
+ --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};
+ --#{$variable-prefix}gradient: #{$gradient};
+}
diff --git a/node_modules/bootstrap/scss/_spinners.scss b/node_modules/bootstrap/scss/_spinners.scss
new file mode 100644
index 00000000..a4a2c77c
--- /dev/null
+++ b/node_modules/bootstrap/scss/_spinners.scss
@@ -0,0 +1,69 @@
+//
+// Rotating border
+//
+
+// scss-docs-start spinner-border-keyframes
+@keyframes spinner-border {
+ to { transform: rotate(360deg) #{"/* rtl:ignore */"}; }
+}
+// scss-docs-end spinner-border-keyframes
+
+.spinner-border {
+ display: inline-block;
+ width: $spinner-width;
+ height: $spinner-height;
+ vertical-align: $spinner-vertical-align;
+ border: $spinner-border-width solid currentColor;
+ border-right-color: transparent;
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 50%;
+ animation: $spinner-animation-speed linear infinite spinner-border;
+}
+
+.spinner-border-sm {
+ width: $spinner-width-sm;
+ height: $spinner-height-sm;
+ border-width: $spinner-border-width-sm;
+}
+
+//
+// Growing circle
+//
+
+// scss-docs-start spinner-grow-keyframes
+@keyframes spinner-grow {
+ 0% {
+ transform: scale(0);
+ }
+ 50% {
+ opacity: 1;
+ transform: none;
+ }
+}
+// scss-docs-end spinner-grow-keyframes
+
+.spinner-grow {
+ display: inline-block;
+ width: $spinner-width;
+ height: $spinner-height;
+ vertical-align: $spinner-vertical-align;
+ background-color: currentColor;
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 50%;
+ opacity: 0;
+ animation: $spinner-animation-speed linear infinite spinner-grow;
+}
+
+.spinner-grow-sm {
+ width: $spinner-width-sm;
+ height: $spinner-height-sm;
+}
+
+@if $enable-reduced-motion {
+ @media (prefers-reduced-motion: reduce) {
+ .spinner-border,
+ .spinner-grow {
+ animation-duration: $spinner-animation-speed * 2;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/_tables.scss b/node_modules/bootstrap/scss/_tables.scss
new file mode 100644
index 00000000..19f679d2
--- /dev/null
+++ b/node_modules/bootstrap/scss/_tables.scss
@@ -0,0 +1,151 @@
+//
+// Basic Bootstrap table
+//
+
+.table {
+ --#{$variable-prefix}table-bg: #{$table-bg};
+ --#{$variable-prefix}table-accent-bg: #{$table-bg};
+ --#{$variable-prefix}table-striped-color: #{$table-striped-color};
+ --#{$variable-prefix}table-striped-bg: #{$table-striped-bg};
+ --#{$variable-prefix}table-active-color: #{$table-active-color};
+ --#{$variable-prefix}table-active-bg: #{$table-active-bg};
+ --#{$variable-prefix}table-hover-color: #{$table-hover-color};
+ --#{$variable-prefix}table-hover-bg: #{$table-hover-bg};
+
+ width: 100%;
+ margin-bottom: $spacer;
+ color: $table-color;
+ vertical-align: $table-cell-vertical-align;
+ border-color: $table-border-color;
+
+ // Target th & td
+ // We need the child combinator to prevent styles leaking to nested tables which doesn't have a `.table` class.
+ // We use the universal selectors here to simplify the selector (else we would need 6 different selectors).
+ // Another advantage is that this generates less code and makes the selector less specific making it easier to override.
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ padding: $table-cell-padding-y $table-cell-padding-x;
+ background-color: var(--#{$variable-prefix}table-bg);
+ border-bottom-width: $table-border-width;
+ box-shadow: inset 0 0 0 9999px var(--#{$variable-prefix}table-accent-bg);
+ }
+
+ > tbody {
+ vertical-align: inherit;
+ }
+
+ > thead {
+ vertical-align: bottom;
+ }
+
+ // Highlight border color between thead, tbody and tfoot.
+ > :not(:last-child) > :last-child > * {
+ border-bottom-color: $table-group-separator-color;
+ }
+}
+
+
+//
+// Change placement of captions with a class
+//
+
+.caption-top {
+ caption-side: top;
+}
+
+
+//
+// Condensed table w/ half padding
+//
+
+.table-sm {
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ padding: $table-cell-padding-y-sm $table-cell-padding-x-sm;
+ }
+}
+
+
+// Border versions
+//
+// Add or remove borders all around the table and between all the columns.
+//
+// When borders are added on all sides of the cells, the corners can render odd when
+// these borders do not have the same color or if they are semi-transparent.
+// Therefor we add top and border bottoms to the `tr`s and left and right borders
+// to the `td`s or `th`s
+
+.table-bordered {
+ > :not(caption) > * {
+ border-width: $table-border-width 0;
+
+ // stylelint-disable-next-line selector-max-universal
+ > * {
+ border-width: 0 $table-border-width;
+ }
+ }
+}
+
+.table-borderless {
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ border-bottom-width: 0;
+ }
+}
+
+// Zebra-striping
+//
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+
+.table-striped {
+ > tbody > tr:nth-of-type(#{$table-striped-order}) {
+ --#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-striped-bg);
+ color: var(--#{$variable-prefix}table-striped-color);
+ }
+}
+
+// Active table
+//
+// The `.table-active` class can be added to highlight rows or cells
+
+.table-active {
+ --#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-active-bg);
+ color: var(--#{$variable-prefix}table-active-color);
+}
+
+// Hover effect
+//
+// Placed here since it has to come after the potential zebra striping
+
+.table-hover {
+ > tbody > tr:hover {
+ --#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-hover-bg);
+ color: var(--#{$variable-prefix}table-hover-color);
+ }
+}
+
+
+// Table variants
+//
+// Table variants set the table cell backgrounds, border colors
+// and the colors of the striped, hovered & active tables
+
+@each $color, $value in $table-variants {
+ @include table-variant($color, $value);
+}
+
+// Responsive tables
+//
+// Generate series of `.table-responsive-*` classes for configuring the screen
+// size of where your table will overflow.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ @include media-breakpoint-down($breakpoint) {
+ .table-responsive#{$infix} {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/_toasts.scss b/node_modules/bootstrap/scss/_toasts.scss
new file mode 100644
index 00000000..5c533d7f
--- /dev/null
+++ b/node_modules/bootstrap/scss/_toasts.scss
@@ -0,0 +1,51 @@
+.toast {
+ width: $toast-max-width;
+ max-width: 100%;
+ @include font-size($toast-font-size);
+ color: $toast-color;
+ pointer-events: auto;
+ background-color: $toast-background-color;
+ background-clip: padding-box;
+ border: $toast-border-width solid $toast-border-color;
+ box-shadow: $toast-box-shadow;
+ @include border-radius($toast-border-radius);
+
+ &:not(.showing):not(.show) {
+ opacity: 0;
+ }
+
+ &.hide {
+ display: none;
+ }
+}
+
+.toast-container {
+ width: max-content;
+ max-width: 100%;
+ pointer-events: none;
+
+ > :not(:last-child) {
+ margin-bottom: $toast-spacing;
+ }
+}
+
+.toast-header {
+ display: flex;
+ align-items: center;
+ padding: $toast-padding-y $toast-padding-x;
+ color: $toast-header-color;
+ background-color: $toast-header-background-color;
+ background-clip: padding-box;
+ border-bottom: $toast-border-width solid $toast-header-border-color;
+ @include border-top-radius(subtract($toast-border-radius, $toast-border-width));
+
+ .btn-close {
+ margin-right: $toast-padding-x / -2;
+ margin-left: $toast-padding-x;
+ }
+}
+
+.toast-body {
+ padding: $toast-padding-x; // apply to both vertical and horizontal
+ word-wrap: break-word;
+}
diff --git a/node_modules/bootstrap/scss/_tooltip.scss b/node_modules/bootstrap/scss/_tooltip.scss
new file mode 100644
index 00000000..2993bf7d
--- /dev/null
+++ b/node_modules/bootstrap/scss/_tooltip.scss
@@ -0,0 +1,115 @@
+// Base class
+.tooltip {
+ position: absolute;
+ z-index: $zindex-tooltip;
+ display: block;
+ margin: $tooltip-margin;
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ @include reset-text();
+ @include font-size($tooltip-font-size);
+ // Allow breaking very long words so they don't overflow the tooltip's bounds
+ word-wrap: break-word;
+ opacity: 0;
+
+ &.show { opacity: $tooltip-opacity; }
+
+ .tooltip-arrow {
+ position: absolute;
+ display: block;
+ width: $tooltip-arrow-width;
+ height: $tooltip-arrow-height;
+
+ &::before {
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ }
+ }
+}
+
+.bs-tooltip-top {
+ padding: $tooltip-arrow-height 0;
+
+ .tooltip-arrow {
+ bottom: 0;
+
+ &::before {
+ top: -1px;
+ border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+ border-top-color: $tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-end {
+ padding: 0 $tooltip-arrow-height;
+
+ .tooltip-arrow {
+ left: 0;
+ width: $tooltip-arrow-height;
+ height: $tooltip-arrow-width;
+
+ &::before {
+ right: -1px;
+ border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
+ border-right-color: $tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-bottom {
+ padding: $tooltip-arrow-height 0;
+
+ .tooltip-arrow {
+ top: 0;
+
+ &::before {
+ bottom: -1px;
+ border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+ border-bottom-color: $tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-start {
+ padding: 0 $tooltip-arrow-height;
+
+ .tooltip-arrow {
+ right: 0;
+ width: $tooltip-arrow-height;
+ height: $tooltip-arrow-width;
+
+ &::before {
+ left: -1px;
+ border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
+ border-left-color: $tooltip-arrow-color;
+ }
+ }
+}
+
+.bs-tooltip-auto {
+ &[data-popper-placement^="top"] {
+ @extend .bs-tooltip-top;
+ }
+ &[data-popper-placement^="right"] {
+ @extend .bs-tooltip-end;
+ }
+ &[data-popper-placement^="bottom"] {
+ @extend .bs-tooltip-bottom;
+ }
+ &[data-popper-placement^="left"] {
+ @extend .bs-tooltip-start;
+ }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+ max-width: $tooltip-max-width;
+ padding: $tooltip-padding-y $tooltip-padding-x;
+ color: $tooltip-color;
+ text-align: center;
+ background-color: $tooltip-bg;
+ @include border-radius($tooltip-border-radius);
+}
diff --git a/node_modules/bootstrap/scss/_transitions.scss b/node_modules/bootstrap/scss/_transitions.scss
new file mode 100644
index 00000000..2905df45
--- /dev/null
+++ b/node_modules/bootstrap/scss/_transitions.scss
@@ -0,0 +1,21 @@
+.fade {
+ @include transition($transition-fade);
+
+ &:not(.show) {
+ opacity: 0;
+ }
+}
+
+// scss-docs-start collapse-classes
+.collapse {
+ &:not(.show) {
+ display: none;
+ }
+}
+
+.collapsing {
+ height: 0;
+ overflow: hidden;
+ @include transition($transition-collapse);
+}
+// scss-docs-end collapse-classes
diff --git a/node_modules/bootstrap/scss/_type.scss b/node_modules/bootstrap/scss/_type.scss
new file mode 100644
index 00000000..b2d524ca
--- /dev/null
+++ b/node_modules/bootstrap/scss/_type.scss
@@ -0,0 +1,104 @@
+//
+// Headings
+//
+.h1 {
+ @extend h1;
+}
+
+.h2 {
+ @extend h2;
+}
+
+.h3 {
+ @extend h3;
+}
+
+.h4 {
+ @extend h4;
+}
+
+.h5 {
+ @extend h5;
+}
+
+.h6 {
+ @extend h6;
+}
+
+
+.lead {
+ @include font-size($lead-font-size);
+ font-weight: $lead-font-weight;
+}
+
+// Type display classes
+@each $display, $font-size in $display-font-sizes {
+ .display-#{$display} {
+ @include font-size($font-size);
+ font-weight: $display-font-weight;
+ line-height: $display-line-height;
+ }
+}
+
+//
+// Emphasis
+//
+.small {
+ @extend small;
+}
+
+.mark {
+ @extend mark;
+}
+
+//
+// Lists
+//
+
+.list-unstyled {
+ @include list-unstyled();
+}
+
+// Inline turns list items into inline-block
+.list-inline {
+ @include list-unstyled();
+}
+.list-inline-item {
+ display: inline-block;
+
+ &:not(:last-child) {
+ margin-right: $list-inline-padding;
+ }
+}
+
+
+//
+// Misc
+//
+
+// Builds on `abbr`
+.initialism {
+ @include font-size($initialism-font-size);
+ text-transform: uppercase;
+}
+
+// Blockquotes
+.blockquote {
+ margin-bottom: $blockquote-margin-y;
+ @include font-size($blockquote-font-size);
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+.blockquote-footer {
+ margin-top: -$blockquote-margin-y;
+ margin-bottom: $blockquote-margin-y;
+ @include font-size($blockquote-footer-font-size);
+ color: $blockquote-footer-color;
+
+ &::before {
+ content: "\2014\00A0"; // em dash, nbsp
+ }
+}
diff --git a/node_modules/bootstrap/scss/_utilities.scss b/node_modules/bootstrap/scss/_utilities.scss
new file mode 100644
index 00000000..3c927cf5
--- /dev/null
+++ b/node_modules/bootstrap/scss/_utilities.scss
@@ -0,0 +1,594 @@
+// stylelint-disable indentation
+
+// Utilities
+
+$utilities: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$utilities: map-merge(
+ (
+ // scss-docs-start utils-vertical-align
+ "align": (
+ property: vertical-align,
+ class: align,
+ values: baseline top middle bottom text-bottom text-top
+ ),
+ // scss-docs-end utils-vertical-align
+ // scss-docs-start utils-float
+ "float": (
+ responsive: true,
+ property: float,
+ values: (
+ start: left,
+ end: right,
+ none: none,
+ )
+ ),
+ // scss-docs-end utils-float
+ // scss-docs-start utils-overflow
+ "overflow": (
+ property: overflow,
+ values: auto hidden visible scroll,
+ ),
+ // scss-docs-end utils-overflow
+ // scss-docs-start utils-display
+ "display": (
+ responsive: true,
+ print: true,
+ property: display,
+ class: d,
+ values: inline inline-block block grid table table-row table-cell flex inline-flex none
+ ),
+ // scss-docs-end utils-display
+ // scss-docs-start utils-shadow
+ "shadow": (
+ property: box-shadow,
+ class: shadow,
+ values: (
+ null: $box-shadow,
+ sm: $box-shadow-sm,
+ lg: $box-shadow-lg,
+ none: none,
+ )
+ ),
+ // scss-docs-end utils-shadow
+ // scss-docs-start utils-position
+ "position": (
+ property: position,
+ values: static relative absolute fixed sticky
+ ),
+ "top": (
+ property: top,
+ values: $position-values
+ ),
+ "bottom": (
+ property: bottom,
+ values: $position-values
+ ),
+ "start": (
+ property: left,
+ class: start,
+ values: $position-values
+ ),
+ "end": (
+ property: right,
+ class: end,
+ values: $position-values
+ ),
+ "translate-middle": (
+ property: transform,
+ class: translate-middle,
+ values: (
+ null: translate(-50%, -50%),
+ x: translateX(-50%),
+ y: translateY(-50%),
+ )
+ ),
+ // scss-docs-end utils-position
+ // scss-docs-start utils-borders
+ "border": (
+ property: border,
+ values: (
+ null: $border-width solid $border-color,
+ 0: 0,
+ )
+ ),
+ "border-top": (
+ property: border-top,
+ values: (
+ null: $border-width solid $border-color,
+ 0: 0,
+ )
+ ),
+ "border-end": (
+ property: border-right,
+ class: border-end,
+ values: (
+ null: $border-width solid $border-color,
+ 0: 0,
+ )
+ ),
+ "border-bottom": (
+ property: border-bottom,
+ values: (
+ null: $border-width solid $border-color,
+ 0: 0,
+ )
+ ),
+ "border-start": (
+ property: border-left,
+ class: border-start,
+ values: (
+ null: $border-width solid $border-color,
+ 0: 0,
+ )
+ ),
+ "border-color": (
+ property: border-color,
+ class: border,
+ values: map-merge($theme-colors, ("white": $white))
+ ),
+ "border-width": (
+ property: border-width,
+ class: border,
+ values: $border-widths
+ ),
+ // scss-docs-end utils-borders
+ // Sizing utilities
+ // scss-docs-start utils-sizing
+ "width": (
+ property: width,
+ class: w,
+ values: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%,
+ auto: auto
+ )
+ ),
+ "max-width": (
+ property: max-width,
+ class: mw,
+ values: (100: 100%)
+ ),
+ "viewport-width": (
+ property: width,
+ class: vw,
+ values: (100: 100vw)
+ ),
+ "min-viewport-width": (
+ property: min-width,
+ class: min-vw,
+ values: (100: 100vw)
+ ),
+ "height": (
+ property: height,
+ class: h,
+ values: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%,
+ auto: auto
+ )
+ ),
+ "max-height": (
+ property: max-height,
+ class: mh,
+ values: (100: 100%)
+ ),
+ "viewport-height": (
+ property: height,
+ class: vh,
+ values: (100: 100vh)
+ ),
+ "min-viewport-height": (
+ property: min-height,
+ class: min-vh,
+ values: (100: 100vh)
+ ),
+ // scss-docs-end utils-sizing
+ // Flex utilities
+ // scss-docs-start utils-flex
+ "flex": (
+ responsive: true,
+ property: flex,
+ values: (fill: 1 1 auto)
+ ),
+ "flex-direction": (
+ responsive: true,
+ property: flex-direction,
+ class: flex,
+ values: row column row-reverse column-reverse
+ ),
+ "flex-grow": (
+ responsive: true,
+ property: flex-grow,
+ class: flex,
+ values: (
+ grow-0: 0,
+ grow-1: 1,
+ )
+ ),
+ "flex-shrink": (
+ responsive: true,
+ property: flex-shrink,
+ class: flex,
+ values: (
+ shrink-0: 0,
+ shrink-1: 1,
+ )
+ ),
+ "flex-wrap": (
+ responsive: true,
+ property: flex-wrap,
+ class: flex,
+ values: wrap nowrap wrap-reverse
+ ),
+ "gap": (
+ responsive: true,
+ property: gap,
+ class: gap,
+ values: $spacers
+ ),
+ "justify-content": (
+ responsive: true,
+ property: justify-content,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ between: space-between,
+ around: space-around,
+ evenly: space-evenly,
+ )
+ ),
+ "align-items": (
+ responsive: true,
+ property: align-items,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ baseline: baseline,
+ stretch: stretch,
+ )
+ ),
+ "align-content": (
+ responsive: true,
+ property: align-content,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ between: space-between,
+ around: space-around,
+ stretch: stretch,
+ )
+ ),
+ "align-self": (
+ responsive: true,
+ property: align-self,
+ values: (
+ auto: auto,
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ baseline: baseline,
+ stretch: stretch,
+ )
+ ),
+ "order": (
+ responsive: true,
+ property: order,
+ values: (
+ first: -1,
+ 0: 0,
+ 1: 1,
+ 2: 2,
+ 3: 3,
+ 4: 4,
+ 5: 5,
+ last: 6,
+ ),
+ ),
+ // scss-docs-end utils-flex
+ // Margin utilities
+ // scss-docs-start utils-spacing
+ "margin": (
+ responsive: true,
+ property: margin,
+ class: m,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-x": (
+ responsive: true,
+ property: margin-right margin-left,
+ class: mx,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-y": (
+ responsive: true,
+ property: margin-top margin-bottom,
+ class: my,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-top": (
+ responsive: true,
+ property: margin-top,
+ class: mt,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-end": (
+ responsive: true,
+ property: margin-right,
+ class: me,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-bottom": (
+ responsive: true,
+ property: margin-bottom,
+ class: mb,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-start": (
+ responsive: true,
+ property: margin-left,
+ class: ms,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ // Negative margin utilities
+ "negative-margin": (
+ responsive: true,
+ property: margin,
+ class: m,
+ values: $negative-spacers
+ ),
+ "negative-margin-x": (
+ responsive: true,
+ property: margin-right margin-left,
+ class: mx,
+ values: $negative-spacers
+ ),
+ "negative-margin-y": (
+ responsive: true,
+ property: margin-top margin-bottom,
+ class: my,
+ values: $negative-spacers
+ ),
+ "negative-margin-top": (
+ responsive: true,
+ property: margin-top,
+ class: mt,
+ values: $negative-spacers
+ ),
+ "negative-margin-end": (
+ responsive: true,
+ property: margin-right,
+ class: me,
+ values: $negative-spacers
+ ),
+ "negative-margin-bottom": (
+ responsive: true,
+ property: margin-bottom,
+ class: mb,
+ values: $negative-spacers
+ ),
+ "negative-margin-start": (
+ responsive: true,
+ property: margin-left,
+ class: ms,
+ values: $negative-spacers
+ ),
+ // Padding utilities
+ "padding": (
+ responsive: true,
+ property: padding,
+ class: p,
+ values: $spacers
+ ),
+ "padding-x": (
+ responsive: true,
+ property: padding-right padding-left,
+ class: px,
+ values: $spacers
+ ),
+ "padding-y": (
+ responsive: true,
+ property: padding-top padding-bottom,
+ class: py,
+ values: $spacers
+ ),
+ "padding-top": (
+ responsive: true,
+ property: padding-top,
+ class: pt,
+ values: $spacers
+ ),
+ "padding-end": (
+ responsive: true,
+ property: padding-right,
+ class: pe,
+ values: $spacers
+ ),
+ "padding-bottom": (
+ responsive: true,
+ property: padding-bottom,
+ class: pb,
+ values: $spacers
+ ),
+ "padding-start": (
+ responsive: true,
+ property: padding-left,
+ class: ps,
+ values: $spacers
+ ),
+ // scss-docs-end utils-spacing
+ // Text
+ // scss-docs-start utils-text
+ "font-family": (
+ property: font-family,
+ class: font,
+ values: (monospace: var(--#{$variable-prefix}font-monospace))
+ ),
+ "font-size": (
+ rfs: true,
+ property: font-size,
+ class: fs,
+ values: $font-sizes
+ ),
+ "font-style": (
+ property: font-style,
+ class: fst,
+ values: italic normal
+ ),
+ "font-weight": (
+ property: font-weight,
+ class: fw,
+ values: (
+ light: $font-weight-light,
+ lighter: $font-weight-lighter,
+ normal: $font-weight-normal,
+ bold: $font-weight-bold,
+ bolder: $font-weight-bolder
+ )
+ ),
+ "line-height": (
+ property: line-height,
+ class: lh,
+ values: (
+ 1: 1,
+ sm: $line-height-sm,
+ base: $line-height-base,
+ lg: $line-height-lg,
+ )
+ ),
+ "text-align": (
+ responsive: true,
+ property: text-align,
+ class: text,
+ values: (
+ start: left,
+ end: right,
+ center: center,
+ )
+ ),
+ "text-decoration": (
+ property: text-decoration,
+ values: none underline line-through
+ ),
+ "text-transform": (
+ property: text-transform,
+ class: text,
+ values: lowercase uppercase capitalize
+ ),
+ "white-space": (
+ property: white-space,
+ class: text,
+ values: (
+ wrap: normal,
+ nowrap: nowrap,
+ )
+ ),
+ "word-wrap": (
+ property: word-wrap word-break,
+ class: text,
+ values: (break: break-word),
+ rtl: false
+ ),
+ // scss-docs-end utils-text
+ // scss-docs-start utils-color
+ "color": (
+ property: color,
+ class: text,
+ values: map-merge(
+ $theme-colors,
+ (
+ "white": $white,
+ "body": $body-color,
+ "muted": $text-muted,
+ "black-50": rgba($black, .5),
+ "white-50": rgba($white, .5),
+ "reset": inherit,
+ )
+ )
+ ),
+ // scss-docs-end utils-color
+ // scss-docs-start utils-bg-color
+ "background-color": (
+ property: background-color,
+ class: bg,
+ values: map-merge(
+ $theme-colors,
+ (
+ "body": $body-bg,
+ "white": $white,
+ "transparent": transparent
+ )
+ )
+ ),
+ // scss-docs-end utils-bg-color
+ "gradient": (
+ property: background-image,
+ class: bg,
+ values: (gradient: var(--#{$variable-prefix}gradient))
+ ),
+ // scss-docs-start utils-interaction
+ "user-select": (
+ property: user-select,
+ values: all auto none
+ ),
+ "pointer-events": (
+ property: pointer-events,
+ class: pe,
+ values: none auto,
+ ),
+ // scss-docs-end utils-interaction
+ // scss-docs-start utils-border-radius
+ "rounded": (
+ property: border-radius,
+ class: rounded,
+ values: (
+ null: $border-radius,
+ 0: 0,
+ 1: $border-radius-sm,
+ 2: $border-radius,
+ 3: $border-radius-lg,
+ circle: 50%,
+ pill: $border-radius-pill
+ )
+ ),
+ "rounded-top": (
+ property: border-top-left-radius border-top-right-radius,
+ class: rounded-top,
+ values: (null: $border-radius)
+ ),
+ "rounded-end": (
+ property: border-top-right-radius border-bottom-right-radius,
+ class: rounded-end,
+ values: (null: $border-radius)
+ ),
+ "rounded-bottom": (
+ property: border-bottom-right-radius border-bottom-left-radius,
+ class: rounded-bottom,
+ values: (null: $border-radius)
+ ),
+ "rounded-start": (
+ property: border-bottom-left-radius border-top-left-radius,
+ class: rounded-start,
+ values: (null: $border-radius)
+ ),
+ // scss-docs-end utils-border-radius
+ // scss-docs-start utils-visibility
+ "visibility": (
+ property: visibility,
+ class: null,
+ values: (
+ visible: visible,
+ invisible: hidden,
+ )
+ )
+ // scss-docs-end utils-visibility
+ ),
+ $utilities
+);
diff --git a/node_modules/bootstrap/scss/_variables.scss b/node_modules/bootstrap/scss/_variables.scss
new file mode 100644
index 00000000..1e17606f
--- /dev/null
+++ b/node_modules/bootstrap/scss/_variables.scss
@@ -0,0 +1,1464 @@
+// Variables
+//
+// Variables should follow the `$component-state-property-size` formula for
+// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
+
+// Color system
+
+// scss-docs-start gray-color-variables
+$white: #fff !default;
+$gray-100: #f8f9fa !default;
+$gray-200: #e9ecef !default;
+$gray-300: #dee2e6 !default;
+$gray-400: #ced4da !default;
+$gray-500: #adb5bd !default;
+$gray-600: #6c757d !default;
+$gray-700: #495057 !default;
+$gray-800: #343a40 !default;
+$gray-900: #212529 !default;
+$black: #000 !default;
+// scss-docs-end gray-color-variables
+
+// fusv-disable
+// scss-docs-start gray-colors-map
+$grays: (
+ "100": $gray-100,
+ "200": $gray-200,
+ "300": $gray-300,
+ "400": $gray-400,
+ "500": $gray-500,
+ "600": $gray-600,
+ "700": $gray-700,
+ "800": $gray-800,
+ "900": $gray-900
+) !default;
+// scss-docs-end gray-colors-map
+// fusv-enable
+
+// scss-docs-start color-variables
+$blue: #0d6efd !default;
+$indigo: #6610f2 !default;
+$purple: #6f42c1 !default;
+$pink: #d63384 !default;
+$red: #dc3545 !default;
+$orange: #fd7e14 !default;
+$yellow: #ffc107 !default;
+$green: #198754 !default;
+$teal: #20c997 !default;
+$cyan: #0dcaf0 !default;
+// scss-docs-end color-variables
+
+// scss-docs-start colors-map
+$colors: (
+ "blue": $blue,
+ "indigo": $indigo,
+ "purple": $purple,
+ "pink": $pink,
+ "red": $red,
+ "orange": $orange,
+ "yellow": $yellow,
+ "green": $green,
+ "teal": $teal,
+ "cyan": $cyan,
+ "white": $white,
+ "gray": $gray-600,
+ "gray-dark": $gray-800
+) !default;
+// scss-docs-end colors-map
+
+// scss-docs-start theme-color-variables
+$primary: $blue !default;
+$secondary: $gray-600 !default;
+$success: $green !default;
+$info: $cyan !default;
+$warning: $yellow !default;
+$danger: $red !default;
+$light: $gray-100 !default;
+$dark: $gray-900 !default;
+// scss-docs-end theme-color-variables
+
+// scss-docs-start theme-colors-map
+$theme-colors: (
+ "primary": $primary,
+ "secondary": $secondary,
+ "success": $success,
+ "info": $info,
+ "warning": $warning,
+ "danger": $danger,
+ "light": $light,
+ "dark": $dark
+) !default;
+// scss-docs-end theme-colors-map
+
+// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.
+// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
+$min-contrast-ratio: 4.5 !default;
+
+// Customize the light and dark text colors for use in our color contrast function.
+$color-contrast-dark: $black !default;
+$color-contrast-light: $white !default;
+
+// fusv-disable
+$blue-100: tint-color($blue, 80%) !default;
+$blue-200: tint-color($blue, 60%) !default;
+$blue-300: tint-color($blue, 40%) !default;
+$blue-400: tint-color($blue, 20%) !default;
+$blue-500: $blue !default;
+$blue-600: shade-color($blue, 20%) !default;
+$blue-700: shade-color($blue, 40%) !default;
+$blue-800: shade-color($blue, 60%) !default;
+$blue-900: shade-color($blue, 80%) !default;
+
+$indigo-100: tint-color($indigo, 80%) !default;
+$indigo-200: tint-color($indigo, 60%) !default;
+$indigo-300: tint-color($indigo, 40%) !default;
+$indigo-400: tint-color($indigo, 20%) !default;
+$indigo-500: $indigo !default;
+$indigo-600: shade-color($indigo, 20%) !default;
+$indigo-700: shade-color($indigo, 40%) !default;
+$indigo-800: shade-color($indigo, 60%) !default;
+$indigo-900: shade-color($indigo, 80%) !default;
+
+$purple-100: tint-color($purple, 80%) !default;
+$purple-200: tint-color($purple, 60%) !default;
+$purple-300: tint-color($purple, 40%) !default;
+$purple-400: tint-color($purple, 20%) !default;
+$purple-500: $purple !default;
+$purple-600: shade-color($purple, 20%) !default;
+$purple-700: shade-color($purple, 40%) !default;
+$purple-800: shade-color($purple, 60%) !default;
+$purple-900: shade-color($purple, 80%) !default;
+
+$pink-100: tint-color($pink, 80%) !default;
+$pink-200: tint-color($pink, 60%) !default;
+$pink-300: tint-color($pink, 40%) !default;
+$pink-400: tint-color($pink, 20%) !default;
+$pink-500: $pink !default;
+$pink-600: shade-color($pink, 20%) !default;
+$pink-700: shade-color($pink, 40%) !default;
+$pink-800: shade-color($pink, 60%) !default;
+$pink-900: shade-color($pink, 80%) !default;
+
+$red-100: tint-color($red, 80%) !default;
+$red-200: tint-color($red, 60%) !default;
+$red-300: tint-color($red, 40%) !default;
+$red-400: tint-color($red, 20%) !default;
+$red-500: $red !default;
+$red-600: shade-color($red, 20%) !default;
+$red-700: shade-color($red, 40%) !default;
+$red-800: shade-color($red, 60%) !default;
+$red-900: shade-color($red, 80%) !default;
+
+$orange-100: tint-color($orange, 80%) !default;
+$orange-200: tint-color($orange, 60%) !default;
+$orange-300: tint-color($orange, 40%) !default;
+$orange-400: tint-color($orange, 20%) !default;
+$orange-500: $orange !default;
+$orange-600: shade-color($orange, 20%) !default;
+$orange-700: shade-color($orange, 40%) !default;
+$orange-800: shade-color($orange, 60%) !default;
+$orange-900: shade-color($orange, 80%) !default;
+
+$yellow-100: tint-color($yellow, 80%) !default;
+$yellow-200: tint-color($yellow, 60%) !default;
+$yellow-300: tint-color($yellow, 40%) !default;
+$yellow-400: tint-color($yellow, 20%) !default;
+$yellow-500: $yellow !default;
+$yellow-600: shade-color($yellow, 20%) !default;
+$yellow-700: shade-color($yellow, 40%) !default;
+$yellow-800: shade-color($yellow, 60%) !default;
+$yellow-900: shade-color($yellow, 80%) !default;
+
+$green-100: tint-color($green, 80%) !default;
+$green-200: tint-color($green, 60%) !default;
+$green-300: tint-color($green, 40%) !default;
+$green-400: tint-color($green, 20%) !default;
+$green-500: $green !default;
+$green-600: shade-color($green, 20%) !default;
+$green-700: shade-color($green, 40%) !default;
+$green-800: shade-color($green, 60%) !default;
+$green-900: shade-color($green, 80%) !default;
+
+$teal-100: tint-color($teal, 80%) !default;
+$teal-200: tint-color($teal, 60%) !default;
+$teal-300: tint-color($teal, 40%) !default;
+$teal-400: tint-color($teal, 20%) !default;
+$teal-500: $teal !default;
+$teal-600: shade-color($teal, 20%) !default;
+$teal-700: shade-color($teal, 40%) !default;
+$teal-800: shade-color($teal, 60%) !default;
+$teal-900: shade-color($teal, 80%) !default;
+
+$cyan-100: tint-color($cyan, 80%) !default;
+$cyan-200: tint-color($cyan, 60%) !default;
+$cyan-300: tint-color($cyan, 40%) !default;
+$cyan-400: tint-color($cyan, 20%) !default;
+$cyan-500: $cyan !default;
+$cyan-600: shade-color($cyan, 20%) !default;
+$cyan-700: shade-color($cyan, 40%) !default;
+$cyan-800: shade-color($cyan, 60%) !default;
+$cyan-900: shade-color($cyan, 80%) !default;
+// fusv-enable
+
+// Characters which are escaped by the escape-svg function
+$escaped-characters: (
+ ("<", "%3c"),
+ (">", "%3e"),
+ ("#", "%23"),
+ ("(", "%28"),
+ (")", "%29"),
+) !default;
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-caret: true !default;
+$enable-rounded: true !default;
+$enable-shadows: false !default;
+$enable-gradients: false !default;
+$enable-transitions: true !default;
+$enable-reduced-motion: true !default;
+$enable-smooth-scroll: true !default;
+$enable-grid-classes: true !default;
+$enable-button-pointers: true !default;
+$enable-rfs: true !default;
+$enable-validation-icons: true !default;
+$enable-negative-margins: false !default;
+$enable-deprecation-messages: true !default;
+$enable-important-utilities: true !default;
+
+// Prefix for :root CSS variables
+
+$variable-prefix: bs- !default;
+
+// Gradient
+//
+// The gradient which is added to components if `$enable-gradients` is `true`
+// This gradient is also added to elements with `.bg-gradient`
+// scss-docs-start variable-gradient
+$gradient: linear-gradient(180deg, rgba($white, .15), rgba($white, 0)) !default;
+// scss-docs-end variable-gradient
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the $spacers map, should you need more variation.
+
+// scss-docs-start spacer-variables-maps
+$spacer: 1rem !default;
+$spacers: (
+ 0: 0,
+ 1: $spacer / 4,
+ 2: $spacer / 2,
+ 3: $spacer,
+ 4: $spacer * 1.5,
+ 5: $spacer * 3,
+) !default;
+
+$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
+// scss-docs-end spacer-variables-maps
+
+// Position
+//
+// Define the edge positioning anchors of the position utilities.
+
+// scss-docs-start position-map
+$position-values: (
+ 0: 0,
+ 50: 50%,
+ 100: 100%
+) !default;
+// scss-docs-end position-map
+
+// Body
+//
+// Settings for the `` element.
+
+$body-bg: $white !default;
+$body-color: $gray-900 !default;
+$body-text-align: null !default;
+
+
+// Links
+//
+// Style anchor elements.
+
+$link-color: $primary !default;
+$link-decoration: underline !default;
+$link-shade-percentage: 20% !default;
+$link-hover-color: shift-color($link-color, $link-shade-percentage) !default;
+$link-hover-decoration: null !default;
+
+$stretched-link-pseudo-element: after !default;
+$stretched-link-z-index: 1 !default;
+
+// Paragraphs
+//
+// Style p element.
+
+$paragraph-margin-bottom: 1rem !default;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+// scss-docs-start grid-breakpoints
+$grid-breakpoints: (
+ xs: 0,
+ sm: 576px,
+ md: 768px,
+ lg: 992px,
+ xl: 1200px,
+ xxl: 1400px
+) !default;
+// scss-docs-end grid-breakpoints
+
+@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+@include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints");
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+// scss-docs-start container-max-widths
+$container-max-widths: (
+ sm: 540px,
+ md: 720px,
+ lg: 960px,
+ xl: 1140px,
+ xxl: 1320px
+) !default;
+// scss-docs-end container-max-widths
+
+@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+$grid-columns: 12 !default;
+$grid-gutter-width: 1.5rem !default;
+$grid-row-columns: 6 !default;
+
+$gutters: $spacers !default;
+
+// Container padding
+
+$container-padding-x: $grid-gutter-width / 2 !default;
+
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+// scss-docs-start border-variables
+$border-width: 1px !default;
+$border-widths: (
+ 1: 1px,
+ 2: 2px,
+ 3: 3px,
+ 4: 4px,
+ 5: 5px
+) !default;
+
+$border-color: $gray-300 !default;
+// scss-docs-end border-variables
+
+// scss-docs-start border-radius-variables
+$border-radius: .25rem !default;
+$border-radius-sm: .2rem !default;
+$border-radius-lg: .3rem !default;
+$border-radius-pill: 50rem !default;
+// scss-docs-end border-radius-variables
+
+// scss-docs-start box-shadow-variables
+$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;
+$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;
+$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;
+$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default;
+// scss-docs-end box-shadow-variables
+
+$component-active-color: $white !default;
+$component-active-bg: $primary !default;
+
+// scss-docs-start caret-variables
+$caret-width: .3em !default;
+$caret-vertical-align: $caret-width * .85 !default;
+$caret-spacing: $caret-width * .85 !default;
+// scss-docs-end caret-variables
+
+$transition-base: all .2s ease-in-out !default;
+$transition-fade: opacity .15s linear !default;
+// scss-docs-start collapse-transition
+$transition-collapse: height .35s ease !default;
+// scss-docs-end collapse-transition
+
+// stylelint-disable function-disallowed-list
+// scss-docs-start aspect-ratios
+$aspect-ratios: (
+ "1x1": 100%,
+ "4x3": calc(3 / 4 * 100%),
+ "16x9": calc(9 / 16 * 100%),
+ "21x9": calc(9 / 21 * 100%)
+) !default;
+// scss-docs-end aspect-ratios
+// stylelint-enable function-disallowed-list
+
+// Typography
+//
+// Font, line-height, and color for body text, headings, and more.
+
+// scss-docs-start font-variables
+// stylelint-disable value-keyword-case
+$font-family-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
+$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
+// stylelint-enable value-keyword-case
+$font-family-base: var(--#{$variable-prefix}font-sans-serif) !default;
+$font-family-code: var(--#{$variable-prefix}font-monospace) !default;
+
+// $font-size-root effects the value of `rem`, which is used for as well font sizes, paddings and margins
+// $font-size-base effects the font size of the body text
+$font-size-root: null !default;
+$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`
+$font-size-sm: $font-size-base * .875 !default;
+$font-size-lg: $font-size-base * 1.25 !default;
+
+$font-weight-lighter: lighter !default;
+$font-weight-light: 300 !default;
+$font-weight-normal: 400 !default;
+$font-weight-bold: 700 !default;
+$font-weight-bolder: bolder !default;
+
+$font-weight-base: $font-weight-normal !default;
+
+$line-height-base: 1.5 !default;
+$line-height-sm: 1.25 !default;
+$line-height-lg: 2 !default;
+
+$h1-font-size: $font-size-base * 2.5 !default;
+$h2-font-size: $font-size-base * 2 !default;
+$h3-font-size: $font-size-base * 1.75 !default;
+$h4-font-size: $font-size-base * 1.5 !default;
+$h5-font-size: $font-size-base * 1.25 !default;
+$h6-font-size: $font-size-base !default;
+// scss-docs-end font-variables
+
+// scss-docs-start font-sizes
+$font-sizes: (
+ 1: $h1-font-size,
+ 2: $h2-font-size,
+ 3: $h3-font-size,
+ 4: $h4-font-size,
+ 5: $h5-font-size,
+ 6: $h6-font-size
+) !default;
+// scss-docs-end font-sizes
+
+// scss-docs-start headings-variables
+$headings-margin-bottom: $spacer / 2 !default;
+$headings-font-family: null !default;
+$headings-font-style: null !default;
+$headings-font-weight: 500 !default;
+$headings-line-height: 1.2 !default;
+$headings-color: null !default;
+// scss-docs-end headings-variables
+
+// scss-docs-start display-headings
+$display-font-sizes: (
+ 1: 5rem,
+ 2: 4.5rem,
+ 3: 4rem,
+ 4: 3.5rem,
+ 5: 3rem,
+ 6: 2.5rem
+) !default;
+
+$display-font-weight: 300 !default;
+$display-line-height: $headings-line-height !default;
+// scss-docs-end display-headings
+
+// scss-docs-start type-variables
+$lead-font-size: $font-size-base * 1.25 !default;
+$lead-font-weight: 300 !default;
+
+$small-font-size: .875em !default;
+
+$sub-sup-font-size: .75em !default;
+
+$text-muted: $gray-600 !default;
+
+$initialism-font-size: $small-font-size !default;
+
+$blockquote-margin-y: $spacer !default;
+$blockquote-font-size: $font-size-base * 1.25 !default;
+$blockquote-footer-color: $gray-600 !default;
+$blockquote-footer-font-size: $small-font-size !default;
+
+$hr-margin-y: $spacer !default;
+$hr-color: inherit !default;
+$hr-height: $border-width !default;
+$hr-opacity: .25 !default;
+
+$legend-margin-bottom: .5rem !default;
+$legend-font-size: 1.5rem !default;
+$legend-font-weight: null !default;
+
+$mark-padding: .2em !default;
+
+$dt-font-weight: $font-weight-bold !default;
+
+$nested-kbd-font-weight: $font-weight-bold !default;
+
+$list-inline-padding: .5rem !default;
+
+$mark-bg: #fcf8e3 !default;
+// scss-docs-end type-variables
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+// scss-docs-start table-variables
+$table-cell-padding-y: .5rem !default;
+$table-cell-padding-x: .5rem !default;
+$table-cell-padding-y-sm: .25rem !default;
+$table-cell-padding-x-sm: .25rem !default;
+
+$table-cell-vertical-align: top !default;
+
+$table-color: $body-color !default;
+$table-bg: transparent !default;
+
+$table-th-font-weight: null !default;
+
+$table-striped-color: $table-color !default;
+$table-striped-bg-factor: .05 !default;
+$table-striped-bg: rgba($black, $table-striped-bg-factor) !default;
+
+$table-active-color: $table-color !default;
+$table-active-bg-factor: .1 !default;
+$table-active-bg: rgba($black, $table-active-bg-factor) !default;
+
+$table-hover-color: $table-color !default;
+$table-hover-bg-factor: .075 !default;
+$table-hover-bg: rgba($black, $table-hover-bg-factor) !default;
+
+$table-border-factor: .1 !default;
+$table-border-width: $border-width !default;
+$table-border-color: $border-color !default;
+
+$table-striped-order: odd !default;
+
+$table-group-separator-color: currentColor !default;
+
+$table-caption-color: $text-muted !default;
+
+$table-bg-scale: -80% !default;
+// scss-docs-end table-variables
+
+// scss-docs-start table-loop
+$table-variants: (
+ "primary": shift-color($primary, $table-bg-scale),
+ "secondary": shift-color($secondary, $table-bg-scale),
+ "success": shift-color($success, $table-bg-scale),
+ "info": shift-color($info, $table-bg-scale),
+ "warning": shift-color($warning, $table-bg-scale),
+ "danger": shift-color($danger, $table-bg-scale),
+ "light": $light,
+ "dark": $dark,
+) !default;
+// scss-docs-end table-loop
+
+
+// Buttons + Forms
+//
+// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
+
+// scss-docs-start input-btn-variables
+$input-btn-padding-y: .375rem !default;
+$input-btn-padding-x: .75rem !default;
+$input-btn-font-family: null !default;
+$input-btn-font-size: $font-size-base !default;
+$input-btn-line-height: $line-height-base !default;
+
+$input-btn-focus-width: .25rem !default;
+$input-btn-focus-color-opacity: .25 !default;
+$input-btn-focus-color: rgba($component-active-bg, $input-btn-focus-color-opacity) !default;
+$input-btn-focus-blur: 0 !default;
+$input-btn-focus-box-shadow: 0 0 $input-btn-focus-blur $input-btn-focus-width $input-btn-focus-color !default;
+
+$input-btn-padding-y-sm: .25rem !default;
+$input-btn-padding-x-sm: .5rem !default;
+$input-btn-font-size-sm: $font-size-sm !default;
+
+$input-btn-padding-y-lg: .5rem !default;
+$input-btn-padding-x-lg: 1rem !default;
+$input-btn-font-size-lg: $font-size-lg !default;
+
+$input-btn-border-width: $border-width !default;
+// scss-docs-end input-btn-variables
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background, and border color.
+
+// scss-docs-start btn-variables
+$btn-padding-y: $input-btn-padding-y !default;
+$btn-padding-x: $input-btn-padding-x !default;
+$btn-font-family: $input-btn-font-family !default;
+$btn-font-size: $input-btn-font-size !default;
+$btn-line-height: $input-btn-line-height !default;
+$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping
+
+$btn-padding-y-sm: $input-btn-padding-y-sm !default;
+$btn-padding-x-sm: $input-btn-padding-x-sm !default;
+$btn-font-size-sm: $input-btn-font-size-sm !default;
+
+$btn-padding-y-lg: $input-btn-padding-y-lg !default;
+$btn-padding-x-lg: $input-btn-padding-x-lg !default;
+$btn-font-size-lg: $input-btn-font-size-lg !default;
+
+$btn-border-width: $input-btn-border-width !default;
+
+$btn-font-weight: $font-weight-normal !default;
+$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;
+$btn-focus-width: $input-btn-focus-width !default;
+$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;
+$btn-disabled-opacity: .65 !default;
+$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;
+
+$btn-link-color: $link-color !default;
+$btn-link-hover-color: $link-hover-color !default;
+$btn-link-disabled-color: $gray-600 !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius: $border-radius !default;
+$btn-border-radius-sm: $border-radius-sm !default;
+$btn-border-radius-lg: $border-radius-lg !default;
+
+$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$btn-hover-bg-shade-amount: 15% !default;
+$btn-hover-bg-tint-amount: 15% !default;
+$btn-hover-border-shade-amount: 20% !default;
+$btn-hover-border-tint-amount: 10% !default;
+$btn-active-bg-shade-amount: 20% !default;
+$btn-active-bg-tint-amount: 20% !default;
+$btn-active-border-shade-amount: 25% !default;
+$btn-active-border-tint-amount: 10% !default;
+// scss-docs-end btn-variables
+
+
+// Forms
+
+// scss-docs-start form-text-variables
+$form-text-margin-top: .25rem !default;
+$form-text-font-size: $small-font-size !default;
+$form-text-font-style: null !default;
+$form-text-font-weight: null !default;
+$form-text-color: $text-muted !default;
+// scss-docs-end form-text-variables
+
+// scss-docs-start form-label-variables
+$form-label-margin-bottom: .5rem !default;
+$form-label-font-size: null !default;
+$form-label-font-style: null !default;
+$form-label-font-weight: null !default;
+$form-label-color: null !default;
+// scss-docs-end form-label-variables
+
+// scss-docs-start form-input-variables
+$input-padding-y: $input-btn-padding-y !default;
+$input-padding-x: $input-btn-padding-x !default;
+$input-font-family: $input-btn-font-family !default;
+$input-font-size: $input-btn-font-size !default;
+$input-font-weight: $font-weight-base !default;
+$input-line-height: $input-btn-line-height !default;
+
+$input-padding-y-sm: $input-btn-padding-y-sm !default;
+$input-padding-x-sm: $input-btn-padding-x-sm !default;
+$input-font-size-sm: $input-btn-font-size-sm !default;
+
+$input-padding-y-lg: $input-btn-padding-y-lg !default;
+$input-padding-x-lg: $input-btn-padding-x-lg !default;
+$input-font-size-lg: $input-btn-font-size-lg !default;
+
+$input-bg: $white !default;
+$input-disabled-bg: $gray-200 !default;
+$input-disabled-border-color: null !default;
+
+$input-color: $body-color !default;
+$input-border-color: $gray-400 !default;
+$input-border-width: $input-btn-border-width !default;
+$input-box-shadow: $box-shadow-inset !default;
+
+$input-border-radius: $border-radius !default;
+$input-border-radius-sm: $border-radius-sm !default;
+$input-border-radius-lg: $border-radius-lg !default;
+
+$input-focus-bg: $input-bg !default;
+$input-focus-border-color: tint-color($component-active-bg, 50%) !default;
+$input-focus-color: $input-color !default;
+$input-focus-width: $input-btn-focus-width !default;
+$input-focus-box-shadow: $input-btn-focus-box-shadow !default;
+
+$input-placeholder-color: $gray-600 !default;
+$input-plaintext-color: $body-color !default;
+
+$input-height-border: $input-border-width * 2 !default;
+
+$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;
+$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;
+$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;
+
+$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;
+$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;
+$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;
+
+$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+// scss-docs-end form-input-variables
+
+// scss-docs-start form-check-variables
+$form-check-input-width: 1em !default;
+$form-check-min-height: $font-size-base * $line-height-base !default;
+$form-check-padding-start: $form-check-input-width + .5em !default;
+$form-check-margin-bottom: .125rem !default;
+$form-check-label-color: null !default;
+$form-check-label-cursor: null !default;
+$form-check-transition: null !default;
+
+$form-check-input-active-filter: brightness(90%) !default;
+
+$form-check-input-bg: $input-bg !default;
+$form-check-input-border: 1px solid rgba($black, .25) !default;
+$form-check-input-border-radius: .25em !default;
+$form-check-radio-border-radius: 50% !default;
+$form-check-input-focus-border: $input-focus-border-color !default;
+$form-check-input-focus-box-shadow: $input-btn-focus-box-shadow !default;
+
+$form-check-input-checked-color: $component-active-color !default;
+$form-check-input-checked-bg-color: $component-active-bg !default;
+$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default;
+$form-check-input-checked-bg-image: url("data:image/svg+xml,
") !default;
+$form-check-radio-checked-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-check-input-indeterminate-color: $component-active-color !default;
+$form-check-input-indeterminate-bg-color: $component-active-bg !default;
+$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;
+$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-check-input-disabled-opacity: .5 !default;
+$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default;
+$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default;
+
+$form-check-inline-margin-end: 1rem !default;
+// scss-docs-end form-check-variables
+
+// scss-docs-start form-switch-variables
+$form-switch-color: rgba(0, 0, 0, .25) !default;
+$form-switch-width: 2em !default;
+$form-switch-padding-start: $form-switch-width + .5em !default;
+$form-switch-bg-image: url("data:image/svg+xml,
") !default;
+$form-switch-border-radius: $form-switch-width !default;
+$form-switch-transition: background-position .15s ease-in-out !default;
+
+$form-switch-focus-color: $input-focus-border-color !default;
+$form-switch-focus-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-switch-checked-color: $component-active-color !default;
+$form-switch-checked-bg-image: url("data:image/svg+xml,
") !default;
+$form-switch-checked-bg-position: right center !default;
+// scss-docs-end form-switch-variables
+
+// scss-docs-start input-group-variables
+$input-group-addon-padding-y: $input-padding-y !default;
+$input-group-addon-padding-x: $input-padding-x !default;
+$input-group-addon-font-weight: $input-font-weight !default;
+$input-group-addon-color: $input-color !default;
+$input-group-addon-bg: $gray-200 !default;
+$input-group-addon-border-color: $input-border-color !default;
+// scss-docs-end input-group-variables
+
+// scss-docs-start form-select-variables
+$form-select-padding-y: $input-padding-y !default;
+$form-select-padding-x: $input-padding-x !default;
+$form-select-font-family: $input-font-family !default;
+$form-select-font-size: $input-font-size !default;
+$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image
+$form-select-font-weight: $input-font-weight !default;
+$form-select-line-height: $input-line-height !default;
+$form-select-color: $input-color !default;
+$form-select-bg: $input-bg !default;
+$form-select-disabled-color: null !default;
+$form-select-disabled-bg: $gray-200 !default;
+$form-select-disabled-border-color: $input-disabled-border-color !default;
+$form-select-bg-position: right $form-select-padding-x center !default;
+$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions
+$form-select-indicator-color: $gray-800 !default;
+$form-select-indicator: url("data:image/svg+xml,
") !default;
+
+$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default;
+$form-select-feedback-icon-position: center right $form-select-indicator-padding !default;
+$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;
+
+$form-select-border-width: $input-border-width !default;
+$form-select-border-color: $input-border-color !default;
+$form-select-border-radius: $border-radius !default;
+$form-select-box-shadow: $box-shadow-inset !default;
+
+$form-select-focus-border-color: $input-focus-border-color !default;
+$form-select-focus-width: $input-focus-width !default;
+$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default;
+
+$form-select-padding-y-sm: $input-padding-y-sm !default;
+$form-select-padding-x-sm: $input-padding-x-sm !default;
+$form-select-font-size-sm: $input-font-size-sm !default;
+
+$form-select-padding-y-lg: $input-padding-y-lg !default;
+$form-select-padding-x-lg: $input-padding-x-lg !default;
+$form-select-font-size-lg: $input-font-size-lg !default;
+// scss-docs-end form-select-variables
+
+// scss-docs-start form-range-variables
+$form-range-track-width: 100% !default;
+$form-range-track-height: .5rem !default;
+$form-range-track-cursor: pointer !default;
+$form-range-track-bg: $gray-300 !default;
+$form-range-track-border-radius: 1rem !default;
+$form-range-track-box-shadow: $box-shadow-inset !default;
+
+$form-range-thumb-width: 1rem !default;
+$form-range-thumb-height: $form-range-thumb-width !default;
+$form-range-thumb-bg: $component-active-bg !default;
+$form-range-thumb-border: 0 !default;
+$form-range-thumb-border-radius: 1rem !default;
+$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;
+$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;
+$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge
+$form-range-thumb-active-bg: tint-color($component-active-bg, 70%) !default;
+$form-range-thumb-disabled-bg: $gray-500 !default;
+$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+// scss-docs-end form-range-variables
+
+// scss-docs-start form-file-variables
+$form-file-button-color: $input-color !default;
+$form-file-button-bg: $input-group-addon-bg !default;
+$form-file-button-hover-bg: shade-color($form-file-button-bg, 5%) !default;
+// scss-docs-end form-file-variables
+
+// scss-docs-start form-floating-variables
+$form-floating-height: add(3.5rem, $input-height-border) !default;
+$form-floating-padding-x: $input-padding-x !default;
+$form-floating-padding-y: 1rem !default;
+$form-floating-input-padding-t: 1.625rem !default;
+$form-floating-input-padding-b: .625rem !default;
+$form-floating-label-opacity: .65 !default;
+$form-floating-label-transform: scale(.85) translateY(-.5rem) translateX(.15rem) !default;
+$form-floating-transition: opacity .1s ease-in-out, transform .1s ease-in-out !default;
+// scss-docs-end form-floating-variables
+
+// Form validation
+
+// scss-docs-start form-feedback-variables
+$form-feedback-margin-top: $form-text-margin-top !default;
+$form-feedback-font-size: $form-text-font-size !default;
+$form-feedback-font-style: $form-text-font-style !default;
+$form-feedback-valid-color: $success !default;
+$form-feedback-invalid-color: $danger !default;
+
+$form-feedback-icon-valid-color: $form-feedback-valid-color !default;
+$form-feedback-icon-valid: url("data:image/svg+xml,
") !default;
+$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;
+$form-feedback-icon-invalid: url("data:image/svg+xml,
") !default;
+// scss-docs-end form-feedback-variables
+
+// scss-docs-start form-validation-states
+$form-validation-states: (
+ "valid": (
+ "color": $form-feedback-valid-color,
+ "icon": $form-feedback-icon-valid
+ ),
+ "invalid": (
+ "color": $form-feedback-invalid-color,
+ "icon": $form-feedback-icon-invalid
+ )
+) !default;
+// scss-docs-end form-validation-states
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+// scss-docs-start zindex-stack
+$zindex-dropdown: 1000 !default;
+$zindex-sticky: 1020 !default;
+$zindex-fixed: 1030 !default;
+$zindex-modal-backdrop: 1040 !default;
+$zindex-offcanvas: 1050 !default;
+$zindex-modal: 1060 !default;
+$zindex-popover: 1070 !default;
+$zindex-tooltip: 1080 !default;
+// scss-docs-end zindex-stack
+
+
+// Navs
+
+// scss-docs-start nav-variables
+$nav-link-padding-y: .5rem !default;
+$nav-link-padding-x: 1rem !default;
+$nav-link-font-size: null !default;
+$nav-link-font-weight: null !default;
+$nav-link-color: $link-color !default;
+$nav-link-hover-color: $link-hover-color !default;
+$nav-link-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;
+$nav-link-disabled-color: $gray-600 !default;
+
+$nav-tabs-border-color: $gray-300 !default;
+$nav-tabs-border-width: $border-width !default;
+$nav-tabs-border-radius: $border-radius !default;
+$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;
+$nav-tabs-link-active-color: $gray-700 !default;
+$nav-tabs-link-active-bg: $body-bg !default;
+$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;
+
+$nav-pills-border-radius: $border-radius !default;
+$nav-pills-link-active-color: $component-active-color !default;
+$nav-pills-link-active-bg: $component-active-bg !default;
+// scss-docs-end nav-variables
+
+
+// Navbar
+
+// scss-docs-start navbar-variables
+$navbar-padding-y: $spacer / 2 !default;
+$navbar-padding-x: null !default;
+
+$navbar-nav-link-padding-x: .5rem !default;
+
+$navbar-brand-font-size: $font-size-lg !default;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;
+$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;
+$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;
+$navbar-brand-margin-end: 1rem !default;
+
+$navbar-toggler-padding-y: .25rem !default;
+$navbar-toggler-padding-x: .75rem !default;
+$navbar-toggler-font-size: $font-size-lg !default;
+$navbar-toggler-border-radius: $btn-border-radius !default;
+$navbar-toggler-focus-width: $btn-focus-width !default;
+$navbar-toggler-transition: box-shadow .15s ease-in-out !default;
+// scss-docs-end navbar-variables
+
+// scss-docs-start navbar-theme-variables
+$navbar-dark-color: rgba($white, .55) !default;
+$navbar-dark-hover-color: rgba($white, .75) !default;
+$navbar-dark-active-color: $white !default;
+$navbar-dark-disabled-color: rgba($white, .25) !default;
+$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,
") !default;
+$navbar-dark-toggler-border-color: rgba($white, .1) !default;
+
+$navbar-light-color: rgba($black, .55) !default;
+$navbar-light-hover-color: rgba($black, .7) !default;
+$navbar-light-active-color: rgba($black, .9) !default;
+$navbar-light-disabled-color: rgba($black, .3) !default;
+$navbar-light-toggler-icon-bg: url("data:image/svg+xml,
") !default;
+$navbar-light-toggler-border-color: rgba($black, .1) !default;
+
+$navbar-light-brand-color: $navbar-light-active-color !default;
+$navbar-light-brand-hover-color: $navbar-light-active-color !default;
+$navbar-dark-brand-color: $navbar-dark-active-color !default;
+$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;
+// scss-docs-end navbar-theme-variables
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+// scss-docs-start dropdown-variables
+$dropdown-min-width: 10rem !default;
+$dropdown-padding-x: 0 !default;
+$dropdown-padding-y: .5rem !default;
+$dropdown-spacer: .125rem !default;
+$dropdown-font-size: $font-size-base !default;
+$dropdown-color: $body-color !default;
+$dropdown-bg: $white !default;
+$dropdown-border-color: rgba($black, .15) !default;
+$dropdown-border-radius: $border-radius !default;
+$dropdown-border-width: $border-width !default;
+$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;
+$dropdown-divider-bg: $dropdown-border-color !default;
+$dropdown-divider-margin-y: $spacer / 2 !default;
+$dropdown-box-shadow: $box-shadow !default;
+
+$dropdown-link-color: $gray-900 !default;
+$dropdown-link-hover-color: shade-color($gray-900, 10%) !default;
+$dropdown-link-hover-bg: $gray-200 !default;
+
+$dropdown-link-active-color: $component-active-color !default;
+$dropdown-link-active-bg: $component-active-bg !default;
+
+$dropdown-link-disabled-color: $gray-500 !default;
+
+$dropdown-item-padding-y: $spacer / 4 !default;
+$dropdown-item-padding-x: $spacer !default;
+
+$dropdown-header-color: $gray-600 !default;
+$dropdown-header-padding: $dropdown-padding-y $dropdown-item-padding-x !default;
+// scss-docs-end dropdown-variables
+
+// scss-docs-start dropdown-dark-variables
+$dropdown-dark-color: $gray-300 !default;
+$dropdown-dark-bg: $gray-800 !default;
+$dropdown-dark-border-color: $dropdown-border-color !default;
+$dropdown-dark-divider-bg: $dropdown-divider-bg !default;
+$dropdown-dark-box-shadow: null !default;
+$dropdown-dark-link-color: $dropdown-dark-color !default;
+$dropdown-dark-link-hover-color: $white !default;
+$dropdown-dark-link-hover-bg: rgba($white, .15) !default;
+$dropdown-dark-link-active-color: $dropdown-link-active-color !default;
+$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default;
+$dropdown-dark-link-disabled-color: $gray-500 !default;
+$dropdown-dark-header-color: $gray-500 !default;
+// scss-docs-end dropdown-dark-variables
+
+
+// Pagination
+
+// scss-docs-start pagination-variables
+$pagination-padding-y: .375rem !default;
+$pagination-padding-x: .75rem !default;
+$pagination-padding-y-sm: .25rem !default;
+$pagination-padding-x-sm: .5rem !default;
+$pagination-padding-y-lg: .75rem !default;
+$pagination-padding-x-lg: 1.5rem !default;
+
+$pagination-color: $link-color !default;
+$pagination-bg: $white !default;
+$pagination-border-width: $border-width !default;
+$pagination-border-radius: $border-radius !default;
+$pagination-margin-start: -$pagination-border-width !default;
+$pagination-border-color: $gray-300 !default;
+
+$pagination-focus-color: $link-hover-color !default;
+$pagination-focus-bg: $gray-200 !default;
+$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;
+$pagination-focus-outline: 0 !default;
+
+$pagination-hover-color: $link-hover-color !default;
+$pagination-hover-bg: $gray-200 !default;
+$pagination-hover-border-color: $gray-300 !default;
+
+$pagination-active-color: $component-active-color !default;
+$pagination-active-bg: $component-active-bg !default;
+$pagination-active-border-color: $pagination-active-bg !default;
+
+$pagination-disabled-color: $gray-600 !default;
+$pagination-disabled-bg: $white !default;
+$pagination-disabled-border-color: $gray-300 !default;
+
+$pagination-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$pagination-border-radius-sm: $border-radius-sm !default;
+$pagination-border-radius-lg: $border-radius-lg !default;
+// scss-docs-end pagination-variables
+
+
+// Cards
+
+// scss-docs-start card-variables
+$card-spacer-y: $spacer !default;
+$card-spacer-x: $spacer !default;
+$card-title-spacer-y: $spacer / 2 !default;
+$card-border-width: $border-width !default;
+$card-border-radius: $border-radius !default;
+$card-border-color: rgba($black, .125) !default;
+$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;
+$card-cap-padding-y: $card-spacer-y / 2 !default;
+$card-cap-padding-x: $card-spacer-x !default;
+$card-cap-bg: rgba($black, .03) !default;
+$card-cap-color: null !default;
+$card-height: null !default;
+$card-color: null !default;
+$card-bg: $white !default;
+$card-img-overlay-padding: $spacer !default;
+$card-group-margin: $grid-gutter-width / 2 !default;
+// scss-docs-end card-variables
+
+// Accordion
+
+// scss-docs-start accordion-variables
+$accordion-padding-y: 1rem !default;
+$accordion-padding-x: 1.25rem !default;
+$accordion-color: $body-color !default;
+$accordion-bg: $body-bg !default;
+$accordion-border-width: $border-width !default;
+$accordion-border-color: rgba($black, .125) !default;
+$accordion-border-radius: $border-radius !default;
+$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default;
+
+$accordion-body-padding-y: $accordion-padding-y !default;
+$accordion-body-padding-x: $accordion-padding-x !default;
+
+$accordion-button-padding-y: $accordion-padding-y !default;
+$accordion-button-padding-x: $accordion-padding-x !default;
+$accordion-button-color: $accordion-color !default;
+$accordion-button-bg: $accordion-bg !default;
+$accordion-transition: $btn-transition, border-radius .15s ease !default;
+$accordion-button-active-bg: tint-color($component-active-bg, 90%) !default;
+$accordion-button-active-color: shade-color($primary, 10%) !default;
+
+$accordion-button-focus-border-color: $input-focus-border-color !default;
+$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default;
+
+$accordion-icon-width: 1.25rem !default;
+$accordion-icon-color: $accordion-color !default;
+$accordion-icon-active-color: $accordion-button-active-color !default;
+$accordion-icon-transition: transform .2s ease-in-out !default;
+$accordion-icon-transform: rotate(-180deg) !default;
+
+$accordion-button-icon: url("data:image/svg+xml,
") !default;
+$accordion-button-active-icon: url("data:image/svg+xml,
") !default;
+// scss-docs-end accordion-variables
+
+// Tooltips
+
+// scss-docs-start tooltip-variables
+$tooltip-font-size: $font-size-sm !default;
+$tooltip-max-width: 200px !default;
+$tooltip-color: $white !default;
+$tooltip-bg: $black !default;
+$tooltip-border-radius: $border-radius !default;
+$tooltip-opacity: .9 !default;
+$tooltip-padding-y: $spacer / 4 !default;
+$tooltip-padding-x: $spacer / 2 !default;
+$tooltip-margin: 0 !default;
+
+$tooltip-arrow-width: .8rem !default;
+$tooltip-arrow-height: .4rem !default;
+$tooltip-arrow-color: $tooltip-bg !default;
+// scss-docs-end tooltip-variables
+
+// Form tooltips must come after regular tooltips
+// scss-docs-start tooltip-feedback-variables
+$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;
+$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;
+$form-feedback-tooltip-font-size: $tooltip-font-size !default;
+$form-feedback-tooltip-line-height: null !default;
+$form-feedback-tooltip-opacity: $tooltip-opacity !default;
+$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;
+// scss-docs-start tooltip-feedback-variables
+
+
+// Popovers
+
+// scss-docs-start popover-variables
+$popover-font-size: $font-size-sm !default;
+$popover-bg: $white !default;
+$popover-max-width: 276px !default;
+$popover-border-width: $border-width !default;
+$popover-border-color: rgba($black, .2) !default;
+$popover-border-radius: $border-radius-lg !default;
+$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;
+$popover-box-shadow: $box-shadow !default;
+
+$popover-header-bg: shade-color($popover-bg, 6%) !default;
+$popover-header-color: $headings-color !default;
+$popover-header-padding-y: .5rem !default;
+$popover-header-padding-x: $spacer !default;
+
+$popover-body-color: $body-color !default;
+$popover-body-padding-y: $spacer !default;
+$popover-body-padding-x: $spacer !default;
+
+$popover-arrow-width: 1rem !default;
+$popover-arrow-height: .5rem !default;
+$popover-arrow-color: $popover-bg !default;
+
+$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;
+// scss-docs-end popover-variables
+
+
+// Toasts
+
+// scss-docs-start toast-variables
+$toast-max-width: 350px !default;
+$toast-padding-x: .75rem !default;
+$toast-padding-y: .5rem !default;
+$toast-font-size: .875rem !default;
+$toast-color: null !default;
+$toast-background-color: rgba($white, .85) !default;
+$toast-border-width: 1px !default;
+$toast-border-color: rgba(0, 0, 0, .1) !default;
+$toast-border-radius: $border-radius !default;
+$toast-box-shadow: $box-shadow !default;
+$toast-spacing: $container-padding-x !default;
+
+$toast-header-color: $gray-600 !default;
+$toast-header-background-color: rgba($white, .85) !default;
+$toast-header-border-color: rgba(0, 0, 0, .05) !default;
+// scss-docs-end toast-variables
+
+
+// Badges
+
+// scss-docs-start badge-variables
+$badge-font-size: .75em !default;
+$badge-font-weight: $font-weight-bold !default;
+$badge-color: $white !default;
+$badge-padding-y: .35em !default;
+$badge-padding-x: .65em !default;
+$badge-border-radius: $border-radius !default;
+// scss-docs-end badge-variables
+
+
+// Modals
+
+// scss-docs-start modal-variables
+$modal-inner-padding: $spacer !default;
+
+$modal-footer-margin-between: .5rem !default;
+
+$modal-dialog-margin: .5rem !default;
+$modal-dialog-margin-y-sm-up: 1.75rem !default;
+
+$modal-title-line-height: $line-height-base !default;
+
+$modal-content-color: null !default;
+$modal-content-bg: $white !default;
+$modal-content-border-color: rgba($black, .2) !default;
+$modal-content-border-width: $border-width !default;
+$modal-content-border-radius: $border-radius-lg !default;
+$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;
+$modal-content-box-shadow-xs: $box-shadow-sm !default;
+$modal-content-box-shadow-sm-up: $box-shadow !default;
+
+$modal-backdrop-bg: $black !default;
+$modal-backdrop-opacity: .5 !default;
+$modal-header-border-color: $border-color !default;
+$modal-footer-border-color: $modal-header-border-color !default;
+$modal-header-border-width: $modal-content-border-width !default;
+$modal-footer-border-width: $modal-header-border-width !default;
+$modal-header-padding-y: $modal-inner-padding !default;
+$modal-header-padding-x: $modal-inner-padding !default;
+$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility
+
+$modal-sm: 300px !default;
+$modal-md: 500px !default;
+$modal-lg: 800px !default;
+$modal-xl: 1140px !default;
+
+$modal-fade-transform: translate(0, -50px) !default;
+$modal-show-transform: none !default;
+$modal-transition: transform .3s ease-out !default;
+$modal-scale-transform: scale(1.02) !default;
+// scss-docs-end modal-variables
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+// scss-docs-start alert-variables
+$alert-padding-y: $spacer !default;
+$alert-padding-x: $spacer !default;
+$alert-margin-bottom: 1rem !default;
+$alert-border-radius: $border-radius !default;
+$alert-link-font-weight: $font-weight-bold !default;
+$alert-border-width: $border-width !default;
+$alert-bg-scale: -80% !default;
+$alert-border-scale: -70% !default;
+$alert-color-scale: 40% !default;
+$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side
+// scss-docs-end alert-variables
+
+
+// Progress bars
+
+// scss-docs-start progress-variables
+$progress-height: 1rem !default;
+$progress-font-size: $font-size-base * .75 !default;
+$progress-bg: $gray-200 !default;
+$progress-border-radius: $border-radius !default;
+$progress-box-shadow: $box-shadow-inset !default;
+$progress-bar-color: $white !default;
+$progress-bar-bg: $primary !default;
+$progress-bar-animation-timing: 1s linear infinite !default;
+$progress-bar-transition: width .6s ease !default;
+// scss-docs-end progress-variables
+
+
+// List group
+
+// scss-docs-start list-group-variables
+$list-group-color: $gray-900 !default;
+$list-group-bg: $white !default;
+$list-group-border-color: rgba($black, .125) !default;
+$list-group-border-width: $border-width !default;
+$list-group-border-radius: $border-radius !default;
+
+$list-group-item-padding-y: $spacer / 2 !default;
+$list-group-item-padding-x: $spacer !default;
+$list-group-item-bg-scale: -80% !default;
+$list-group-item-color-scale: 40% !default;
+
+$list-group-hover-bg: $gray-100 !default;
+$list-group-active-color: $component-active-color !default;
+$list-group-active-bg: $component-active-bg !default;
+$list-group-active-border-color: $list-group-active-bg !default;
+
+$list-group-disabled-color: $gray-600 !default;
+$list-group-disabled-bg: $list-group-bg !default;
+
+$list-group-action-color: $gray-700 !default;
+$list-group-action-hover-color: $list-group-action-color !default;
+
+$list-group-action-active-color: $body-color !default;
+$list-group-action-active-bg: $gray-200 !default;
+// scss-docs-end list-group-variables
+
+
+// Image thumbnails
+
+// scss-docs-start thumbnail-variables
+$thumbnail-padding: .25rem !default;
+$thumbnail-bg: $body-bg !default;
+$thumbnail-border-width: $border-width !default;
+$thumbnail-border-color: $gray-300 !default;
+$thumbnail-border-radius: $border-radius !default;
+$thumbnail-box-shadow: $box-shadow-sm !default;
+// scss-docs-end thumbnail-variables
+
+
+// Figures
+
+// scss-docs-start figure-variables
+$figure-caption-font-size: $small-font-size !default;
+$figure-caption-color: $gray-600 !default;
+// scss-docs-end figure-variables
+
+
+// Breadcrumbs
+
+// scss-docs-start breadcrumb-variables
+$breadcrumb-font-size: null !default;
+$breadcrumb-padding-y: 0 !default;
+$breadcrumb-padding-x: 0 !default;
+$breadcrumb-item-padding-x: .5rem !default;
+$breadcrumb-margin-bottom: 1rem !default;
+$breadcrumb-bg: null !default;
+$breadcrumb-divider-color: $gray-600 !default;
+$breadcrumb-active-color: $gray-600 !default;
+$breadcrumb-divider: quote("/") !default;
+$breadcrumb-divider-flipped: $breadcrumb-divider !default;
+$breadcrumb-border-radius: null !default;
+// scss-docs-end breadcrumb-variables
+
+// Carousel
+
+// scss-docs-start carousel-variables
+$carousel-control-color: $white !default;
+$carousel-control-width: 15% !default;
+$carousel-control-opacity: .5 !default;
+$carousel-control-hover-opacity: .9 !default;
+$carousel-control-transition: opacity .15s ease !default;
+
+$carousel-indicator-width: 30px !default;
+$carousel-indicator-height: 3px !default;
+$carousel-indicator-hit-area-height: 10px !default;
+$carousel-indicator-spacer: 3px !default;
+$carousel-indicator-opacity: .5 !default;
+$carousel-indicator-active-bg: $white !default;
+$carousel-indicator-active-opacity: 1 !default;
+$carousel-indicator-transition: opacity .6s ease !default;
+
+$carousel-caption-width: 70% !default;
+$carousel-caption-color: $white !default;
+$carousel-caption-padding-y: 1.25rem !default;
+$carousel-caption-spacer: 1.25rem !default;
+
+$carousel-control-icon-width: 2rem !default;
+
+$carousel-control-prev-icon-bg: url("data:image/svg+xml,
") !default;
+$carousel-control-next-icon-bg: url("data:image/svg+xml,
") !default;
+
+$carousel-transition-duration: .6s !default;
+$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
+
+$carousel-dark-indicator-active-bg: $black !default;
+$carousel-dark-caption-color: $black !default;
+$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default;
+// scss-docs-end carousel-variables
+
+
+// Spinners
+
+// scss-docs-start spinner-variables
+$spinner-width: 2rem !default;
+$spinner-height: $spinner-width !default;
+$spinner-vertical-align: -.125em !default;
+$spinner-border-width: .25em !default;
+$spinner-animation-speed: .75s !default;
+
+$spinner-width-sm: 1rem !default;
+$spinner-height-sm: $spinner-width-sm !default;
+$spinner-border-width-sm: .2em !default;
+// scss-docs-end spinner-variables
+
+
+// Close
+
+// scss-docs-start close-variables
+$btn-close-width: 1em !default;
+$btn-close-height: $btn-close-width !default;
+$btn-close-padding-x: .25em !default;
+$btn-close-padding-y: $btn-close-padding-x !default;
+$btn-close-color: $black !default;
+$btn-close-bg: url("data:image/svg+xml,
") !default;
+$btn-close-focus-shadow: $input-btn-focus-box-shadow !default;
+$btn-close-opacity: .5 !default;
+$btn-close-hover-opacity: .75 !default;
+$btn-close-focus-opacity: 1 !default;
+$btn-close-disabled-opacity: .25 !default;
+$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default;
+// scss-docs-end close-variables
+
+
+// Offcanvas
+
+// scss-docs-start offcanvas-variables
+$offcanvas-padding-y: $modal-inner-padding !default;
+$offcanvas-padding-x: $modal-inner-padding !default;
+$offcanvas-horizontal-width: 400px !default;
+$offcanvas-vertical-height: 30vh !default;
+$offcanvas-transition-duration: .3s !default;
+$offcanvas-border-color: $modal-content-border-color !default;
+$offcanvas-border-width: $modal-content-border-width !default;
+$offcanvas-title-line-height: $modal-title-line-height !default;
+$offcanvas-bg-color: $modal-content-bg !default;
+$offcanvas-color: $modal-content-color !default;
+$offcanvas-box-shadow: $modal-content-box-shadow-xs !default;
+// scss-docs-end offcanvas-variables
+
+// Code
+
+$code-font-size: $small-font-size !default;
+$code-color: $pink !default;
+
+$kbd-padding-y: .2rem !default;
+$kbd-padding-x: .4rem !default;
+$kbd-font-size: $code-font-size !default;
+$kbd-color: $white !default;
+$kbd-bg: $gray-900 !default;
+
+$pre-color: null !default;
diff --git a/node_modules/bootstrap/scss/bootstrap-grid.scss b/node_modules/bootstrap/scss/bootstrap-grid.scss
new file mode 100644
index 00000000..5f3a14d1
--- /dev/null
+++ b/node_modules/bootstrap/scss/bootstrap-grid.scss
@@ -0,0 +1,65 @@
+/*!
+ * Bootstrap Grid v5.0.1 (https://getbootstrap.com/)
+ * Copyright 2011-2021 The Bootstrap Authors
+ * Copyright 2011-2021 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+
+$include-column-box-sizing: true !default;
+
+@import "functions";
+@import "variables";
+
+@import "mixins/lists";
+@import "mixins/breakpoints";
+@import "mixins/container";
+@import "mixins/grid";
+@import "mixins/utilities";
+
+@import "vendor/rfs";
+
+@import "containers";
+@import "grid";
+
+@import "utilities";
+// Only use the utilities we need
+// stylelint-disable-next-line scss/dollar-variable-default
+$utilities: map-get-multiple(
+ $utilities,
+ (
+ "display",
+ "order",
+ "flex",
+ "flex-direction",
+ "flex-grow",
+ "flex-shrink",
+ "flex-wrap",
+ "justify-content",
+ "align-items",
+ "align-content",
+ "align-self",
+ "margin",
+ "margin-x",
+ "margin-y",
+ "margin-top",
+ "margin-end",
+ "margin-bottom",
+ "margin-start",
+ "negative-margin",
+ "negative-margin-x",
+ "negative-margin-y",
+ "negative-margin-top",
+ "negative-margin-end",
+ "negative-margin-bottom",
+ "negative-margin-start",
+ "padding",
+ "padding-x",
+ "padding-y",
+ "padding-top",
+ "padding-end",
+ "padding-bottom",
+ "padding-start",
+ )
+);
+
+@import "utilities/api";
diff --git a/node_modules/bootstrap/scss/bootstrap-reboot.scss b/node_modules/bootstrap/scss/bootstrap-reboot.scss
new file mode 100644
index 00000000..3fb5fa0e
--- /dev/null
+++ b/node_modules/bootstrap/scss/bootstrap-reboot.scss
@@ -0,0 +1,15 @@
+/*!
+ * Bootstrap Reboot v5.0.1 (https://getbootstrap.com/)
+ * Copyright 2011-2021 The Bootstrap Authors
+ * Copyright 2011-2021 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
+ */
+
+@import "functions";
+@import "variables";
+// Prevent the usage of custom properties since we don't add them to `:root` in reboot
+$font-family-base: $font-family-sans-serif; // stylelint-disable-line scss/dollar-variable-default
+$font-family-code: $font-family-monospace; // stylelint-disable-line scss/dollar-variable-default
+@import "mixins";
+@import "reboot";
diff --git a/node_modules/bootstrap/scss/bootstrap-utilities.scss b/node_modules/bootstrap/scss/bootstrap-utilities.scss
new file mode 100644
index 00000000..f921a5d1
--- /dev/null
+++ b/node_modules/bootstrap/scss/bootstrap-utilities.scss
@@ -0,0 +1,18 @@
+/*!
+ * Bootstrap Utilities v5.0.1 (https://getbootstrap.com/)
+ * Copyright 2011-2021 The Bootstrap Authors
+ * Copyright 2011-2021 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+
+// Configuration
+@import "functions";
+@import "variables";
+@import "mixins";
+@import "utilities";
+
+// Helpers
+@import "helpers";
+
+// Utilities
+@import "utilities/api";
diff --git a/node_modules/bootstrap/scss/bootstrap.scss b/node_modules/bootstrap/scss/bootstrap.scss
new file mode 100644
index 00000000..86c079db
--- /dev/null
+++ b/node_modules/bootstrap/scss/bootstrap.scss
@@ -0,0 +1,52 @@
+/*!
+ * Bootstrap v5.0.1 (https://getbootstrap.com/)
+ * Copyright 2011-2021 The Bootstrap Authors
+ * Copyright 2011-2021 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+
+// scss-docs-start import-stack
+// Configuration
+@import "functions";
+@import "variables";
+@import "mixins";
+@import "utilities";
+
+// Layout & components
+@import "root";
+@import "reboot";
+@import "type";
+@import "images";
+@import "containers";
+@import "grid";
+@import "tables";
+@import "forms";
+@import "buttons";
+@import "transitions";
+@import "dropdown";
+@import "button-group";
+@import "nav";
+@import "navbar";
+@import "card";
+@import "accordion";
+@import "breadcrumb";
+@import "pagination";
+@import "badge";
+@import "alert";
+@import "progress";
+@import "list-group";
+@import "close";
+@import "toasts";
+@import "modal";
+@import "tooltip";
+@import "popover";
+@import "carousel";
+@import "spinners";
+@import "offcanvas";
+
+// Helpers
+@import "helpers";
+
+// Utilities
+@import "utilities/api";
+// scss-docs-end import-stack
diff --git a/node_modules/bootstrap/scss/forms/_floating-labels.scss b/node_modules/bootstrap/scss/forms/_floating-labels.scss
new file mode 100644
index 00000000..8b2e2b8e
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_floating-labels.scss
@@ -0,0 +1,61 @@
+.form-floating {
+ position: relative;
+
+ > .form-control,
+ > .form-select {
+ height: $form-floating-height;
+ padding: $form-floating-padding-y $form-floating-padding-x;
+ }
+
+ > label {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%; // allow textareas
+ padding: $form-floating-padding-y $form-floating-padding-x;
+ pointer-events: none;
+ border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
+ transform-origin: 0 0;
+ @include transition($form-floating-transition);
+ }
+
+ // stylelint-disable no-duplicate-selectors
+ > .form-control {
+ &::placeholder {
+ color: transparent;
+ }
+
+ &:focus,
+ &:not(:placeholder-shown) {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ &:-webkit-autofill {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ }
+
+ > .form-select {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+
+ > .form-control:focus,
+ > .form-control:not(:placeholder-shown),
+ > .form-select {
+ ~ label {
+ opacity: $form-floating-label-opacity;
+ transform: $form-floating-label-transform;
+ }
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ > .form-control:-webkit-autofill {
+ ~ label {
+ opacity: $form-floating-label-opacity;
+ transform: $form-floating-label-transform;
+ }
+ }
+ // stylelint-enable no-duplicate-selectors
+}
diff --git a/node_modules/bootstrap/scss/forms/_form-check.scss b/node_modules/bootstrap/scss/forms/_form-check.scss
new file mode 100644
index 00000000..b34250a7
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_form-check.scss
@@ -0,0 +1,152 @@
+//
+// Check/radio
+//
+
+.form-check {
+ display: block;
+ min-height: $form-check-min-height;
+ padding-left: $form-check-padding-start;
+ margin-bottom: $form-check-margin-bottom;
+
+ .form-check-input {
+ float: left;
+ margin-left: $form-check-padding-start * -1;
+ }
+}
+
+.form-check-input {
+ width: $form-check-input-width;
+ height: $form-check-input-width;
+ margin-top: ($line-height-base - $form-check-input-width) / 2; // line-height minus check height
+ vertical-align: top;
+ background-color: $form-check-input-bg;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ border: $form-check-input-border;
+ appearance: none;
+ color-adjust: exact; // Keep themed appearance for print
+ @include transition($form-check-transition);
+
+ &[type="checkbox"] {
+ @include border-radius($form-check-input-border-radius);
+ }
+
+ &[type="radio"] {
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: $form-check-radio-border-radius;
+ }
+
+ &:active {
+ filter: $form-check-input-active-filter;
+ }
+
+ &:focus {
+ border-color: $form-check-input-focus-border;
+ outline: 0;
+ box-shadow: $form-check-input-focus-box-shadow;
+ }
+
+ &:checked {
+ background-color: $form-check-input-checked-bg-color;
+ border-color: $form-check-input-checked-border-color;
+
+ &[type="checkbox"] {
+ @if $enable-gradients {
+ background-image: escape-svg($form-check-input-checked-bg-image), var(--#{$variable-prefix}gradient);
+ } @else {
+ background-image: escape-svg($form-check-input-checked-bg-image);
+ }
+ }
+
+ &[type="radio"] {
+ @if $enable-gradients {
+ background-image: escape-svg($form-check-radio-checked-bg-image), var(--#{$variable-prefix}gradient);
+ } @else {
+ background-image: escape-svg($form-check-radio-checked-bg-image);
+ }
+ }
+ }
+
+ &[type="checkbox"]:indeterminate {
+ background-color: $form-check-input-indeterminate-bg-color;
+ border-color: $form-check-input-indeterminate-border-color;
+
+ @if $enable-gradients {
+ background-image: escape-svg($form-check-input-indeterminate-bg-image), var(--#{$variable-prefix}gradient);
+ } @else {
+ background-image: escape-svg($form-check-input-indeterminate-bg-image);
+ }
+ }
+
+ &:disabled {
+ pointer-events: none;
+ filter: none;
+ opacity: $form-check-input-disabled-opacity;
+ }
+
+ // Use disabled attribute in addition of :disabled pseudo-class
+ // See: https://github.com/twbs/bootstrap/issues/28247
+ &[disabled],
+ &:disabled {
+ ~ .form-check-label {
+ opacity: $form-check-label-disabled-opacity;
+ }
+ }
+}
+
+.form-check-label {
+ color: $form-check-label-color;
+ cursor: $form-check-label-cursor;
+}
+
+//
+// Switch
+//
+
+.form-switch {
+ padding-left: $form-switch-padding-start;
+
+ .form-check-input {
+ width: $form-switch-width;
+ margin-left: $form-switch-padding-start * -1;
+ background-image: escape-svg($form-switch-bg-image);
+ background-position: left center;
+ @include border-radius($form-switch-border-radius);
+ @include transition($form-switch-transition);
+
+ &:focus {
+ background-image: escape-svg($form-switch-focus-bg-image);
+ }
+
+ &:checked {
+ background-position: $form-switch-checked-bg-position;
+
+ @if $enable-gradients {
+ background-image: escape-svg($form-switch-checked-bg-image), var(--#{$variable-prefix}gradient);
+ } @else {
+ background-image: escape-svg($form-switch-checked-bg-image);
+ }
+ }
+ }
+}
+
+.form-check-inline {
+ display: inline-block;
+ margin-right: $form-check-inline-margin-end;
+}
+
+.btn-check {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+
+ &[disabled],
+ &:disabled {
+ + .btn {
+ pointer-events: none;
+ filter: none;
+ opacity: $form-check-btn-check-disabled-opacity;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/forms/_form-control.scss b/node_modules/bootstrap/scss/forms/_form-control.scss
new file mode 100644
index 00000000..9728b91f
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_form-control.scss
@@ -0,0 +1,219 @@
+//
+// General form controls (plus a few specific high-level interventions)
+//
+
+.form-control {
+ display: block;
+ width: 100%;
+ padding: $input-padding-y $input-padding-x;
+ font-family: $input-font-family;
+ @include font-size($input-font-size);
+ font-weight: $input-font-weight;
+ line-height: $input-line-height;
+ color: $input-color;
+ background-color: $input-bg;
+ background-clip: padding-box;
+ border: $input-border-width solid $input-border-color;
+ appearance: none; // Fix appearance for date inputs in Safari
+
+ // Note: This has no effect on
s in some browsers, due to the limited stylability of ``s in CSS.
+ @include border-radius($input-border-radius, 0);
+
+ @include box-shadow($input-box-shadow);
+ @include transition($input-transition);
+
+ &[type="file"] {
+ overflow: hidden; // prevent pseudo element button overlap
+
+ &:not(:disabled):not([readonly]) {
+ cursor: pointer;
+ }
+ }
+
+ // Customize the `:focus` state to imitate native WebKit styles.
+ &:focus {
+ color: $input-focus-color;
+ background-color: $input-focus-bg;
+ border-color: $input-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ @include box-shadow($input-box-shadow, $input-focus-box-shadow);
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: $input-focus-box-shadow;
+ }
+ }
+
+ // Add some height to date inputs on iOS
+ // https://github.com/twbs/bootstrap/issues/23307
+ // TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved
+ &::-webkit-date-and-time-value {
+ // Multiply line-height by 1em if it has no unit
+ height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
+ }
+
+ // Placeholder
+ &::placeholder {
+ color: $input-placeholder-color;
+ // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
+ opacity: 1;
+ }
+
+ // Disabled and read-only inputs
+ //
+ // HTML5 says that controls under a fieldset > legend:first-child won't be
+ // disabled if the fieldset is disabled. Due to implementation difficulty, we
+ // don't honor that edge case; we style them as disabled anyway.
+ &:disabled,
+ &[readonly] {
+ background-color: $input-disabled-bg;
+ border-color: $input-disabled-border-color;
+ // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+ opacity: 1;
+ }
+
+ // File input buttons theming
+ &::file-selector-button {
+ padding: $input-padding-y $input-padding-x;
+ margin: (-$input-padding-y) (-$input-padding-x);
+ margin-inline-end: $input-padding-x;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: $input-border-width;
+ border-radius: 0; // stylelint-disable-line property-disallowed-list
+ @include transition($btn-transition);
+ }
+
+ &:hover:not(:disabled):not([readonly])::file-selector-button {
+ background-color: $form-file-button-hover-bg;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y $input-padding-x;
+ margin: (-$input-padding-y) (-$input-padding-x);
+ margin-inline-end: $input-padding-x;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: $input-border-width;
+ border-radius: 0; // stylelint-disable-line property-disallowed-list
+ @include transition($btn-transition);
+ }
+
+ &:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
+ background-color: $form-file-button-hover-bg;
+ }
+}
+
+// Readonly controls as plain text
+//
+// Apply class to a readonly input to make it appear like regular plain
+// text (without any border, background color, focus indicator)
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: $input-padding-y 0;
+ margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+ line-height: $input-line-height;
+ color: $input-plaintext-color;
+ background-color: transparent;
+ border: solid transparent;
+ border-width: $input-border-width 0;
+
+ &.form-control-sm,
+ &.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+//
+// Repeated in `_input_group.scss` to avoid Sass extend issues.
+
+.form-control-sm {
+ min-height: $input-height-sm;
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ @include border-radius($input-border-radius-sm);
+
+ &::file-selector-button {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
+ margin-inline-end: $input-padding-x-sm;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
+ margin-inline-end: $input-padding-x-sm;
+ }
+}
+
+.form-control-lg {
+ min-height: $input-height-lg;
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ @include border-radius($input-border-radius-lg);
+
+ &::file-selector-button {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
+ margin-inline-end: $input-padding-x-lg;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
+ margin-inline-end: $input-padding-x-lg;
+ }
+}
+
+// Make sure textareas don't shrink too much when resized
+// https://github.com/twbs/bootstrap/pull/29124
+// stylelint-disable selector-no-qualifying-type
+textarea {
+ &.form-control {
+ min-height: $input-height;
+ }
+
+ &.form-control-sm {
+ min-height: $input-height-sm;
+ }
+
+ &.form-control-lg {
+ min-height: $input-height-lg;
+ }
+}
+// stylelint-enable selector-no-qualifying-type
+
+.form-control-color {
+ max-width: 3rem;
+ height: auto; // Override fixed browser height
+ padding: $input-padding-y;
+
+ &:not(:disabled):not([readonly]) {
+ cursor: pointer;
+ }
+
+ &::-moz-color-swatch {
+ height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
+ @include border-radius($input-border-radius);
+ }
+
+ &::-webkit-color-swatch {
+ height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
+ @include border-radius($input-border-radius);
+ }
+}
diff --git a/node_modules/bootstrap/scss/forms/_form-range.scss b/node_modules/bootstrap/scss/forms/_form-range.scss
new file mode 100644
index 00000000..ae1d841d
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_form-range.scss
@@ -0,0 +1,91 @@
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.form-range {
+ width: 100%;
+ height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2);
+ padding: 0; // Need to reset padding
+ background-color: transparent;
+ appearance: none;
+
+ &:focus {
+ outline: 0;
+
+ // Pseudo-elements must be split across multiple rulesets to have an effect.
+ // No box-shadow() mixin for focus accessibility.
+ &::-webkit-slider-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ &::-moz-range-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ }
+
+ &::-moz-focus-outer {
+ border: 0;
+ }
+
+ &::-webkit-slider-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ margin-top: ($form-range-track-height - $form-range-thumb-height) / 2; // Webkit specific
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($form-range-thumb-transition);
+ appearance: none;
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-webkit-slider-runnable-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent; // Why?
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent;
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &::-moz-range-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($form-range-thumb-transition);
+ appearance: none;
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-moz-range-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent;
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent; // Firefox specific?
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &:disabled {
+ pointer-events: none;
+
+ &::-webkit-slider-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+
+ &::-moz-range-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/forms/_form-select.scss b/node_modules/bootstrap/scss/forms/_form-select.scss
new file mode 100644
index 00000000..15d5061a
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_form-select.scss
@@ -0,0 +1,67 @@
+// Select
+//
+// Replaces the browser default select with a custom one, mostly pulled from
+// https://primer.github.io/.
+
+.form-select {
+ display: block;
+ width: 100%;
+ padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x;
+ font-family: $form-select-font-family;
+ @include font-size($form-select-font-size);
+ font-weight: $form-select-font-weight;
+ line-height: $form-select-line-height;
+ color: $form-select-color;
+ background-color: $form-select-bg;
+ background-image: escape-svg($form-select-indicator);
+ background-repeat: no-repeat;
+ background-position: $form-select-bg-position;
+ background-size: $form-select-bg-size;
+ border: $form-select-border-width solid $form-select-border-color;
+ @include border-radius($form-select-border-radius, 0);
+ @include box-shadow($form-select-box-shadow);
+ appearance: none;
+
+ &:focus {
+ border-color: $form-select-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ @include box-shadow($form-select-box-shadow, $form-select-focus-box-shadow);
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: $form-select-focus-box-shadow;
+ }
+ }
+
+ &[multiple],
+ &[size]:not([size="1"]) {
+ padding-right: $form-select-padding-x;
+ background-image: none;
+ }
+
+ &:disabled {
+ color: $form-select-disabled-color;
+ background-color: $form-select-disabled-bg;
+ border-color: $form-select-disabled-border-color;
+ }
+
+ // Remove outline from select box in FF
+ &:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 $form-select-color;
+ }
+}
+
+.form-select-sm {
+ padding-top: $form-select-padding-y-sm;
+ padding-bottom: $form-select-padding-y-sm;
+ padding-left: $form-select-padding-x-sm;
+ @include font-size($form-select-font-size-sm);
+}
+
+.form-select-lg {
+ padding-top: $form-select-padding-y-lg;
+ padding-bottom: $form-select-padding-y-lg;
+ padding-left: $form-select-padding-x-lg;
+ @include font-size($form-select-font-size-lg);
+}
diff --git a/node_modules/bootstrap/scss/forms/_form-text.scss b/node_modules/bootstrap/scss/forms/_form-text.scss
new file mode 100644
index 00000000..f080d1a2
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_form-text.scss
@@ -0,0 +1,11 @@
+//
+// Form text
+//
+
+.form-text {
+ margin-top: $form-text-margin-top;
+ @include font-size($form-text-font-size);
+ font-style: $form-text-font-style;
+ font-weight: $form-text-font-weight;
+ color: $form-text-color;
+}
diff --git a/node_modules/bootstrap/scss/forms/_input-group.scss b/node_modules/bootstrap/scss/forms/_input-group.scss
new file mode 100644
index 00000000..14005293
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_input-group.scss
@@ -0,0 +1,121 @@
+//
+// Base styles
+//
+
+.input-group {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // For form validation feedback
+ align-items: stretch;
+ width: 100%;
+
+ > .form-control,
+ > .form-select {
+ position: relative; // For focus state's z-index
+ flex: 1 1 auto;
+ width: 1%;
+ min-width: 0; // https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size
+ }
+
+ // Bring the "active" form control to the top of surrounding elements
+ > .form-control:focus,
+ > .form-select:focus {
+ z-index: 3;
+ }
+
+ // Ensure buttons are always above inputs for more visually pleasing borders.
+ // This isn't needed for `.input-group-text` since it shares the same border-color
+ // as our inputs.
+ .btn {
+ position: relative;
+ z-index: 2;
+
+ &:focus {
+ z-index: 3;
+ }
+ }
+}
+
+
+// Textual addons
+//
+// Serves as a catch-all element for any text or radio/checkbox input you wish
+// to prepend or append to an input.
+
+.input-group-text {
+ display: flex;
+ align-items: center;
+ padding: $input-group-addon-padding-y $input-group-addon-padding-x;
+ @include font-size($input-font-size); // Match inputs
+ font-weight: $input-group-addon-font-weight;
+ line-height: $input-line-height;
+ color: $input-group-addon-color;
+ text-align: center;
+ white-space: nowrap;
+ background-color: $input-group-addon-bg;
+ border: $input-border-width solid $input-group-addon-border-color;
+ @include border-radius($input-border-radius);
+}
+
+
+// Sizing
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control,
+.input-group-lg > .form-select,
+.input-group-lg > .input-group-text,
+.input-group-lg > .btn {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ @include border-radius($input-border-radius-lg);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .form-select,
+.input-group-sm > .input-group-text,
+.input-group-sm > .btn {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ @include border-radius($input-border-radius-sm);
+}
+
+.input-group-lg > .form-select,
+.input-group-sm > .form-select {
+ padding-right: $form-select-padding-x + $form-select-indicator-padding;
+}
+
+
+// Rounded corners
+//
+// These rulesets must come after the sizing ones to properly override sm and lg
+// border-radius values when extending. They're more specific than we'd like
+// with the `.input-group >` part, but without it, we cannot override the sizing.
+
+// stylelint-disable-next-line no-duplicate-selectors
+.input-group {
+ &:not(.has-validation) {
+ > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),
+ > .dropdown-toggle:nth-last-child(n + 3) {
+ @include border-end-radius(0);
+ }
+ }
+
+ &.has-validation {
+ > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu),
+ > .dropdown-toggle:nth-last-child(n + 4) {
+ @include border-end-radius(0);
+ }
+ }
+
+ $validation-messages: "";
+ @each $state in map-keys($form-validation-states) {
+ $validation-messages: $validation-messages + ":not(." + unquote($state) + "-tooltip)" + ":not(." + unquote($state) + "-feedback)";
+ }
+
+ > :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
+ margin-left: -$input-border-width;
+ @include border-start-radius(0);
+ }
+}
diff --git a/node_modules/bootstrap/scss/forms/_labels.scss b/node_modules/bootstrap/scss/forms/_labels.scss
new file mode 100644
index 00000000..39ecafcd
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_labels.scss
@@ -0,0 +1,36 @@
+//
+// Labels
+//
+
+.form-label {
+ margin-bottom: $form-label-margin-bottom;
+ @include font-size($form-label-font-size);
+ font-style: $form-label-font-style;
+ font-weight: $form-label-font-weight;
+ color: $form-label-color;
+}
+
+// For use with horizontal and inline forms, when you need the label (or legend)
+// text to align with the form controls.
+.col-form-label {
+ padding-top: add($input-padding-y, $input-border-width);
+ padding-bottom: add($input-padding-y, $input-border-width);
+ margin-bottom: 0; // Override the `` default
+ @include font-size(inherit); // Override the `` default
+ font-style: $form-label-font-style;
+ font-weight: $form-label-font-weight;
+ line-height: $input-line-height;
+ color: $form-label-color;
+}
+
+.col-form-label-lg {
+ padding-top: add($input-padding-y-lg, $input-border-width);
+ padding-bottom: add($input-padding-y-lg, $input-border-width);
+ @include font-size($input-font-size-lg);
+}
+
+.col-form-label-sm {
+ padding-top: add($input-padding-y-sm, $input-border-width);
+ padding-bottom: add($input-padding-y-sm, $input-border-width);
+ @include font-size($input-font-size-sm);
+}
diff --git a/node_modules/bootstrap/scss/forms/_validation.scss b/node_modules/bootstrap/scss/forms/_validation.scss
new file mode 100644
index 00000000..c48123a7
--- /dev/null
+++ b/node_modules/bootstrap/scss/forms/_validation.scss
@@ -0,0 +1,12 @@
+// Form validation
+//
+// Provide feedback to users when form field values are valid or invalid. Works
+// primarily for client-side validation via scoped `:invalid` and `:valid`
+// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
+// server-side validation.
+
+// scss-docs-start form-validation-states-loop
+@each $state, $data in $form-validation-states {
+ @include form-validation-state($state, $data...);
+}
+// scss-docs-end form-validation-states-loop
diff --git a/node_modules/bootstrap/scss/helpers/_clearfix.scss b/node_modules/bootstrap/scss/helpers/_clearfix.scss
new file mode 100644
index 00000000..e92522a9
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_clearfix.scss
@@ -0,0 +1,3 @@
+.clearfix {
+ @include clearfix();
+}
diff --git a/node_modules/bootstrap/scss/helpers/_colored-links.scss b/node_modules/bootstrap/scss/helpers/_colored-links.scss
new file mode 100644
index 00000000..8c167ded
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_colored-links.scss
@@ -0,0 +1,12 @@
+@each $color, $value in $theme-colors {
+ .link-#{$color} {
+ color: $value;
+
+ @if $link-shade-percentage != 0 {
+ &:hover,
+ &:focus {
+ color: if(color-contrast($value) == $color-contrast-light, shade-color($value, $link-shade-percentage), tint-color($value, $link-shade-percentage));
+ }
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/helpers/_position.scss b/node_modules/bootstrap/scss/helpers/_position.scss
new file mode 100644
index 00000000..31851eb7
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_position.scss
@@ -0,0 +1,30 @@
+// Shorthand
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: $zindex-fixed;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $zindex-fixed;
+}
+
+// Responsive sticky top
+@each $breakpoint in map-keys($grid-breakpoints) {
+ @include media-breakpoint-up($breakpoint) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ .sticky#{$infix}-top {
+ position: sticky;
+ top: 0;
+ z-index: $zindex-sticky;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/helpers/_ratio.scss b/node_modules/bootstrap/scss/helpers/_ratio.scss
new file mode 100644
index 00000000..2390ee33
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_ratio.scss
@@ -0,0 +1,26 @@
+// Credit: Nicolas Gallagher and SUIT CSS.
+
+.ratio {
+ position: relative;
+ width: 100%;
+
+ &::before {
+ display: block;
+ padding-top: var(--#{$variable-prefix}aspect-ratio);
+ content: "";
+ }
+
+ > * {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+}
+
+@each $key, $ratio in $aspect-ratios {
+ .ratio-#{$key} {
+ --#{$variable-prefix}aspect-ratio: #{$ratio};
+ }
+}
diff --git a/node_modules/bootstrap/scss/helpers/_stretched-link.scss b/node_modules/bootstrap/scss/helpers/_stretched-link.scss
new file mode 100644
index 00000000..71a1c755
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_stretched-link.scss
@@ -0,0 +1,15 @@
+//
+// Stretched link
+//
+
+.stretched-link {
+ &::#{$stretched-link-pseudo-element} {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $stretched-link-z-index;
+ content: "";
+ }
+}
diff --git a/node_modules/bootstrap/scss/helpers/_text-truncation.scss b/node_modules/bootstrap/scss/helpers/_text-truncation.scss
new file mode 100644
index 00000000..6421dac9
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_text-truncation.scss
@@ -0,0 +1,7 @@
+//
+// Text truncation
+//
+
+.text-truncate {
+ @include text-truncate();
+}
diff --git a/node_modules/bootstrap/scss/helpers/_visually-hidden.scss b/node_modules/bootstrap/scss/helpers/_visually-hidden.scss
new file mode 100644
index 00000000..4760ff03
--- /dev/null
+++ b/node_modules/bootstrap/scss/helpers/_visually-hidden.scss
@@ -0,0 +1,8 @@
+//
+// Visually hidden
+//
+
+.visually-hidden,
+.visually-hidden-focusable:not(:focus):not(:focus-within) {
+ @include visually-hidden();
+}
diff --git a/node_modules/bootstrap/scss/mixins/_alert.scss b/node_modules/bootstrap/scss/mixins/_alert.scss
new file mode 100644
index 00000000..f3eb5951
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_alert.scss
@@ -0,0 +1,11 @@
+// scss-docs-start alert-variant-mixin
+@mixin alert-variant($background, $border, $color) {
+ color: $color;
+ @include gradient-bg($background);
+ border-color: $border;
+
+ .alert-link {
+ color: shade-color($color, 20%);
+ }
+}
+// scss-docs-end alert-variant-mixin
diff --git a/node_modules/bootstrap/scss/mixins/_border-radius.scss b/node_modules/bootstrap/scss/mixins/_border-radius.scss
new file mode 100644
index 00000000..616decbc
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_border-radius.scss
@@ -0,0 +1,78 @@
+// stylelint-disable property-disallowed-list
+// Single side border-radius
+
+// Helper function to replace negative values with 0
+@function valid-radius($radius) {
+ $return: ();
+ @each $value in $radius {
+ @if type-of($value) == number {
+ $return: append($return, max($value, 0));
+ } @else {
+ $return: append($return, $value);
+ }
+ }
+ @return $return;
+}
+
+// scss-docs-start border-radius-mixins
+@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {
+ @if $enable-rounded {
+ border-radius: valid-radius($radius);
+ }
+ @else if $fallback-border-radius != false {
+ border-radius: $fallback-border-radius;
+ }
+}
+
+@mixin border-top-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ border-top-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-right-radius: valid-radius($radius);
+ border-bottom-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-right-radius: valid-radius($radius);
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-top-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-top-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+// scss-docs-end border-radius-mixins
diff --git a/node_modules/bootstrap/scss/mixins/_box-shadow.scss b/node_modules/bootstrap/scss/mixins/_box-shadow.scss
new file mode 100644
index 00000000..4172541f
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_box-shadow.scss
@@ -0,0 +1,18 @@
+@mixin box-shadow($shadow...) {
+ @if $enable-shadows {
+ $result: ();
+
+ @each $value in $shadow {
+ @if $value != null {
+ $result: append($result, $value, "comma");
+ }
+ @if $value == none and length($shadow) > 1 {
+ @warn "The keyword 'none' must be used as a single argument.";
+ }
+ }
+
+ @if (length($result) > 0) {
+ box-shadow: $result;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_breakpoints.scss b/node_modules/bootstrap/scss/mixins/_breakpoints.scss
new file mode 100644
index 00000000..cdc8034e
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_breakpoints.scss
@@ -0,0 +1,127 @@
+// Breakpoint viewport sizes and media queries.
+//
+// Breakpoints are defined as a map of (name: minimum width), order from small to large:
+//
+// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
+//
+// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
+
+// Name of the next breakpoint, or null for the last breakpoint.
+//
+// >> breakpoint-next(sm)
+// md
+// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// md
+// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
+// md
+@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
+ $n: index($breakpoint-names, $name);
+ @if not $n {
+ @error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
+ }
+ @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
+}
+
+// Minimum breakpoint width. Null for the smallest (first) breakpoint.
+//
+// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// 576px
+@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
+ $min: map-get($breakpoints, $name);
+ @return if($min != 0, $min, null);
+}
+
+// Maximum breakpoint width.
+// The maximum value is reduced by 0.02px to work around the limitations of
+// `min-` and `max-` prefixes and viewports with fractional widths.
+// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
+// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
+// See https://bugs.webkit.org/show_bug.cgi?id=178261
+//
+// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// 767.98px
+@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+ $max: map-get($breakpoints, $name);
+ @return if($max and $max > 0, $max - .02, null);
+}
+
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
+// Useful for making responsive utilities.
+//
+// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// "" (Returns a blank string)
+// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// "-sm"
+@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
+ @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+}
+
+// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
+// Makes the @content apply to the given breakpoint and wider.
+@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
+ $min: breakpoint-min($name, $breakpoints);
+ @if $min {
+ @media (min-width: $min) {
+ @content;
+ }
+ } @else {
+ @content;
+ }
+}
+
+// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
+// Makes the @content apply to the given breakpoint and narrower.
+@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
+ $max: breakpoint-max($name, $breakpoints);
+ @if $max {
+ @media (max-width: $max) {
+ @content;
+ }
+ } @else {
+ @content;
+ }
+}
+
+// Media that spans multiple breakpoint widths.
+// Makes the @content apply between the min and max breakpoints
+@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
+ $min: breakpoint-min($lower, $breakpoints);
+ $max: breakpoint-max($upper, $breakpoints);
+
+ @if $min != null and $max != null {
+ @media (min-width: $min) and (max-width: $max) {
+ @content;
+ }
+ } @else if $max == null {
+ @include media-breakpoint-up($lower, $breakpoints) {
+ @content;
+ }
+ } @else if $min == null {
+ @include media-breakpoint-down($upper, $breakpoints) {
+ @content;
+ }
+ }
+}
+
+// Media between the breakpoint's minimum and maximum widths.
+// No minimum for the smallest breakpoint, and no maximum for the largest one.
+// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
+@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
+ $min: breakpoint-min($name, $breakpoints);
+ $next: breakpoint-next($name, $breakpoints);
+ $max: breakpoint-max($next);
+
+ @if $min != null and $max != null {
+ @media (min-width: $min) and (max-width: $max) {
+ @content;
+ }
+ } @else if $max == null {
+ @include media-breakpoint-up($name, $breakpoints) {
+ @content;
+ }
+ } @else if $min == null {
+ @include media-breakpoint-down($next, $breakpoints) {
+ @content;
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_buttons.scss b/node_modules/bootstrap/scss/mixins/_buttons.scss
new file mode 100644
index 00000000..c06fa4a9
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_buttons.scss
@@ -0,0 +1,133 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+// scss-docs-start btn-variant-mixin
+@mixin button-variant(
+ $background,
+ $border,
+ $color: color-contrast($background),
+ $hover-background: if($color == $color-contrast-light, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)),
+ $hover-border: if($color == $color-contrast-light, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)),
+ $hover-color: color-contrast($hover-background),
+ $active-background: if($color == $color-contrast-light, shade-color($background,$btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)),
+ $active-border: if($color == $color-contrast-light, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)),
+ $active-color: color-contrast($active-background),
+ $disabled-background: $background,
+ $disabled-border: $border,
+ $disabled-color: color-contrast($disabled-background)
+) {
+ color: $color;
+ @include gradient-bg($background);
+ border-color: $border;
+ @include box-shadow($btn-box-shadow);
+
+ &:hover {
+ color: $hover-color;
+ @include gradient-bg($hover-background);
+ border-color: $hover-border;
+ }
+
+ .btn-check:focus + &,
+ &:focus {
+ color: $hover-color;
+ @include gradient-bg($hover-background);
+ border-color: $hover-border;
+ @if $enable-shadows {
+ @include box-shadow($btn-box-shadow, 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5));
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5);
+ }
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ .show > &.dropdown-toggle {
+ color: $active-color;
+ background-color: $active-background;
+ // Remove CSS gradients if they're enabled
+ background-image: if($enable-gradients, none, null);
+ border-color: $active-border;
+
+ &:focus {
+ @if $enable-shadows {
+ @include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5));
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: 0 0 0 $btn-focus-width rgba(mix($color, $border, 15%), .5);
+ }
+ }
+ }
+
+ &:disabled,
+ &.disabled {
+ color: $disabled-color;
+ background-color: $disabled-background;
+ // Remove CSS gradients if they're enabled
+ background-image: if($enable-gradients, none, null);
+ border-color: $disabled-border;
+ }
+}
+// scss-docs-end btn-variant-mixin
+
+// scss-docs-start btn-outline-variant-mixin
+@mixin button-outline-variant(
+ $color,
+ $color-hover: color-contrast($color),
+ $active-background: $color,
+ $active-border: $color,
+ $active-color: color-contrast($active-background)
+) {
+ color: $color;
+ border-color: $color;
+
+ &:hover {
+ color: $color-hover;
+ background-color: $active-background;
+ border-color: $active-border;
+ }
+
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.dropdown-toggle.show {
+ color: $active-color;
+ background-color: $active-background;
+ border-color: $active-border;
+
+ &:focus {
+ @if $enable-shadows {
+ @include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($color, .5));
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
+ }
+ }
+ }
+
+ &:disabled,
+ &.disabled {
+ color: $color;
+ background-color: transparent;
+ }
+}
+// scss-docs-end btn-outline-variant-mixin
+
+// scss-docs-start btn-size-mixin
+@mixin button-size($padding-y, $padding-x, $font-size, $border-radius) {
+ padding: $padding-y $padding-x;
+ @include font-size($font-size);
+ // Manually declare to provide an override to the browser default
+ @include border-radius($border-radius, 0);
+}
+// scss-docs-end btn-size-mixin
diff --git a/node_modules/bootstrap/scss/mixins/_caret.scss b/node_modules/bootstrap/scss/mixins/_caret.scss
new file mode 100644
index 00000000..4b0f0360
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_caret.scss
@@ -0,0 +1,64 @@
+// scss-docs-start caret-mixins
+@mixin caret-down {
+ border-top: $caret-width solid;
+ border-right: $caret-width solid transparent;
+ border-bottom: 0;
+ border-left: $caret-width solid transparent;
+}
+
+@mixin caret-up {
+ border-top: 0;
+ border-right: $caret-width solid transparent;
+ border-bottom: $caret-width solid;
+ border-left: $caret-width solid transparent;
+}
+
+@mixin caret-end {
+ border-top: $caret-width solid transparent;
+ border-right: 0;
+ border-bottom: $caret-width solid transparent;
+ border-left: $caret-width solid;
+}
+
+@mixin caret-start {
+ border-top: $caret-width solid transparent;
+ border-right: $caret-width solid;
+ border-bottom: $caret-width solid transparent;
+}
+
+@mixin caret($direction: down) {
+ @if $enable-caret {
+ &::after {
+ display: inline-block;
+ margin-left: $caret-spacing;
+ vertical-align: $caret-vertical-align;
+ content: "";
+ @if $direction == down {
+ @include caret-down();
+ } @else if $direction == up {
+ @include caret-up();
+ } @else if $direction == end {
+ @include caret-end();
+ }
+ }
+
+ @if $direction == start {
+ &::after {
+ display: none;
+ }
+
+ &::before {
+ display: inline-block;
+ margin-right: $caret-spacing;
+ vertical-align: $caret-vertical-align;
+ content: "";
+ @include caret-start();
+ }
+ }
+
+ &:empty::after {
+ margin-left: 0;
+ }
+ }
+}
+// scss-docs-end caret-mixins
diff --git a/node_modules/bootstrap/scss/mixins/_clearfix.scss b/node_modules/bootstrap/scss/mixins/_clearfix.scss
new file mode 100644
index 00000000..ffc62bb2
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_clearfix.scss
@@ -0,0 +1,9 @@
+// scss-docs-start clearfix
+@mixin clearfix() {
+ &::after {
+ display: block;
+ clear: both;
+ content: "";
+ }
+}
+// scss-docs-end clearfix
diff --git a/node_modules/bootstrap/scss/mixins/_color-scheme.scss b/node_modules/bootstrap/scss/mixins/_color-scheme.scss
new file mode 100644
index 00000000..90497aa0
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_color-scheme.scss
@@ -0,0 +1,7 @@
+// scss-docs-start mixin-color-scheme
+@mixin color-scheme($name) {
+ @media (prefers-color-scheme: #{$name}) {
+ @content;
+ }
+}
+// scss-docs-end mixin-color-scheme
diff --git a/node_modules/bootstrap/scss/mixins/_container.scss b/node_modules/bootstrap/scss/mixins/_container.scss
new file mode 100644
index 00000000..ee6044d0
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_container.scss
@@ -0,0 +1,9 @@
+// Container mixins
+
+@mixin make-container($gutter: $container-padding-x) {
+ width: 100%;
+ padding-right: var(--#{$variable-prefix}gutter-x, #{$gutter});
+ padding-left: var(--#{$variable-prefix}gutter-x, #{$gutter});
+ margin-right: auto;
+ margin-left: auto;
+}
diff --git a/node_modules/bootstrap/scss/mixins/_deprecate.scss b/node_modules/bootstrap/scss/mixins/_deprecate.scss
new file mode 100644
index 00000000..df070bc5
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_deprecate.scss
@@ -0,0 +1,10 @@
+// Deprecate mixin
+//
+// This mixin can be used to deprecate mixins or functions.
+// `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to
+// some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap)
+@mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) {
+ @if ($enable-deprecation-messages != false and $ignore-warning != true) {
+ @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}.";
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_forms.scss b/node_modules/bootstrap/scss/mixins/_forms.scss
new file mode 100644
index 00000000..dc5bdb0b
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_forms.scss
@@ -0,0 +1,144 @@
+// This mixin uses an `if()` technique to be compatible with Dart Sass
+// See https://github.com/sass/sass/issues/1873#issuecomment-152293725 for more details
+
+// scss-docs-start form-validation-mixins
+@mixin form-validation-state-selector($state) {
+ @if ($state == "valid" or $state == "invalid") {
+ .was-validated #{if(&, "&", "")}:#{$state},
+ #{if(&, "&", "")}.is-#{$state} {
+ @content;
+ }
+ } @else {
+ #{if(&, "&", "")}.is-#{$state} {
+ @content;
+ }
+ }
+}
+
+@mixin form-validation-state(
+ $state,
+ $color,
+ $icon,
+ $tooltip-color: color-contrast($color),
+ $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity),
+ $focus-box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity)
+) {
+ .#{$state}-feedback {
+ display: none;
+ width: 100%;
+ margin-top: $form-feedback-margin-top;
+ @include font-size($form-feedback-font-size);
+ font-style: $form-feedback-font-style;
+ color: $color;
+ }
+
+ .#{$state}-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%; // Contain to parent when possible
+ padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
+ margin-top: .1rem;
+ @include font-size($form-feedback-tooltip-font-size);
+ line-height: $form-feedback-tooltip-line-height;
+ color: $tooltip-color;
+ background-color: $tooltip-bg-color;
+ @include border-radius($form-feedback-tooltip-border-radius);
+ }
+
+ @include form-validation-state-selector($state) {
+ ~ .#{$state}-feedback,
+ ~ .#{$state}-tooltip {
+ display: block;
+ }
+ }
+
+ .form-control {
+ @include form-validation-state-selector($state) {
+ border-color: $color;
+
+ @if $enable-validation-icons {
+ padding-right: $input-height-inner;
+ background-image: escape-svg($icon);
+ background-repeat: no-repeat;
+ background-position: right $input-height-inner-quarter center;
+ background-size: $input-height-inner-half $input-height-inner-half;
+ }
+
+ &:focus {
+ border-color: $color;
+ box-shadow: $focus-box-shadow;
+ }
+ }
+ }
+
+ // stylelint-disable-next-line selector-no-qualifying-type
+ textarea.form-control {
+ @include form-validation-state-selector($state) {
+ @if $enable-validation-icons {
+ padding-right: $input-height-inner;
+ background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
+ }
+ }
+ }
+
+ .form-select {
+ @include form-validation-state-selector($state) {
+ border-color: $color;
+
+ @if $enable-validation-icons {
+ &:not([multiple]):not([size]),
+ &:not([multiple])[size="1"] {
+ padding-right: $form-select-feedback-icon-padding-end;
+ background-image: escape-svg($form-select-indicator), escape-svg($icon);
+ background-position: $form-select-bg-position, $form-select-feedback-icon-position;
+ background-size: $form-select-bg-size, $form-select-feedback-icon-size;
+ }
+ }
+
+ &:focus {
+ border-color: $color;
+ box-shadow: $focus-box-shadow;
+ }
+ }
+ }
+
+ .form-check-input {
+ @include form-validation-state-selector($state) {
+ border-color: $color;
+
+ &:checked {
+ background-color: $color;
+ }
+
+ &:focus {
+ box-shadow: $focus-box-shadow;
+ }
+
+ ~ .form-check-label {
+ color: $color;
+ }
+ }
+ }
+ .form-check-inline .form-check-input {
+ ~ .#{$state}-feedback {
+ margin-left: .5em;
+ }
+ }
+
+ .input-group .form-control,
+ .input-group .form-select {
+ @include form-validation-state-selector($state) {
+ @if $state == "valid" {
+ z-index: 1;
+ } @else if $state == "invalid" {
+ z-index: 2;
+ }
+ &:focus {
+ z-index: 3;
+ }
+ }
+ }
+}
+// scss-docs-end form-validation-mixins
diff --git a/node_modules/bootstrap/scss/mixins/_gradients.scss b/node_modules/bootstrap/scss/mixins/_gradients.scss
new file mode 100644
index 00000000..44167d1b
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_gradients.scss
@@ -0,0 +1,47 @@
+// Gradients
+
+// scss-docs-start gradient-bg-mixin
+@mixin gradient-bg($color: null) {
+ background-color: $color;
+
+ @if $enable-gradients {
+ background-image: var(--#{$variable-prefix}gradient);
+ }
+}
+// scss-docs-end gradient-bg-mixin
+
+// scss-docs-start gradient-mixins
+// Horizontal gradient, from left to right
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
+ background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
+}
+
+// Vertical gradient, from top to bottom
+//
+// Creates two color stops, start and end, by specifying a color and position for each color stop.
+@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: null, $end-percent: null) {
+ background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
+}
+
+@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) {
+ background-image: linear-gradient($deg, $start-color, $end-color);
+}
+
+@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
+ background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color);
+}
+
+@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
+ background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color);
+}
+
+@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) {
+ background-image: radial-gradient(circle, $inner-color, $outer-color);
+}
+
+@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) {
+ background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent);
+}
+// scss-docs-end gradient-mixins
diff --git a/node_modules/bootstrap/scss/mixins/_grid.scss b/node_modules/bootstrap/scss/mixins/_grid.scss
new file mode 100644
index 00000000..d757eac7
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_grid.scss
@@ -0,0 +1,125 @@
+/// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+@mixin make-row($gutter: $grid-gutter-width) {
+ --#{$variable-prefix}gutter-x: #{$gutter};
+ --#{$variable-prefix}gutter-y: 0;
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: calc(var(--#{$variable-prefix}gutter-y) * -1); // stylelint-disable-line function-disallowed-list
+ margin-right: calc(var(--#{$variable-prefix}gutter-x) / -2); // stylelint-disable-line function-disallowed-list
+ margin-left: calc(var(--#{$variable-prefix}gutter-x) / -2); // stylelint-disable-line function-disallowed-list
+}
+
+@mixin make-col-ready($gutter: $grid-gutter-width) {
+ // Add box sizing if only the grid is loaded
+ box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);
+ // Prevent columns from becoming too narrow when at smaller grid tiers by
+ // always setting `width: 100%;`. This works because we set the width
+ // later on to override this initial width.
+ flex-shrink: 0;
+ width: 100%;
+ max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid
+ padding-right: calc(var(--#{$variable-prefix}gutter-x) / 2); // stylelint-disable-line function-disallowed-list
+ padding-left: calc(var(--#{$variable-prefix}gutter-x) / 2); // stylelint-disable-line function-disallowed-list
+ margin-top: var(--#{$variable-prefix}gutter-y);
+}
+
+@mixin make-col($size: false, $columns: $grid-columns) {
+ @if $size {
+ flex: 0 0 auto;
+ width: percentage($size / $columns);
+ } @else {
+ flex: 1 1 0;
+ max-width: 100%;
+ }
+}
+
+@mixin make-col-auto() {
+ flex: 0 0 auto;
+ width: auto;
+}
+
+@mixin make-col-offset($size, $columns: $grid-columns) {
+ $num: $size / $columns;
+ margin-left: if($num == 0, 0, percentage($num));
+}
+
+// Row columns
+//
+// Specify on a parent element(e.g., .row) to force immediate children into NN
+// numberof columns. Supports wrapping to new lines, but does not do a Masonry
+// style grid.
+@mixin row-cols($count) {
+ > * {
+ flex: 0 0 auto;
+ width: 100% / $count;
+ }
+}
+
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `$grid-columns`.
+
+@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
+ @each $breakpoint in map-keys($breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+ @include media-breakpoint-up($breakpoint, $breakpoints) {
+ // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+ .col#{$infix} {
+ flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
+ }
+
+ .row-cols#{$infix}-auto > * {
+ @include make-col-auto();
+ }
+
+ @if $grid-row-columns > 0 {
+ @for $i from 1 through $grid-row-columns {
+ .row-cols#{$infix}-#{$i} {
+ @include row-cols($i);
+ }
+ }
+ }
+
+ .col#{$infix}-auto {
+ @include make-col-auto();
+ }
+
+ @if $columns > 0 {
+ @for $i from 1 through $columns {
+ .col#{$infix}-#{$i} {
+ @include make-col($i, $columns);
+ }
+ }
+
+ // `$columns - 1` because offsetting by the width of an entire row isn't possible
+ @for $i from 0 through ($columns - 1) {
+ @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+ .offset#{$infix}-#{$i} {
+ @include make-col-offset($i, $columns);
+ }
+ }
+ }
+ }
+
+ // Gutters
+ //
+ // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.
+ @each $key, $value in $gutters {
+ .g#{$infix}-#{$key},
+ .gx#{$infix}-#{$key} {
+ --#{$variable-prefix}gutter-x: #{$value};
+ }
+
+ .g#{$infix}-#{$key},
+ .gy#{$infix}-#{$key} {
+ --#{$variable-prefix}gutter-y: #{$value};
+ }
+ }
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_image.scss b/node_modules/bootstrap/scss/mixins/_image.scss
new file mode 100644
index 00000000..e1df779f
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_image.scss
@@ -0,0 +1,16 @@
+// Image Mixins
+// - Responsive image
+// - Retina image
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+@mixin img-fluid {
+ // Part 1: Set a maximum relative to the parent
+ max-width: 100%;
+ // Part 2: Override the height to auto, otherwise images will be stretched
+ // when setting a width and height attribute on the img element.
+ height: auto;
+}
diff --git a/node_modules/bootstrap/scss/mixins/_list-group.scss b/node_modules/bootstrap/scss/mixins/_list-group.scss
new file mode 100644
index 00000000..e55415f2
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_list-group.scss
@@ -0,0 +1,24 @@
+// List Groups
+
+// scss-docs-start list-group-mixin
+@mixin list-group-item-variant($state, $background, $color) {
+ .list-group-item-#{$state} {
+ color: $color;
+ background-color: $background;
+
+ &.list-group-item-action {
+ &:hover,
+ &:focus {
+ color: $color;
+ background-color: shade-color($background, 10%);
+ }
+
+ &.active {
+ color: $white;
+ background-color: $color;
+ border-color: $color;
+ }
+ }
+ }
+}
+// scss-docs-end list-group-mixin
diff --git a/node_modules/bootstrap/scss/mixins/_lists.scss b/node_modules/bootstrap/scss/mixins/_lists.scss
new file mode 100644
index 00000000..25185626
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_lists.scss
@@ -0,0 +1,7 @@
+// Lists
+
+// Unstyled keeps list items block level, just removes default browser padding and list-style
+@mixin list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
diff --git a/node_modules/bootstrap/scss/mixins/_pagination.scss b/node_modules/bootstrap/scss/mixins/_pagination.scss
new file mode 100644
index 00000000..3101b380
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_pagination.scss
@@ -0,0 +1,31 @@
+// Pagination
+
+// scss-docs-start pagination-mixin
+@mixin pagination-size($padding-y, $padding-x, $font-size, $border-radius) {
+ .page-link {
+ padding: $padding-y $padding-x;
+ @include font-size($font-size);
+ }
+
+ .page-item {
+ @if $pagination-margin-start == (-$pagination-border-width) {
+ &:first-child {
+ .page-link {
+ @include border-start-radius($border-radius);
+ }
+ }
+
+ &:last-child {
+ .page-link {
+ @include border-end-radius($border-radius);
+ }
+ }
+ } @else {
+ //Add border-radius to all pageLinks in case they have left margin
+ .page-link {
+ @include border-radius($border-radius);
+ }
+ }
+ }
+}
+// scss-docs-end pagination-mixin
diff --git a/node_modules/bootstrap/scss/mixins/_reset-text.scss b/node_modules/bootstrap/scss/mixins/_reset-text.scss
new file mode 100644
index 00000000..354f5648
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_reset-text.scss
@@ -0,0 +1,17 @@
+@mixin reset-text {
+ font-family: $font-family-base;
+ // We deliberately do NOT reset font-size or overflow-wrap / word-wrap.
+ font-style: normal;
+ font-weight: $font-weight-normal;
+ line-height: $line-height-base;
+ text-align: left; // Fallback for where `start` is not supported
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+}
diff --git a/node_modules/bootstrap/scss/mixins/_resize.scss b/node_modules/bootstrap/scss/mixins/_resize.scss
new file mode 100644
index 00000000..66f233a6
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_resize.scss
@@ -0,0 +1,6 @@
+// Resize anything
+
+@mixin resizable($direction) {
+ overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
+ resize: $direction; // Options: horizontal, vertical, both
+}
diff --git a/node_modules/bootstrap/scss/mixins/_table-variants.scss b/node_modules/bootstrap/scss/mixins/_table-variants.scss
new file mode 100644
index 00000000..9fd0fb02
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_table-variants.scss
@@ -0,0 +1,21 @@
+// scss-docs-start table-variant
+@mixin table-variant($state, $background) {
+ .table-#{$state} {
+ $color: color-contrast(opaque($body-bg, $background));
+ $hover-bg: mix($color, $background, percentage($table-hover-bg-factor));
+ $striped-bg: mix($color, $background, percentage($table-striped-bg-factor));
+ $active-bg: mix($color, $background, percentage($table-active-bg-factor));
+
+ --#{$variable-prefix}table-bg: #{$background};
+ --#{$variable-prefix}table-striped-bg: #{$striped-bg};
+ --#{$variable-prefix}table-striped-color: #{color-contrast($striped-bg)};
+ --#{$variable-prefix}table-active-bg: #{$active-bg};
+ --#{$variable-prefix}table-active-color: #{color-contrast($active-bg)};
+ --#{$variable-prefix}table-hover-bg: #{$hover-bg};
+ --#{$variable-prefix}table-hover-color: #{color-contrast($hover-bg)};
+
+ color: $color;
+ border-color: mix($color, $background, percentage($table-border-factor));
+ }
+}
+// scss-docs-end table-variant
diff --git a/node_modules/bootstrap/scss/mixins/_text-truncate.scss b/node_modules/bootstrap/scss/mixins/_text-truncate.scss
new file mode 100644
index 00000000..3504bb1a
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_text-truncate.scss
@@ -0,0 +1,8 @@
+// Text truncate
+// Requires inline-block or block for proper styling
+
+@mixin text-truncate() {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
diff --git a/node_modules/bootstrap/scss/mixins/_transition.scss b/node_modules/bootstrap/scss/mixins/_transition.scss
new file mode 100644
index 00000000..d437f6d8
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_transition.scss
@@ -0,0 +1,26 @@
+// stylelint-disable property-disallowed-list
+@mixin transition($transition...) {
+ @if length($transition) == 0 {
+ $transition: $transition-base;
+ }
+
+ @if length($transition) > 1 {
+ @each $value in $transition {
+ @if $value == null or $value == none {
+ @warn "The keyword 'none' or 'null' must be used as a single argument.";
+ }
+ }
+ }
+
+ @if $enable-transitions {
+ @if nth($transition, 1) != null {
+ transition: $transition;
+ }
+
+ @if $enable-reduced-motion and nth($transition, 1) != null and nth($transition, 1) != none {
+ @media (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_utilities.scss b/node_modules/bootstrap/scss/mixins/_utilities.scss
new file mode 100644
index 00000000..4d2370a0
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_utilities.scss
@@ -0,0 +1,68 @@
+// Utility generator
+// Used to generate utilities & print utilities
+@mixin generate-utility($utility, $infix, $is-rfs-media-query: false) {
+ $values: map-get($utility, values);
+
+ // If the values are a list or string, convert it into a map
+ @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" {
+ $values: zip($values, $values);
+ }
+
+ @each $key, $value in $values {
+ $properties: map-get($utility, property);
+
+ // Multiple properties are possible, for example with vertical or horizontal margins or paddings
+ @if type-of($properties) == "string" {
+ $properties: append((), $properties);
+ }
+
+ // Use custom class if present
+ $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));
+ $property-class: if($property-class == null, "", $property-class);
+
+ // State params to generate pseudo-classes
+ $state: if(map-has-key($utility, state), map-get($utility, state), ());
+
+ $infix: if($property-class == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix);
+
+ // Don't prefix if value key is null (eg. with shadow class)
+ $property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, "");
+
+ @if map-get($utility, rfs) {
+ // Inside the media query
+ @if $is-rfs-media-query {
+ $val: rfs-value($value);
+
+ // Do not render anything if fluid and non fluid values are the same
+ $value: if($val == rfs-fluid-value($value), null, $val);
+ }
+ @else {
+ $value: rfs-fluid-value($value);
+ }
+ }
+
+ $is-rtl: map-get($utility, rtl);
+
+ @if $value != null {
+ @if $is-rtl == false {
+ /* rtl:begin:remove */
+ }
+ .#{$property-class + $infix + $property-class-modifier} {
+ @each $property in $properties {
+ #{$property}: $value if($enable-important-utilities, !important, null);
+ }
+ }
+
+ @each $pseudo in $state {
+ .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
+ @each $property in $properties {
+ #{$property}: $value if($enable-important-utilities, !important, null);
+ }
+ }
+ }
+ @if $is-rtl == false {
+ /* rtl:end:remove */
+ }
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/mixins/_visually-hidden.scss b/node_modules/bootstrap/scss/mixins/_visually-hidden.scss
new file mode 100644
index 00000000..ed7bc9c1
--- /dev/null
+++ b/node_modules/bootstrap/scss/mixins/_visually-hidden.scss
@@ -0,0 +1,29 @@
+// stylelint-disable declaration-no-important
+
+// Hide content visually while keeping it accessible to assistive technologies
+//
+// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/
+// See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
+
+@mixin visually-hidden() {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important; // Fix for https://github.com/twbs/bootstrap/issues/25686
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+
+// Use to only display content when it's focused, or one of its child elements is focused
+// (i.e. when focus is within the element/container that the class was applied to)
+//
+// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+
+@mixin visually-hidden-focusable() {
+ &:not(:focus):not(:focus-within) {
+ @include visually-hidden();
+ }
+}
diff --git a/node_modules/bootstrap/scss/utilities/_api.scss b/node_modules/bootstrap/scss/utilities/_api.scss
new file mode 100644
index 00000000..62e1d398
--- /dev/null
+++ b/node_modules/bootstrap/scss/utilities/_api.scss
@@ -0,0 +1,47 @@
+// Loop over each breakpoint
+@each $breakpoint in map-keys($grid-breakpoints) {
+
+ // Generate media query if needed
+ @include media-breakpoint-up($breakpoint) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ // Loop over each utility property
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Only proceed if responsive media queries are enabled or if it's the base media query
+ @if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
+ @include generate-utility($utility, $infix);
+ }
+ }
+ }
+}
+
+// RFS rescaling
+@media (min-width: $rfs-mq-value) {
+ @each $breakpoint in map-keys($grid-breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {
+ // Loop over each utility property
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Only proceed if responsive media queries are enabled or if it's the base media query
+ @if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") {
+ @include generate-utility($utility, $infix, true);
+ }
+ }
+ }
+ }
+}
+
+
+// Print utilities
+@media print {
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Then check if the utility needs print styles
+ @if type-of($utility) == "map" and map-get($utility, print) == true {
+ @include generate-utility($utility, "-print");
+ }
+ }
+}
diff --git a/node_modules/bootstrap/scss/vendor/_rfs.scss b/node_modules/bootstrap/scss/vendor/_rfs.scss
new file mode 100644
index 00000000..798753ec
--- /dev/null
+++ b/node_modules/bootstrap/scss/vendor/_rfs.scss
@@ -0,0 +1,312 @@
+// stylelint-disable property-blacklist, scss/dollar-variable-default
+
+// SCSS RFS mixin
+//
+// Automated responsive values for font sizes, paddings, margins and much more
+//
+// Licensed under MIT (https://github.com/twbs/rfs/blob/master/LICENSE)
+
+// Configuration
+
+// Base value
+$rfs-base-value: 1.25rem !default;
+$rfs-unit: rem !default;
+
+@if $rfs-unit != rem and $rfs-unit != px {
+ @error "`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.";
+}
+
+// Breakpoint at where values start decreasing if screen width is smaller
+$rfs-breakpoint: 1200px !default;
+$rfs-breakpoint-unit: px !default;
+
+@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {
+ @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.";
+}
+
+// Resize values based on screen height and width
+$rfs-two-dimensional: false !default;
+
+// Factor of decrease
+$rfs-factor: 10 !default;
+
+@if type-of($rfs-factor) != number or $rfs-factor <= 1 {
+ @error "`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.";
+}
+
+// Mode. Possibilities: "min-media-query", "max-media-query"
+$rfs-mode: min-media-query !default;
+
+// Generate enable or disable classes. Possibilities: false, "enable" or "disable"
+$rfs-class: false !default;
+
+// 1 rem = $rfs-rem-value px
+$rfs-rem-value: 16 !default;
+
+// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14
+$rfs-safari-iframe-resize-bug-fix: false !default;
+
+// Disable RFS by setting $enable-rfs to false
+$enable-rfs: true !default;
+
+// Cache $rfs-base-value unit
+$rfs-base-value-unit: unit($rfs-base-value);
+
+// Remove px-unit from $rfs-base-value for calculations
+@if $rfs-base-value-unit == px {
+ $rfs-base-value: $rfs-base-value / ($rfs-base-value * 0 + 1);
+}
+@else if $rfs-base-value-unit == rem {
+ $rfs-base-value: $rfs-base-value / ($rfs-base-value * 0 + 1 / $rfs-rem-value);
+}
+
+// Cache $rfs-breakpoint unit to prevent multiple calls
+$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);
+
+// Remove unit from $rfs-breakpoint for calculations
+@if $rfs-breakpoint-unit-cache == px {
+ $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);
+}
+@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == "em" {
+ $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);
+}
+
+// Calculate the media query value
+$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit});
+$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);
+$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height);
+
+// Internal mixin used to determine which media query needs to be used
+@mixin _rfs-media-query {
+ @if $rfs-two-dimensional {
+ @if $rfs-mode == max-media-query {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
+ @content;
+ }
+ }
+ @else {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
+ @content;
+ }
+ }
+ }
+ @else {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {
+ @content;
+ }
+ }
+}
+
+// Internal mixin that adds disable classes to the selector if needed.
+@mixin _rfs-rule {
+ @if $rfs-class == disable and $rfs-mode == max-media-query {
+ // Adding an extra class increases specificity, which prevents the media query to override the property
+ &,
+ .disable-rfs &,
+ &.disable-rfs {
+ @content;
+ }
+ }
+ @else if $rfs-class == enable and $rfs-mode == min-media-query {
+ .enable-rfs &,
+ &.enable-rfs {
+ @content;
+ }
+ }
+ @else {
+ @content;
+ }
+}
+
+// Internal mixin that adds enable classes to the selector if needed.
+@mixin _rfs-media-query-rule {
+
+ @if $rfs-class == enable {
+ @if $rfs-mode == min-media-query {
+ @content;
+ }
+
+ @include _rfs-media-query {
+ .enable-rfs &,
+ &.enable-rfs {
+ @content;
+ }
+ }
+ }
+ @else {
+ @if $rfs-class == disable and $rfs-mode == min-media-query {
+ .disable-rfs &,
+ &.disable-rfs {
+ @content;
+ }
+ }
+ @include _rfs-media-query {
+ @content;
+ }
+ }
+}
+
+// Helper function to get the formatted non-responsive value
+@function rfs-value($values) {
+ // Convert to list
+ $values: if(type-of($values) != list, ($values,), $values);
+
+ $val: '';
+
+ // Loop over each value and calculate value
+ @each $value in $values {
+ @if $value == 0 {
+ $val: $val + ' 0';
+ }
+ @else {
+ // Cache $value unit
+ $unit: if(type-of($value) == "number", unit($value), false);
+
+ @if $unit == px {
+ // Convert to rem if needed
+ $val: $val + ' ' + if($rfs-unit == rem, #{$value / ($value * 0 + $rfs-rem-value)}rem, $value);
+ }
+ @else if $unit == rem {
+ // Convert to px if needed
+ $val: $val + ' ' + if($rfs-unit == px, #{$value / ($value * 0 + 1) * $rfs-rem-value}px, $value);
+ }
+ @else {
+ // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
+ $val: $val + ' ' + $value;
+ }
+ }
+ }
+
+ // Remove first space
+ @return unquote(str-slice($val, 2));
+}
+
+// Helper function to get the responsive value calculated by RFS
+@function rfs-fluid-value($values) {
+ // Convert to list
+ $values: if(type-of($values) != list, ($values,), $values);
+
+ $val: '';
+
+ // Loop over each value and calculate value
+ @each $value in $values {
+ @if $value == 0 {
+ $val: $val + ' 0';
+ }
+
+ @else {
+ // Cache $value unit
+ $unit: if(type-of($value) == "number", unit($value), false);
+
+ // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
+ @if not $unit or $unit != px and $unit != rem {
+ $val: $val + ' ' + $value;
+ }
+
+ @else {
+ // Remove unit from $value for calculations
+ $value: $value / ($value * 0 + if($unit == px, 1, 1 / $rfs-rem-value));
+
+ // Only add the media query if the value is greater than the minimum value
+ @if abs($value) <= $rfs-base-value or not $enable-rfs {
+ $val: $val + ' ' + if($rfs-unit == rem, #{$value / $rfs-rem-value}rem, #{$value}px);
+ }
+ @else {
+ // Calculate the minimum value
+ $value-min: $rfs-base-value + (abs($value) - $rfs-base-value) / $rfs-factor;
+
+ // Calculate difference between $value and the minimum value
+ $value-diff: abs($value) - $value-min;
+
+ // Base value formatting
+ $min-width: if($rfs-unit == rem, #{$value-min / $rfs-rem-value}rem, #{$value-min}px);
+
+ // Use negative value if needed
+ $min-width: if($value < 0, -$min-width, $min-width);
+
+ // Use `vmin` if two-dimensional is enabled
+ $variable-unit: if($rfs-two-dimensional, vmin, vw);
+
+ // Calculate the variable width between 0 and $rfs-breakpoint
+ $variable-width: #{$value-diff * 100 / $rfs-breakpoint}#{$variable-unit};
+
+ // Return the calculated value
+ $val: $val + ' calc(' + $min-width + if($value < 0, ' - ', ' + ') + $variable-width + ')';
+ }
+ }
+ }
+ }
+
+ // Remove first space
+ @return unquote(str-slice($val, 2));
+}
+
+// RFS mixin
+@mixin rfs($values, $property: font-size) {
+ @if $values != null {
+ $val: rfs-value($values);
+ $fluidVal: rfs-fluid-value($values);
+
+ // Do not print the media query if responsive & non-responsive values are the same
+ @if $val == $fluidVal {
+ #{$property}: $val;
+ }
+ @else {
+ @include _rfs-rule {
+ #{$property}: if($rfs-mode == max-media-query, $val, $fluidVal);
+
+ // Include safari iframe resize fix if needed
+ min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);
+ }
+
+ @include _rfs-media-query-rule {
+ #{$property}: if($rfs-mode == max-media-query, $fluidVal, $val);
+ }
+ }
+ }
+}
+
+// Shorthand helper mixins
+@mixin font-size($value) {
+ @include rfs($value);
+}
+
+@mixin padding($value) {
+ @include rfs($value, padding);
+}
+
+@mixin padding-top($value) {
+ @include rfs($value, padding-top);
+}
+
+@mixin padding-right($value) {
+ @include rfs($value, padding-right);
+}
+
+@mixin padding-bottom($value) {
+ @include rfs($value, padding-bottom);
+}
+
+@mixin padding-left($value) {
+ @include rfs($value, padding-left);
+}
+
+@mixin margin($value) {
+ @include rfs($value, margin);
+}
+
+@mixin margin-top($value) {
+ @include rfs($value, margin-top);
+}
+
+@mixin margin-right($value) {
+ @include rfs($value, margin-right);
+}
+
+@mixin margin-bottom($value) {
+ @include rfs($value, margin-bottom);
+}
+
+@mixin margin-left($value) {
+ @include rfs($value, margin-left);
+}
diff --git a/node_modules/braces/CHANGELOG.md b/node_modules/braces/CHANGELOG.md
new file mode 100644
index 00000000..36f798b0
--- /dev/null
+++ b/node_modules/braces/CHANGELOG.md
@@ -0,0 +1,184 @@
+# Release history
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+
+ Guiding Principles
+
+- Changelogs are for humans, not machines.
+- There should be an entry for every single version.
+- The same types of changes should be grouped.
+- Versions and sections should be linkable.
+- The latest version comes first.
+- The release date of each versions is displayed.
+- Mention whether you follow Semantic Versioning.
+
+
+
+
+ Types of changes
+
+Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
+
+- `Added` for new features.
+- `Changed` for changes in existing functionality.
+- `Deprecated` for soon-to-be removed features.
+- `Removed` for now removed features.
+- `Fixed` for any bug fixes.
+- `Security` in case of vulnerabilities.
+
+
+
+## [3.0.0] - 2018-04-08
+
+v3.0 is a complete refactor, resulting in a faster, smaller codebase, with fewer deps, and a more accurate parser and compiler.
+
+**Breaking Changes**
+
+- The undocumented `.makeRe` method was removed
+
+**Non-breaking changes**
+
+- Caching was removed
+
+## [2.3.2] - 2018-04-08
+
+- start refactoring
+- cover sets
+- better range handling
+
+## [2.3.1] - 2018-02-17
+
+- Remove unnecessary escape in Regex. (#14)
+
+## [2.3.0] - 2017-10-19
+
+- minor code reorganization
+- optimize regex
+- expose `maxLength` option
+
+## [2.2.1] - 2017-05-30
+
+- don't condense when braces contain extglobs
+
+## [2.2.0] - 2017-05-28
+
+- ensure word boundaries are preserved
+- fixes edge case where extglob characters precede a brace pattern
+
+## [2.1.1] - 2017-04-27
+
+- use snapdragon-node
+- handle edge case
+- optimizations, lint
+
+## [2.0.4] - 2017-04-11
+
+- pass opts to compiler
+- minor optimization in create method
+- re-write parser handlers to remove negation regex
+
+## [2.0.3] - 2016-12-10
+
+- use split-string
+- clear queue at the end
+- adds sequences example
+- add unit tests
+
+## [2.0.2] - 2016-10-21
+
+- fix comma handling in nested extglobs
+
+## [2.0.1] - 2016-10-20
+
+- add comments
+- more tests, ensure quotes are stripped
+
+## [2.0.0] - 2016-10-19
+
+- don't expand braces inside character classes
+- add quantifier pattern
+
+## [1.8.5] - 2016-05-21
+
+- Refactor (#10)
+
+## [1.8.4] - 2016-04-20
+
+- fixes https://github.com/jonschlinkert/micromatch/issues/66
+
+## [1.8.0] - 2015-03-18
+
+- adds exponent examples, tests
+- fixes the first example in https://github.com/jonschlinkert/micromatch/issues/38
+
+## [1.6.0] - 2015-01-30
+
+- optimizations, `bash` mode:
+- improve path escaping
+
+## [1.5.0] - 2015-01-28
+
+- Merge pull request #5 from eush77/lib-files
+
+## [1.4.0] - 2015-01-24
+
+- add extglob tests
+- externalize exponent function
+- better whitespace handling
+
+## [1.3.0] - 2015-01-24
+
+- make regex patterns explicity
+
+## [1.1.0] - 2015-01-11
+
+- don't create a match group with `makeRe`
+
+## [1.0.0] - 2014-12-23
+
+- Merge commit '97b05f5544f8348736a8efaecf5c32bbe3e2ad6e'
+- support empty brace syntax
+- better bash coverage
+- better support for regex strings
+
+## [0.1.4] - 2014-11-14
+
+- improve recognition of bad args, recognize mismatched argument types
+- support escaping
+- remove pathname-expansion
+- support whitespace in patterns
+
+## [0.1.0]
+
+- first commit
+
+[2.3.2]: https://github.com/micromatch/braces/compare/2.3.1...2.3.2
+[2.3.1]: https://github.com/micromatch/braces/compare/2.3.0...2.3.1
+[2.3.0]: https://github.com/micromatch/braces/compare/2.2.1...2.3.0
+[2.2.1]: https://github.com/micromatch/braces/compare/2.2.0...2.2.1
+[2.2.0]: https://github.com/micromatch/braces/compare/2.1.1...2.2.0
+[2.1.1]: https://github.com/micromatch/braces/compare/2.1.0...2.1.1
+[2.1.0]: https://github.com/micromatch/braces/compare/2.0.4...2.1.0
+[2.0.4]: https://github.com/micromatch/braces/compare/2.0.3...2.0.4
+[2.0.3]: https://github.com/micromatch/braces/compare/2.0.2...2.0.3
+[2.0.2]: https://github.com/micromatch/braces/compare/2.0.1...2.0.2
+[2.0.1]: https://github.com/micromatch/braces/compare/2.0.0...2.0.1
+[2.0.0]: https://github.com/micromatch/braces/compare/1.8.5...2.0.0
+[1.8.5]: https://github.com/micromatch/braces/compare/1.8.4...1.8.5
+[1.8.4]: https://github.com/micromatch/braces/compare/1.8.0...1.8.4
+[1.8.0]: https://github.com/micromatch/braces/compare/1.6.0...1.8.0
+[1.6.0]: https://github.com/micromatch/braces/compare/1.5.0...1.6.0
+[1.5.0]: https://github.com/micromatch/braces/compare/1.4.0...1.5.0
+[1.4.0]: https://github.com/micromatch/braces/compare/1.3.0...1.4.0
+[1.3.0]: https://github.com/micromatch/braces/compare/1.2.0...1.3.0
+[1.2.0]: https://github.com/micromatch/braces/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/micromatch/braces/compare/1.0.0...1.1.0
+[1.0.0]: https://github.com/micromatch/braces/compare/0.1.4...1.0.0
+[0.1.4]: https://github.com/micromatch/braces/compare/0.1.0...0.1.4
+
+[Unreleased]: https://github.com/micromatch/braces/compare/0.1.0...HEAD
+[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog
\ No newline at end of file
diff --git a/node_modules/braces/LICENSE b/node_modules/braces/LICENSE
new file mode 100644
index 00000000..d32ab442
--- /dev/null
+++ b/node_modules/braces/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2018, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/braces/README.md b/node_modules/braces/README.md
new file mode 100644
index 00000000..cba2f600
--- /dev/null
+++ b/node_modules/braces/README.md
@@ -0,0 +1,593 @@
+# braces [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [](https://www.npmjs.com/package/braces) [](https://npmjs.org/package/braces) [](https://npmjs.org/package/braces) [](https://travis-ci.org/micromatch/braces)
+
+> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save braces
+```
+
+## v3.0.0 Released!!
+
+See the [changelog](CHANGELOG.md) for details.
+
+## Why use braces?
+
+Brace patterns make globs more powerful by adding the ability to match specific ranges and sequences of characters.
+
+* **Accurate** - complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
+* **[fast and performant](#benchmarks)** - Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
+* **Organized code base** - The parser and compiler are easy to maintain and update when edge cases crop up.
+* **Well-tested** - Thousands of test assertions, and passes all of the Bash, minimatch, and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests (as of the date this was written).
+* **Safer** - You shouldn't have to worry about users defining aggressive or malicious brace patterns that can break your application. Braces takes measures to prevent malicious regex that can be used for DDoS attacks (see [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)).
+* [Supports lists](#lists) - (aka "sets") `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
+* [Supports sequences](#sequences) - (aka "ranges") `{01..03}` => `['01', '02', '03']`
+* [Supports steps](#steps) - (aka "increments") `{2..10..2}` => `['2', '4', '6', '8', '10']`
+* [Supports escaping](#escaping) - To prevent evaluation of special characters.
+
+## Usage
+
+The main export is a function that takes one or more brace `patterns` and `options`.
+
+```js
+const braces = require('braces');
+// braces(patterns[, options]);
+
+console.log(braces(['{01..05}', '{a..e}']));
+//=> ['(0[1-5])', '([a-e])']
+
+console.log(braces(['{01..05}', '{a..e}'], { expand: true }));
+//=> ['01', '02', '03', '04', '05', 'a', 'b', 'c', 'd', 'e']
+```
+
+### Brace Expansion vs. Compilation
+
+By default, brace patterns are compiled into strings that are optimized for creating regular expressions and matching.
+
+**Compiled**
+
+```js
+console.log(braces('a/{x,y,z}/b'));
+//=> ['a/(x|y|z)/b']
+console.log(braces(['a/{01..20}/b', 'a/{1..5}/b']));
+//=> [ 'a/(0[1-9]|1[0-9]|20)/b', 'a/([1-5])/b' ]
+```
+
+**Expanded**
+
+Enable brace expansion by setting the `expand` option to true, or by using [braces.expand()](#expand) (returns an array similar to what you'd expect from Bash, or `echo {1..5}`, or [minimatch](https://github.com/isaacs/minimatch)):
+
+```js
+console.log(braces('a/{x,y,z}/b', { expand: true }));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+
+console.log(braces.expand('{01..10}'));
+//=> ['01','02','03','04','05','06','07','08','09','10']
+```
+
+### Lists
+
+Expand lists (like Bash "sets"):
+
+```js
+console.log(braces('a/{foo,bar,baz}/*.js'));
+//=> ['a/(foo|bar|baz)/*.js']
+
+console.log(braces.expand('a/{foo,bar,baz}/*.js'));
+//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
+```
+
+### Sequences
+
+Expand ranges of characters (like Bash "sequences"):
+
+```js
+console.log(braces.expand('{1..3}')); // ['1', '2', '3']
+console.log(braces.expand('a/{1..3}/b')); // ['a/1/b', 'a/2/b', 'a/3/b']
+console.log(braces('{a..c}', { expand: true })); // ['a', 'b', 'c']
+console.log(braces('foo/{a..c}', { expand: true })); // ['foo/a', 'foo/b', 'foo/c']
+
+// supports zero-padded ranges
+console.log(braces('a/{01..03}/b')); //=> ['a/(0[1-3])/b']
+console.log(braces('a/{001..300}/b')); //=> ['a/(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)/b']
+```
+
+See [fill-range](https://github.com/jonschlinkert/fill-range) for all available range-expansion options.
+
+### Steppped ranges
+
+Steps, or increments, may be used with ranges:
+
+```js
+console.log(braces.expand('{2..10..2}'));
+//=> ['2', '4', '6', '8', '10']
+
+console.log(braces('{2..10..2}'));
+//=> ['(2|4|6|8|10)']
+```
+
+When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
+
+### Nesting
+
+Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
+
+**"Expanded" braces**
+
+```js
+console.log(braces.expand('a{b,c,/{x,y}}/e'));
+//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
+
+console.log(braces.expand('a/{x,{1..5},y}/c'));
+//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
+```
+
+**"Optimized" braces**
+
+```js
+console.log(braces('a{b,c,/{x,y}}/e'));
+//=> ['a(b|c|/(x|y))/e']
+
+console.log(braces('a/{x,{1..5},y}/c'));
+//=> ['a/(x|([1-5])|y)/c']
+```
+
+### Escaping
+
+**Escaping braces**
+
+A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
+
+```js
+console.log(braces.expand('a\\{d,c,b}e'));
+//=> ['a{d,c,b}e']
+
+console.log(braces.expand('a{d,c,b\\}e'));
+//=> ['a{d,c,b}e']
+```
+
+**Escaping commas**
+
+Commas inside braces may also be escaped:
+
+```js
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+
+console.log(braces.expand('a{d\\,c,b}e'));
+//=> ['ad,ce', 'abe']
+```
+
+**Single items**
+
+Following bash conventions, a brace pattern is also not expanded when it contains a single character:
+
+```js
+console.log(braces.expand('a{b}c'));
+//=> ['a{b}c']
+```
+
+## Options
+
+### options.maxLength
+
+**Type**: `Number`
+
+**Default**: `65,536`
+
+**Description**: Limit the length of the input string. Useful when the input string is generated or your application allows users to pass a string, et cetera.
+
+```js
+console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
+```
+
+### options.expand
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Generate an "expanded" brace pattern (alternatively you can use the `braces.expand()` method, which does the same thing).
+
+```js
+console.log(braces('a/{b,c}/d', { expand: true }));
+//=> [ 'a/b/d', 'a/c/d' ]
+```
+
+### options.nodupes
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Remove duplicates from the returned array.
+
+### options.rangeLimit
+
+**Type**: `Number`
+
+**Default**: `1000`
+
+**Description**: To prevent malicious patterns from being passed by users, an error is thrown when `braces.expand()` is used or `options.expand` is true and the generated range will exceed the `rangeLimit`.
+
+You can customize `options.rangeLimit` or set it to `Inifinity` to disable this altogether.
+
+**Examples**
+
+```js
+// pattern exceeds the "rangeLimit", so it's optimized automatically
+console.log(braces.expand('{1..1000}'));
+//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
+
+// pattern does not exceed "rangeLimit", so it's NOT optimized
+console.log(braces.expand('{1..100}'));
+//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
+```
+
+### options.transform
+
+**Type**: `Function`
+
+**Default**: `undefined`
+
+**Description**: Customize range expansion.
+
+**Example: Transforming non-numeric values**
+
+```js
+const alpha = braces.expand('x/{a..e}/y', {
+ transform(value, index) {
+ // When non-numeric values are passed, "value" is a character code.
+ return 'foo/' + String.fromCharCode(value) + '-' + index;
+ }
+});
+console.log(alpha);
+//=> [ 'x/foo/a-0/y', 'x/foo/b-1/y', 'x/foo/c-2/y', 'x/foo/d-3/y', 'x/foo/e-4/y' ]
+```
+
+**Example: Transforming numeric values**
+
+```js
+const numeric = braces.expand('{1..5}', {
+ transform(value) {
+ // when numeric values are passed, "value" is a number
+ return 'foo/' + value * 2;
+ }
+});
+console.log(numeric);
+//=> [ 'foo/2', 'foo/4', 'foo/6', 'foo/8', 'foo/10' ]
+```
+
+### options.quantifiers
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+
+Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
+
+The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
+
+**Examples**
+
+```js
+const braces = require('braces');
+console.log(braces('a/b{1,3}/{x,y,z}'));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
+```
+
+### options.unescape
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Strip backslashes that were used for escaping from the result.
+
+## What is "brace expansion"?
+
+Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
+
+In addition to "expansion", braces are also used for matching. In other words:
+
+* [brace expansion](#brace-expansion) is for generating new lists
+* [brace matching](#brace-matching) is for filtering existing lists
+
+
+More about brace expansion (click to expand)
+
+There are two main types of brace expansion:
+
+1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
+2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
+
+Here are some example brace patterns to illustrate how they work:
+
+**Sets**
+
+```
+{a,b,c} => a b c
+{a,b,c}{1,2} => a1 a2 b1 b2 c1 c2
+```
+
+**Sequences**
+
+```
+{1..9} => 1 2 3 4 5 6 7 8 9
+{4..-4} => 4 3 2 1 0 -1 -2 -3 -4
+{1..20..3} => 1 4 7 10 13 16 19
+{a..j} => a b c d e f g h i j
+{j..a} => j i h g f e d c b a
+{a..z..3} => a d g j m p s v y
+```
+
+**Combination**
+
+Sets and sequences can be mixed together or used along with any other strings.
+
+```
+{a,b,c}{1..3} => a1 a2 a3 b1 b2 b3 c1 c2 c3
+foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
+```
+
+The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
+
+## Brace matching
+
+In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
+
+For example, the pattern `foo/{1..3}/bar` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+```
+
+But not:
+
+```
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+## Brace matching pitfalls
+
+Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.
+
+### tldr
+
+**"brace bombs"**
+
+* brace expansion can eat up a huge amount of processing resources
+* as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
+* users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
+
+For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
+
+### The solution
+
+Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
+
+### Geometric complexity
+
+At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
+
+For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
+
+```
+{1,2}{3,4} => (2X2) => 13 14 23 24
+{1,2}{3,4}{5,6} => (2X2X2) => 135 136 145 146 235 236 245 246
+```
+
+But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
+
+```
+{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
+ 249 257 258 259 267 268 269 347 348 349 357
+ 358 359 367 368 369
+```
+
+Now, imagine how this complexity grows given that each element is a n-tuple:
+
+```
+{1..100}{1..100} => (100X100) => 10,000 elements (38.4 kB)
+{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
+```
+
+Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.
+
+**More information**
+
+Interested in learning more about brace expansion?
+
+* [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
+* [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
+* [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
+
+
+
+## Performance
+
+Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.
+
+### Better algorithms
+
+Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.
+
+Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.
+
+**The proof is in the numbers**
+
+Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
+
+| **Pattern** | **braces** | **[minimatch][]** |
+| --- | --- | --- |
+| `{1..9007199254740991}`[^1] | `298 B` (5ms 459μs)| N/A (freezes) |
+| `{1..1000000000000000}` | `41 B` (1ms 15μs) | N/A (freezes) |
+| `{1..100000000000000}` | `40 B` (890μs) | N/A (freezes) |
+| `{1..10000000000000}` | `39 B` (2ms 49μs) | N/A (freezes) |
+| `{1..1000000000000}` | `38 B` (608μs) | N/A (freezes) |
+| `{1..100000000000}` | `37 B` (397μs) | N/A (freezes) |
+| `{1..10000000000}` | `35 B` (983μs) | N/A (freezes) |
+| `{1..1000000000}` | `34 B` (798μs) | N/A (freezes) |
+| `{1..100000000}` | `33 B` (733μs) | N/A (freezes) |
+| `{1..10000000}` | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
+| `{1..1000000}` | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs) |
+| `{1..100000}` | `30 B` (950μs) | `588.89 kB` (146ms 921μs) |
+| `{1..10000}` | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs) |
+| `{1..1000}` | `28 B` (760μs) | `3.89 kB` (1ms 453μs) |
+| `{1..100}` | `22 B` (345μs) | `291 B` (196μs) |
+| `{1..10}` | `10 B` (533μs) | `20 B` (37μs) |
+| `{1..3}` | `7 B` (190μs) | `5 B` (27μs) |
+
+### Faster algorithms
+
+When you need expansion, braces is still much faster.
+
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
+
+| **Pattern** | **braces** | **[minimatch][]** |
+| --- | --- | --- |
+| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
+| `{1..1000000}` | `6.89 MB` (458ms 576μs) | `6.89 MB` (1s 491ms 621μs) |
+| `{1..100000}` | `588.89 kB` (20ms 728μs) | `588.89 kB` (156ms 919μs) |
+| `{1..10000}` | `48.89 kB` (2ms 202μs) | `48.89 kB` (13ms 641μs) |
+| `{1..1000}` | `3.89 kB` (1ms 796μs) | `3.89 kB` (1ms 958μs) |
+| `{1..100}` | `291 B` (424μs) | `291 B` (211μs) |
+| `{1..10}` | `20 B` (487μs) | `20 B` (72μs) |
+| `{1..3}` | `5 B` (166μs) | `5 B` (27μs) |
+
+If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
+
+## Benchmarks
+
+### Running benchmarks
+
+Install dev dependencies:
+
+```bash
+npm i -d && npm benchmark
+```
+
+### Latest results
+
+Braces is more accurate, without sacrificing performance.
+
+```bash
+# range (expanded)
+ braces x 29,040 ops/sec ±3.69% (91 runs sampled))
+ minimatch x 4,735 ops/sec ±1.28% (90 runs sampled)
+
+# range (optimized for regex)
+ braces x 382,878 ops/sec ±0.56% (94 runs sampled)
+ minimatch x 1,040 ops/sec ±0.44% (93 runs sampled)
+
+# nested ranges (expanded)
+ braces x 19,744 ops/sec ±2.27% (92 runs sampled))
+ minimatch x 4,579 ops/sec ±0.50% (93 runs sampled)
+
+# nested ranges (optimized for regex)
+ braces x 246,019 ops/sec ±2.02% (93 runs sampled)
+ minimatch x 1,028 ops/sec ±0.39% (94 runs sampled)
+
+# set (expanded)
+ braces x 138,641 ops/sec ±0.53% (95 runs sampled)
+ minimatch x 219,582 ops/sec ±0.98% (94 runs sampled)
+
+# set (optimized for regex)
+ braces x 388,408 ops/sec ±0.41% (95 runs sampled)
+ minimatch x 44,724 ops/sec ±0.91% (89 runs sampled)
+
+# nested sets (expanded)
+ braces x 84,966 ops/sec ±0.48% (94 runs sampled)
+ minimatch x 140,720 ops/sec ±0.37% (95 runs sampled)
+
+# nested sets (optimized for regex)
+ braces x 263,340 ops/sec ±2.06% (92 runs sampled)
+ minimatch x 28,714 ops/sec ±0.40% (90 runs sampled)
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 197 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 4 | [doowb](https://github.com/doowb) |
+| 1 | [es128](https://github.com/es128) |
+| 1 | [eush77](https://github.com/eush77) |
+| 1 | [hemanth](https://github.com/hemanth) |
+| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [GitHub Profile](https://github.com/jonschlinkert)
+* [Twitter Profile](https://twitter.com/jonschlinkert)
+* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
+
+### License
+
+Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 08, 2019._
\ No newline at end of file
diff --git a/node_modules/braces/index.js b/node_modules/braces/index.js
new file mode 100644
index 00000000..0eee0f56
--- /dev/null
+++ b/node_modules/braces/index.js
@@ -0,0 +1,170 @@
+'use strict';
+
+const stringify = require('./lib/stringify');
+const compile = require('./lib/compile');
+const expand = require('./lib/expand');
+const parse = require('./lib/parse');
+
+/**
+ * Expand the given pattern or create a regex-compatible string.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
+ * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+const braces = (input, options = {}) => {
+ let output = [];
+
+ if (Array.isArray(input)) {
+ for (let pattern of input) {
+ let result = braces.create(pattern, options);
+ if (Array.isArray(result)) {
+ output.push(...result);
+ } else {
+ output.push(result);
+ }
+ }
+ } else {
+ output = [].concat(braces.create(input, options));
+ }
+
+ if (options && options.expand === true && options.nodupes === true) {
+ output = [...new Set(output)];
+ }
+ return output;
+};
+
+/**
+ * Parse the given `str` with the given `options`.
+ *
+ * ```js
+ * // braces.parse(pattern, [, options]);
+ * const ast = braces.parse('a/{b,c}/d');
+ * console.log(ast);
+ * ```
+ * @param {String} pattern Brace pattern to parse
+ * @param {Object} options
+ * @return {Object} Returns an AST
+ * @api public
+ */
+
+braces.parse = (input, options = {}) => parse(input, options);
+
+/**
+ * Creates a braces string from an AST, or an AST node.
+ *
+ * ```js
+ * const braces = require('braces');
+ * let ast = braces.parse('foo/{a,b}/bar');
+ * console.log(stringify(ast.nodes[2])); //=> '{a,b}'
+ * ```
+ * @param {String} `input` Brace pattern or AST.
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.stringify = (input, options = {}) => {
+ if (typeof input === 'string') {
+ return stringify(braces.parse(input, options), options);
+ }
+ return stringify(input, options);
+};
+
+/**
+ * Compiles a brace pattern into a regex-compatible, optimized string.
+ * This method is called by the main [braces](#braces) function by default.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.compile('a/{b,c}/d'));
+ * //=> ['a/(b|c)/d']
+ * ```
+ * @param {String} `input` Brace pattern or AST.
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.compile = (input, options = {}) => {
+ if (typeof input === 'string') {
+ input = braces.parse(input, options);
+ }
+ return compile(input, options);
+};
+
+/**
+ * Expands a brace pattern into an array. This method is called by the
+ * main [braces](#braces) function when `options.expand` is true. Before
+ * using this method it's recommended that you read the [performance notes](#performance))
+ * and advantages of using [.compile](#compile) instead.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/b/d', 'a/c/d'];
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.expand = (input, options = {}) => {
+ if (typeof input === 'string') {
+ input = braces.parse(input, options);
+ }
+
+ let result = expand(input, options);
+
+ // filter out empty strings if specified
+ if (options.noempty === true) {
+ result = result.filter(Boolean);
+ }
+
+ // filter out duplicates if specified
+ if (options.nodupes === true) {
+ result = [...new Set(result)];
+ }
+
+ return result;
+};
+
+/**
+ * Processes a brace pattern and returns either an expanded array
+ * (if `options.expand` is true), a highly optimized regex-compatible string.
+ * This method is called by the main [braces](#braces) function.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
+ * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.create = (input, options = {}) => {
+ if (input === '' || input.length < 3) {
+ return [input];
+ }
+
+ return options.expand !== true
+ ? braces.compile(input, options)
+ : braces.expand(input, options);
+};
+
+/**
+ * Expose "braces"
+ */
+
+module.exports = braces;
diff --git a/node_modules/braces/lib/compile.js b/node_modules/braces/lib/compile.js
new file mode 100644
index 00000000..3e984a4b
--- /dev/null
+++ b/node_modules/braces/lib/compile.js
@@ -0,0 +1,57 @@
+'use strict';
+
+const fill = require('fill-range');
+const utils = require('./utils');
+
+const compile = (ast, options = {}) => {
+ let walk = (node, parent = {}) => {
+ let invalidBlock = utils.isInvalidBrace(parent);
+ let invalidNode = node.invalid === true && options.escapeInvalid === true;
+ let invalid = invalidBlock === true || invalidNode === true;
+ let prefix = options.escapeInvalid === true ? '\\' : '';
+ let output = '';
+
+ if (node.isOpen === true) {
+ return prefix + node.value;
+ }
+ if (node.isClose === true) {
+ return prefix + node.value;
+ }
+
+ if (node.type === 'open') {
+ return invalid ? (prefix + node.value) : '(';
+ }
+
+ if (node.type === 'close') {
+ return invalid ? (prefix + node.value) : ')';
+ }
+
+ if (node.type === 'comma') {
+ return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
+ }
+
+ if (node.value) {
+ return node.value;
+ }
+
+ if (node.nodes && node.ranges > 0) {
+ let args = utils.reduce(node.nodes);
+ let range = fill(...args, { ...options, wrap: false, toRegex: true });
+
+ if (range.length !== 0) {
+ return args.length > 1 && range.length > 1 ? `(${range})` : range;
+ }
+ }
+
+ if (node.nodes) {
+ for (let child of node.nodes) {
+ output += walk(child, node);
+ }
+ }
+ return output;
+ };
+
+ return walk(ast);
+};
+
+module.exports = compile;
diff --git a/node_modules/braces/lib/constants.js b/node_modules/braces/lib/constants.js
new file mode 100644
index 00000000..a9379436
--- /dev/null
+++ b/node_modules/braces/lib/constants.js
@@ -0,0 +1,57 @@
+'use strict';
+
+module.exports = {
+ MAX_LENGTH: 1024 * 64,
+
+ // Digits
+ CHAR_0: '0', /* 0 */
+ CHAR_9: '9', /* 9 */
+
+ // Alphabet chars.
+ CHAR_UPPERCASE_A: 'A', /* A */
+ CHAR_LOWERCASE_A: 'a', /* a */
+ CHAR_UPPERCASE_Z: 'Z', /* Z */
+ CHAR_LOWERCASE_Z: 'z', /* z */
+
+ CHAR_LEFT_PARENTHESES: '(', /* ( */
+ CHAR_RIGHT_PARENTHESES: ')', /* ) */
+
+ CHAR_ASTERISK: '*', /* * */
+
+ // Non-alphabetic chars.
+ CHAR_AMPERSAND: '&', /* & */
+ CHAR_AT: '@', /* @ */
+ CHAR_BACKSLASH: '\\', /* \ */
+ CHAR_BACKTICK: '`', /* ` */
+ CHAR_CARRIAGE_RETURN: '\r', /* \r */
+ CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
+ CHAR_COLON: ':', /* : */
+ CHAR_COMMA: ',', /* , */
+ CHAR_DOLLAR: '$', /* . */
+ CHAR_DOT: '.', /* . */
+ CHAR_DOUBLE_QUOTE: '"', /* " */
+ CHAR_EQUAL: '=', /* = */
+ CHAR_EXCLAMATION_MARK: '!', /* ! */
+ CHAR_FORM_FEED: '\f', /* \f */
+ CHAR_FORWARD_SLASH: '/', /* / */
+ CHAR_HASH: '#', /* # */
+ CHAR_HYPHEN_MINUS: '-', /* - */
+ CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
+ CHAR_LEFT_CURLY_BRACE: '{', /* { */
+ CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
+ CHAR_LINE_FEED: '\n', /* \n */
+ CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
+ CHAR_PERCENT: '%', /* % */
+ CHAR_PLUS: '+', /* + */
+ CHAR_QUESTION_MARK: '?', /* ? */
+ CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
+ CHAR_RIGHT_CURLY_BRACE: '}', /* } */
+ CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
+ CHAR_SEMICOLON: ';', /* ; */
+ CHAR_SINGLE_QUOTE: '\'', /* ' */
+ CHAR_SPACE: ' ', /* */
+ CHAR_TAB: '\t', /* \t */
+ CHAR_UNDERSCORE: '_', /* _ */
+ CHAR_VERTICAL_LINE: '|', /* | */
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
+};
diff --git a/node_modules/braces/lib/expand.js b/node_modules/braces/lib/expand.js
new file mode 100644
index 00000000..376c748a
--- /dev/null
+++ b/node_modules/braces/lib/expand.js
@@ -0,0 +1,113 @@
+'use strict';
+
+const fill = require('fill-range');
+const stringify = require('./stringify');
+const utils = require('./utils');
+
+const append = (queue = '', stash = '', enclose = false) => {
+ let result = [];
+
+ queue = [].concat(queue);
+ stash = [].concat(stash);
+
+ if (!stash.length) return queue;
+ if (!queue.length) {
+ return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash;
+ }
+
+ for (let item of queue) {
+ if (Array.isArray(item)) {
+ for (let value of item) {
+ result.push(append(value, stash, enclose));
+ }
+ } else {
+ for (let ele of stash) {
+ if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
+ result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele));
+ }
+ }
+ }
+ return utils.flatten(result);
+};
+
+const expand = (ast, options = {}) => {
+ let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit;
+
+ let walk = (node, parent = {}) => {
+ node.queue = [];
+
+ let p = parent;
+ let q = parent.queue;
+
+ while (p.type !== 'brace' && p.type !== 'root' && p.parent) {
+ p = p.parent;
+ q = p.queue;
+ }
+
+ if (node.invalid || node.dollar) {
+ q.push(append(q.pop(), stringify(node, options)));
+ return;
+ }
+
+ if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) {
+ q.push(append(q.pop(), ['{}']));
+ return;
+ }
+
+ if (node.nodes && node.ranges > 0) {
+ let args = utils.reduce(node.nodes);
+
+ if (utils.exceedsLimit(...args, options.step, rangeLimit)) {
+ throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
+ }
+
+ let range = fill(...args, options);
+ if (range.length === 0) {
+ range = stringify(node, options);
+ }
+
+ q.push(append(q.pop(), range));
+ node.nodes = [];
+ return;
+ }
+
+ let enclose = utils.encloseBrace(node);
+ let queue = node.queue;
+ let block = node;
+
+ while (block.type !== 'brace' && block.type !== 'root' && block.parent) {
+ block = block.parent;
+ queue = block.queue;
+ }
+
+ for (let i = 0; i < node.nodes.length; i++) {
+ let child = node.nodes[i];
+
+ if (child.type === 'comma' && node.type === 'brace') {
+ if (i === 1) queue.push('');
+ queue.push('');
+ continue;
+ }
+
+ if (child.type === 'close') {
+ q.push(append(q.pop(), queue, enclose));
+ continue;
+ }
+
+ if (child.value && child.type !== 'open') {
+ queue.push(append(queue.pop(), child.value));
+ continue;
+ }
+
+ if (child.nodes) {
+ walk(child, node);
+ }
+ }
+
+ return queue;
+ };
+
+ return utils.flatten(walk(ast));
+};
+
+module.exports = expand;
diff --git a/node_modules/braces/lib/parse.js b/node_modules/braces/lib/parse.js
new file mode 100644
index 00000000..145ea264
--- /dev/null
+++ b/node_modules/braces/lib/parse.js
@@ -0,0 +1,333 @@
+'use strict';
+
+const stringify = require('./stringify');
+
+/**
+ * Constants
+ */
+
+const {
+ MAX_LENGTH,
+ CHAR_BACKSLASH, /* \ */
+ CHAR_BACKTICK, /* ` */
+ CHAR_COMMA, /* , */
+ CHAR_DOT, /* . */
+ CHAR_LEFT_PARENTHESES, /* ( */
+ CHAR_RIGHT_PARENTHESES, /* ) */
+ CHAR_LEFT_CURLY_BRACE, /* { */
+ CHAR_RIGHT_CURLY_BRACE, /* } */
+ CHAR_LEFT_SQUARE_BRACKET, /* [ */
+ CHAR_RIGHT_SQUARE_BRACKET, /* ] */
+ CHAR_DOUBLE_QUOTE, /* " */
+ CHAR_SINGLE_QUOTE, /* ' */
+ CHAR_NO_BREAK_SPACE,
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE
+} = require('./constants');
+
+/**
+ * parse
+ */
+
+const parse = (input, options = {}) => {
+ if (typeof input !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ let opts = options || {};
+ let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
+ if (input.length > max) {
+ throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
+ }
+
+ let ast = { type: 'root', input, nodes: [] };
+ let stack = [ast];
+ let block = ast;
+ let prev = ast;
+ let brackets = 0;
+ let length = input.length;
+ let index = 0;
+ let depth = 0;
+ let value;
+ let memo = {};
+
+ /**
+ * Helpers
+ */
+
+ const advance = () => input[index++];
+ const push = node => {
+ if (node.type === 'text' && prev.type === 'dot') {
+ prev.type = 'text';
+ }
+
+ if (prev && prev.type === 'text' && node.type === 'text') {
+ prev.value += node.value;
+ return;
+ }
+
+ block.nodes.push(node);
+ node.parent = block;
+ node.prev = prev;
+ prev = node;
+ return node;
+ };
+
+ push({ type: 'bos' });
+
+ while (index < length) {
+ block = stack[stack.length - 1];
+ value = advance();
+
+ /**
+ * Invalid chars
+ */
+
+ if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) {
+ continue;
+ }
+
+ /**
+ * Escaped chars
+ */
+
+ if (value === CHAR_BACKSLASH) {
+ push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() });
+ continue;
+ }
+
+ /**
+ * Right square bracket (literal): ']'
+ */
+
+ if (value === CHAR_RIGHT_SQUARE_BRACKET) {
+ push({ type: 'text', value: '\\' + value });
+ continue;
+ }
+
+ /**
+ * Left square bracket: '['
+ */
+
+ if (value === CHAR_LEFT_SQUARE_BRACKET) {
+ brackets++;
+
+ let closed = true;
+ let next;
+
+ while (index < length && (next = advance())) {
+ value += next;
+
+ if (next === CHAR_LEFT_SQUARE_BRACKET) {
+ brackets++;
+ continue;
+ }
+
+ if (next === CHAR_BACKSLASH) {
+ value += advance();
+ continue;
+ }
+
+ if (next === CHAR_RIGHT_SQUARE_BRACKET) {
+ brackets--;
+
+ if (brackets === 0) {
+ break;
+ }
+ }
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Parentheses
+ */
+
+ if (value === CHAR_LEFT_PARENTHESES) {
+ block = push({ type: 'paren', nodes: [] });
+ stack.push(block);
+ push({ type: 'text', value });
+ continue;
+ }
+
+ if (value === CHAR_RIGHT_PARENTHESES) {
+ if (block.type !== 'paren') {
+ push({ type: 'text', value });
+ continue;
+ }
+ block = stack.pop();
+ push({ type: 'text', value });
+ block = stack[stack.length - 1];
+ continue;
+ }
+
+ /**
+ * Quotes: '|"|`
+ */
+
+ if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
+ let open = value;
+ let next;
+
+ if (options.keepQuotes !== true) {
+ value = '';
+ }
+
+ while (index < length && (next = advance())) {
+ if (next === CHAR_BACKSLASH) {
+ value += next + advance();
+ continue;
+ }
+
+ if (next === open) {
+ if (options.keepQuotes === true) value += next;
+ break;
+ }
+
+ value += next;
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Left curly brace: '{'
+ */
+
+ if (value === CHAR_LEFT_CURLY_BRACE) {
+ depth++;
+
+ let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
+ let brace = {
+ type: 'brace',
+ open: true,
+ close: false,
+ dollar,
+ depth,
+ commas: 0,
+ ranges: 0,
+ nodes: []
+ };
+
+ block = push(brace);
+ stack.push(block);
+ push({ type: 'open', value });
+ continue;
+ }
+
+ /**
+ * Right curly brace: '}'
+ */
+
+ if (value === CHAR_RIGHT_CURLY_BRACE) {
+ if (block.type !== 'brace') {
+ push({ type: 'text', value });
+ continue;
+ }
+
+ let type = 'close';
+ block = stack.pop();
+ block.close = true;
+
+ push({ type, value });
+ depth--;
+
+ block = stack[stack.length - 1];
+ continue;
+ }
+
+ /**
+ * Comma: ','
+ */
+
+ if (value === CHAR_COMMA && depth > 0) {
+ if (block.ranges > 0) {
+ block.ranges = 0;
+ let open = block.nodes.shift();
+ block.nodes = [open, { type: 'text', value: stringify(block) }];
+ }
+
+ push({ type: 'comma', value });
+ block.commas++;
+ continue;
+ }
+
+ /**
+ * Dot: '.'
+ */
+
+ if (value === CHAR_DOT && depth > 0 && block.commas === 0) {
+ let siblings = block.nodes;
+
+ if (depth === 0 || siblings.length === 0) {
+ push({ type: 'text', value });
+ continue;
+ }
+
+ if (prev.type === 'dot') {
+ block.range = [];
+ prev.value += value;
+ prev.type = 'range';
+
+ if (block.nodes.length !== 3 && block.nodes.length !== 5) {
+ block.invalid = true;
+ block.ranges = 0;
+ prev.type = 'text';
+ continue;
+ }
+
+ block.ranges++;
+ block.args = [];
+ continue;
+ }
+
+ if (prev.type === 'range') {
+ siblings.pop();
+
+ let before = siblings[siblings.length - 1];
+ before.value += prev.value + value;
+ prev = before;
+ block.ranges--;
+ continue;
+ }
+
+ push({ type: 'dot', value });
+ continue;
+ }
+
+ /**
+ * Text
+ */
+
+ push({ type: 'text', value });
+ }
+
+ // Mark imbalanced braces and brackets as invalid
+ do {
+ block = stack.pop();
+
+ if (block.type !== 'root') {
+ block.nodes.forEach(node => {
+ if (!node.nodes) {
+ if (node.type === 'open') node.isOpen = true;
+ if (node.type === 'close') node.isClose = true;
+ if (!node.nodes) node.type = 'text';
+ node.invalid = true;
+ }
+ });
+
+ // get the location of the block on parent.nodes (block's siblings)
+ let parent = stack[stack.length - 1];
+ let index = parent.nodes.indexOf(block);
+ // replace the (invalid) block with it's nodes
+ parent.nodes.splice(index, 1, ...block.nodes);
+ }
+ } while (stack.length > 0);
+
+ push({ type: 'eos' });
+ return ast;
+};
+
+module.exports = parse;
diff --git a/node_modules/braces/lib/stringify.js b/node_modules/braces/lib/stringify.js
new file mode 100644
index 00000000..414b7bcc
--- /dev/null
+++ b/node_modules/braces/lib/stringify.js
@@ -0,0 +1,32 @@
+'use strict';
+
+const utils = require('./utils');
+
+module.exports = (ast, options = {}) => {
+ let stringify = (node, parent = {}) => {
+ let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
+ let invalidNode = node.invalid === true && options.escapeInvalid === true;
+ let output = '';
+
+ if (node.value) {
+ if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
+ return '\\' + node.value;
+ }
+ return node.value;
+ }
+
+ if (node.value) {
+ return node.value;
+ }
+
+ if (node.nodes) {
+ for (let child of node.nodes) {
+ output += stringify(child);
+ }
+ }
+ return output;
+ };
+
+ return stringify(ast);
+};
+
diff --git a/node_modules/braces/lib/utils.js b/node_modules/braces/lib/utils.js
new file mode 100644
index 00000000..e3551a67
--- /dev/null
+++ b/node_modules/braces/lib/utils.js
@@ -0,0 +1,112 @@
+'use strict';
+
+exports.isInteger = num => {
+ if (typeof num === 'number') {
+ return Number.isInteger(num);
+ }
+ if (typeof num === 'string' && num.trim() !== '') {
+ return Number.isInteger(Number(num));
+ }
+ return false;
+};
+
+/**
+ * Find a node of the given type
+ */
+
+exports.find = (node, type) => node.nodes.find(node => node.type === type);
+
+/**
+ * Find a node of the given type
+ */
+
+exports.exceedsLimit = (min, max, step = 1, limit) => {
+ if (limit === false) return false;
+ if (!exports.isInteger(min) || !exports.isInteger(max)) return false;
+ return ((Number(max) - Number(min)) / Number(step)) >= limit;
+};
+
+/**
+ * Escape the given node with '\\' before node.value
+ */
+
+exports.escapeNode = (block, n = 0, type) => {
+ let node = block.nodes[n];
+ if (!node) return;
+
+ if ((type && node.type === type) || node.type === 'open' || node.type === 'close') {
+ if (node.escaped !== true) {
+ node.value = '\\' + node.value;
+ node.escaped = true;
+ }
+ }
+};
+
+/**
+ * Returns true if the given brace node should be enclosed in literal braces
+ */
+
+exports.encloseBrace = node => {
+ if (node.type !== 'brace') return false;
+ if ((node.commas >> 0 + node.ranges >> 0) === 0) {
+ node.invalid = true;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Returns true if a brace node is invalid.
+ */
+
+exports.isInvalidBrace = block => {
+ if (block.type !== 'brace') return false;
+ if (block.invalid === true || block.dollar) return true;
+ if ((block.commas >> 0 + block.ranges >> 0) === 0) {
+ block.invalid = true;
+ return true;
+ }
+ if (block.open !== true || block.close !== true) {
+ block.invalid = true;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Returns true if a node is an open or close node
+ */
+
+exports.isOpenOrClose = node => {
+ if (node.type === 'open' || node.type === 'close') {
+ return true;
+ }
+ return node.open === true || node.close === true;
+};
+
+/**
+ * Reduce an array of text nodes.
+ */
+
+exports.reduce = nodes => nodes.reduce((acc, node) => {
+ if (node.type === 'text') acc.push(node.value);
+ if (node.type === 'range') node.type = 'text';
+ return acc;
+}, []);
+
+/**
+ * Flatten an array
+ */
+
+exports.flatten = (...args) => {
+ const result = [];
+ const flat = arr => {
+ for (let i = 0; i < arr.length; i++) {
+ let ele = arr[i];
+ Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele);
+ }
+ return result;
+ };
+ flat(args);
+ return result;
+};
diff --git a/node_modules/braces/package.json b/node_modules/braces/package.json
new file mode 100644
index 00000000..dfc1a2e6
--- /dev/null
+++ b/node_modules/braces/package.json
@@ -0,0 +1,126 @@
+{
+ "_args": [
+ [
+ "braces@3.0.2",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "braces@3.0.2",
+ "_id": "braces@3.0.2",
+ "_inBundle": false,
+ "_integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "_location": "/braces",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "braces@3.0.2",
+ "name": "braces",
+ "escapedName": "braces",
+ "rawSpec": "3.0.2",
+ "saveSpec": null,
+ "fetchSpec": "3.0.2"
+ },
+ "_requiredBy": [
+ "/chokidar"
+ ],
+ "_resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "_spec": "3.0.2",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/micromatch/braces/issues"
+ },
+ "contributors": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://twitter.com/doowb"
+ },
+ {
+ "name": "Elan Shanker",
+ "url": "https://github.com/es128"
+ },
+ {
+ "name": "Eugene Sharygin",
+ "url": "https://github.com/eush77"
+ },
+ {
+ "name": "hemanth.hm",
+ "url": "http://h3manth.com"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ }
+ ],
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
+ "devDependencies": {
+ "ansi-colors": "^3.2.4",
+ "bash-path": "^2.0.1",
+ "gulp-format-md": "^2.0.0",
+ "mocha": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "files": [
+ "index.js",
+ "lib"
+ ],
+ "homepage": "https://github.com/micromatch/braces",
+ "keywords": [
+ "alpha",
+ "alphabetical",
+ "bash",
+ "brace",
+ "braces",
+ "expand",
+ "expansion",
+ "filepath",
+ "fill",
+ "fs",
+ "glob",
+ "globbing",
+ "letter",
+ "match",
+ "matches",
+ "matching",
+ "number",
+ "numerical",
+ "path",
+ "range",
+ "ranges",
+ "sh"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "braces",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/micromatch/braces.git"
+ },
+ "scripts": {
+ "benchmark": "node benchmark",
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "lint": {
+ "reflinks": true
+ },
+ "plugins": [
+ "gulp-format-md"
+ ]
+ },
+ "version": "3.0.2"
+}
diff --git a/node_modules/chokidar/LICENSE b/node_modules/chokidar/LICENSE
new file mode 100644
index 00000000..fa9162b5
--- /dev/null
+++ b/node_modules/chokidar/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/chokidar/README.md b/node_modules/chokidar/README.md
new file mode 100644
index 00000000..8ec3deb5
--- /dev/null
+++ b/node_modules/chokidar/README.md
@@ -0,0 +1,308 @@
+# Chokidar [](https://github.com/paulmillr/chokidar) [](https://github.com/paulmillr/chokidar)
+
+> Minimal and efficient cross-platform file watching library
+
+[](https://www.npmjs.com/package/chokidar)
+
+## Why?
+
+Node.js `fs.watch`:
+
+* Doesn't report filenames on MacOS.
+* Doesn't report events at all when using editors like Sublime on MacOS.
+* Often reports events twice.
+* Emits most changes as `rename`.
+* Does not provide an easy way to recursively watch file trees.
+* Does not support recursive watching on Linux.
+
+Node.js `fs.watchFile`:
+
+* Almost as bad at event handling.
+* Also does not provide any recursive watching.
+* Results in high CPU utilization.
+
+Chokidar resolves these problems.
+
+Initially made for **[Brunch](https://brunch.io/)** (an ultra-swift web app build tool), it is now used in
+[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode),
+[gulp](https://github.com/gulpjs/gulp/),
+[karma](https://karma-runner.github.io/),
+[PM2](https://github.com/Unitech/PM2),
+[browserify](http://browserify.org/),
+[webpack](https://webpack.github.io/),
+[BrowserSync](https://www.browsersync.io/),
+and [many others](https://www.npmjs.com/browse/depended/chokidar).
+It has proven itself in production environments.
+
+Version 3 is out! Check out our blog post about it: [Chokidar 3: How to save 32TB of traffic every week](https://paulmillr.com/posts/chokidar-3-save-32tb-of-traffic/)
+
+## How?
+
+Chokidar does still rely on the Node.js core `fs` module, but when using
+`fs.watch` and `fs.watchFile` for watching, it normalizes the events it
+receives, often checking for truth by getting file stats and/or dir contents.
+
+On MacOS, chokidar by default uses a native extension exposing the Darwin
+`FSEvents` API. This provides very efficient recursive watching compared with
+implementations like `kqueue` available on most \*nix platforms. Chokidar still
+does have to do some work to normalize the events received that way as well.
+
+On most other platforms, the `fs.watch`-based implementation is the default, which
+avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
+watchers recursively for everything within scope of the paths that have been
+specified, so be judicious about not wasting system resources by watching much
+more than needed.
+
+## Getting started
+
+Install with npm:
+
+```sh
+npm install chokidar
+```
+
+Then `require` and use it in your code:
+
+```javascript
+const chokidar = require('chokidar');
+
+// One-liner for current directory
+chokidar.watch('.').on('all', (event, path) => {
+ console.log(event, path);
+});
+```
+
+## API
+
+```javascript
+// Example of a more typical implementation structure
+
+// Initialize watcher.
+const watcher = chokidar.watch('file, dir, glob, or array', {
+ ignored: /(^|[\/\\])\../, // ignore dotfiles
+ persistent: true
+});
+
+// Something to use when events are received.
+const log = console.log.bind(console);
+// Add event listeners.
+watcher
+ .on('add', path => log(`File ${path} has been added`))
+ .on('change', path => log(`File ${path} has been changed`))
+ .on('unlink', path => log(`File ${path} has been removed`));
+
+// More possible events.
+watcher
+ .on('addDir', path => log(`Directory ${path} has been added`))
+ .on('unlinkDir', path => log(`Directory ${path} has been removed`))
+ .on('error', error => log(`Watcher error: ${error}`))
+ .on('ready', () => log('Initial scan complete. Ready for changes'))
+ .on('raw', (event, path, details) => { // internal
+ log('Raw event info:', event, path, details);
+ });
+
+// 'add', 'addDir' and 'change' events also receive stat() results as second
+// argument when available: https://nodejs.org/api/fs.html#fs_class_fs_stats
+watcher.on('change', (path, stats) => {
+ if (stats) console.log(`File ${path} changed size to ${stats.size}`);
+});
+
+// Watch new files.
+watcher.add('new-file');
+watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);
+
+// Get list of actual paths being watched on the filesystem
+var watchedPaths = watcher.getWatched();
+
+// Un-watch some files.
+await watcher.unwatch('new-file*');
+
+// Stop watching.
+// The method is async!
+watcher.close().then(() => console.log('closed'));
+
+// Full list of options. See below for descriptions.
+// Do not use this example!
+chokidar.watch('file', {
+ persistent: true,
+
+ ignored: '*.txt',
+ ignoreInitial: false,
+ followSymlinks: true,
+ cwd: '.',
+ disableGlobbing: false,
+
+ usePolling: false,
+ interval: 100,
+ binaryInterval: 300,
+ alwaysStat: false,
+ depth: 99,
+ awaitWriteFinish: {
+ stabilityThreshold: 2000,
+ pollInterval: 100
+ },
+
+ ignorePermissionErrors: false,
+ atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
+});
+
+```
+
+`chokidar.watch(paths, [options])`
+
+* `paths` (string or array of strings). Paths to files, dirs to be watched
+recursively, or glob patterns.
+ - Note: globs must not contain windows separators (`\`),
+ because that's how they work by the standard —
+ you'll need to replace them with forward slashes (`/`).
+ - Note 2: for additional glob documentation, check out low-level
+ library: [picomatch](https://github.com/micromatch/picomatch).
+* `options` (object) Options object as defined below:
+
+#### Persistence
+
+* `persistent` (default: `true`). Indicates whether the process
+should continue to run as long as files are being watched. If set to
+`false` when using `fsevents` to watch, no more events will be emitted
+after `ready`, even if the process continues to run.
+
+#### Path filtering
+
+* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible definition)
+Defines files/paths to be ignored. The whole relative or absolute path is
+tested, not just filename. If a function with two arguments is provided, it
+gets called twice per path - once with a single argument (the path), second
+time with two arguments (the path and the
+[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
+object of that path).
+* `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` events are also emitted for matching paths while
+instantiating the watching as chokidar discovers these file paths (before the `ready` event).
+* `followSymlinks` (default: `true`). When `false`, only the
+symlinks themselves will be watched for changes instead of following
+the link references and bubbling events through the link's path.
+* `cwd` (no default). The base directory from which watch `paths` are to be
+derived. Paths emitted with events will be relative to this.
+* `disableGlobbing` (default: `false`). If set to `true` then the strings passed to `.watch()` and `.add()` are treated as
+literal path names, even if they look like globs.
+
+#### Performance
+
+* `usePolling` (default: `false`).
+Whether to use fs.watchFile (backed by polling), or fs.watch. If polling
+leads to high CPU utilization, consider setting this to `false`. It is
+typically necessary to **set this to `true` to successfully watch files over
+a network**, and it may be necessary to successfully watch files in other
+non-standard situations. Setting to `true` explicitly on MacOS overrides the
+`useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable
+to true (1) or false (0) in order to override this option.
+* _Polling-specific settings_ (effective when `usePolling: true`)
+ * `interval` (default: `100`). Interval of file system polling, in milliseconds. You may also
+ set the CHOKIDAR_INTERVAL env variable to override this option.
+ * `binaryInterval` (default: `300`). Interval of file system
+ polling for binary files.
+ ([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
+* `useFsEvents` (default: `true` on MacOS). Whether to use the
+`fsevents` watching interface if available. When set to `true` explicitly
+and `fsevents` is available this supercedes the `usePolling` setting. When
+set to `false` on MacOS, `usePolling: true` becomes the default.
+* `alwaysStat` (default: `false`). If relying upon the
+[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
+object that may get passed with `add`, `addDir`, and `change` events, set
+this to `true` to ensure it is provided even in cases where it wasn't
+already available from the underlying watch events.
+* `depth` (default: `undefined`). If set, limits how many levels of
+subdirectories will be traversed.
+* `awaitWriteFinish` (default: `false`).
+By default, the `add` event will fire when a file first appears on disk, before
+the entire file has been written. Furthermore, in some cases some `change`
+events will be emitted while the file is being written. In some cases,
+especially when watching for large files there will be a need to wait for the
+write operation to finish before responding to a file creation or modification.
+Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size,
+holding its `add` and `change` events until the size does not change for a
+configurable amount of time. The appropriate duration setting is heavily
+dependent on the OS and hardware. For accurate detection this parameter should
+be relatively high, making file watching much less responsive.
+Use with caution.
+ * *`options.awaitWriteFinish` can be set to an object in order to adjust
+ timing params:*
+ * `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in
+ milliseconds for a file size to remain constant before emitting its event.
+ * `awaitWriteFinish.pollInterval` (default: 100). File size polling interval, in milliseconds.
+
+#### Errors
+
+* `ignorePermissionErrors` (default: `false`). Indicates whether to watch files
+that don't have read permissions if possible. If watching fails due to `EPERM`
+or `EACCES` with this set to `true`, the errors will be suppressed silently.
+* `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`).
+Automatically filters out artifacts that occur when using editors that use
+"atomic writes" instead of writing directly to the source file. If a file is
+re-added within 100 ms of being deleted, Chokidar emits a `change` event
+rather than `unlink` then `add`. If the default of 100 ms does not work well
+for you, you can override it by setting `atomic` to a custom value, in
+milliseconds.
+
+### Methods & Events
+
+`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:
+
+* `.add(path / paths)`: Add files, directories, or glob patterns for tracking.
+Takes an array of strings or just one string.
+* `.on(event, callback)`: Listen for an FS event.
+Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
+`raw`, `error`.
+Additionally `all` is available which gets emitted with the underlying event
+name and path for every event other than `ready`, `raw`, and `error`. `raw` is internal, use it carefully.
+* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
+Takes an array of strings or just one string.
+* `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise. Use with `await` to ensure bugs don't happen.
+* `.getWatched()`: Returns an object representing all the paths on the file
+system being watched by this `FSWatcher` instance. The object's keys are all the
+directories (using absolute paths unless the `cwd` option was used), and the
+values are arrays of the names of the items contained in each directory.
+
+## CLI
+
+If you need a CLI interface for your file watching, check out
+[chokidar-cli](https://github.com/kimmobrunfeldt/chokidar-cli), allowing you to
+execute a command on each change, or get a stdio stream of change events.
+
+## Install Troubleshooting
+
+* `npm WARN optional dep failed, continuing fsevents@n.n.n`
+ * This message is normal part of how `npm` handles optional dependencies and is
+ not indicative of a problem. Even if accompanied by other related error messages,
+ Chokidar should function properly.
+
+* `TypeError: fsevents is not a constructor`
+ * Update chokidar by doing `rm -rf node_modules package-lock.json yarn.lock && npm install`, or update your dependency that uses chokidar.
+
+* Chokidar is producing `ENOSP` error on Linux, like this:
+ * `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
+ `Error: watch /home/ ENOSPC`
+ * This means Chokidar ran out of file handles and you'll need to increase their count by executing the following command in Terminal:
+ `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
+
+## Changelog
+
+For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md).
+- **v3.5 (Jan 6, 2021):** Support for ARM Macs with Apple Silicon. Fixes for deleted symlinks.
+- **v3.4 (Apr 26, 2020):** Support for directory-based symlinks. Fixes for macos file replacement.
+- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. That fixes IO race conditions related to close method.
+- **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Race condition fixes. Windows glob fixes. Improve stability by using tight range of dependency versions.
+- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%.
+- **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher.
+- **v2 (Dec 29, 2017):** Globs are now posix-style-only; without windows support. Tons of bugfixes.
+- **v1 (Apr 7, 2015):** Glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
+- **v0.1 (Apr 20, 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)
+
+## Also
+
+Why was chokidar named this way? What's the meaning behind it?
+
+>Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four).
+
+## License
+
+MIT (c) Paul Miller (), see [LICENSE](LICENSE) file.
diff --git a/node_modules/chokidar/index.js b/node_modules/chokidar/index.js
new file mode 100644
index 00000000..ed4b6d5b
--- /dev/null
+++ b/node_modules/chokidar/index.js
@@ -0,0 +1,973 @@
+'use strict';
+
+const { EventEmitter } = require('events');
+const fs = require('fs');
+const sysPath = require('path');
+const { promisify } = require('util');
+const readdirp = require('readdirp');
+const anymatch = require('anymatch').default;
+const globParent = require('glob-parent');
+const isGlob = require('is-glob');
+const braces = require('braces');
+const normalizePath = require('normalize-path');
+
+const NodeFsHandler = require('./lib/nodefs-handler');
+const FsEventsHandler = require('./lib/fsevents-handler');
+const {
+ EV_ALL,
+ EV_READY,
+ EV_ADD,
+ EV_CHANGE,
+ EV_UNLINK,
+ EV_ADD_DIR,
+ EV_UNLINK_DIR,
+ EV_RAW,
+ EV_ERROR,
+
+ STR_CLOSE,
+ STR_END,
+
+ BACK_SLASH_RE,
+ DOUBLE_SLASH_RE,
+ SLASH_OR_BACK_SLASH_RE,
+ DOT_RE,
+ REPLACER_RE,
+
+ SLASH,
+ SLASH_SLASH,
+ BRACE_START,
+ BANG,
+ ONE_DOT,
+ TWO_DOTS,
+ GLOBSTAR,
+ SLASH_GLOBSTAR,
+ ANYMATCH_OPTS,
+ STRING_TYPE,
+ FUNCTION_TYPE,
+ EMPTY_STR,
+ EMPTY_FN,
+
+ isWindows,
+ isMacos,
+ isIBMi
+} = require('./lib/constants');
+
+const stat = promisify(fs.stat);
+const readdir = promisify(fs.readdir);
+
+/**
+ * @typedef {String} Path
+ * @typedef {'all'|'add'|'addDir'|'change'|'unlink'|'unlinkDir'|'raw'|'error'|'ready'} EventName
+ * @typedef {'readdir'|'watch'|'add'|'remove'|'change'} ThrottleType
+ */
+
+/**
+ *
+ * @typedef {Object} WatchHelpers
+ * @property {Boolean} followSymlinks
+ * @property {'stat'|'lstat'} statMethod
+ * @property {Path} path
+ * @property {Path} watchPath
+ * @property {Function} entryPath
+ * @property {Boolean} hasGlob
+ * @property {Object} globFilter
+ * @property {Function} filterPath
+ * @property {Function} filterDir
+ */
+
+const arrify = (value = []) => Array.isArray(value) ? value : [value];
+const flatten = (list, result = []) => {
+ list.forEach(item => {
+ if (Array.isArray(item)) {
+ flatten(item, result);
+ } else {
+ result.push(item);
+ }
+ });
+ return result;
+};
+
+const unifyPaths = (paths_) => {
+ /**
+ * @type {Array}
+ */
+ const paths = flatten(arrify(paths_));
+ if (!paths.every(p => typeof p === STRING_TYPE)) {
+ throw new TypeError(`Non-string provided as watch path: ${paths}`);
+ }
+ return paths.map(normalizePathToUnix);
+};
+
+// If SLASH_SLASH occurs at the beginning of path, it is not replaced
+// because "//StoragePC/DrivePool/Movies" is a valid network path
+const toUnix = (string) => {
+ let str = string.replace(BACK_SLASH_RE, SLASH);
+ let prepend = false;
+ if (str.startsWith(SLASH_SLASH)) {
+ prepend = true;
+ }
+ while (str.match(DOUBLE_SLASH_RE)) {
+ str = str.replace(DOUBLE_SLASH_RE, SLASH);
+ }
+ if (prepend) {
+ str = SLASH + str;
+ }
+ return str;
+};
+
+// Our version of upath.normalize
+// TODO: this is not equal to path-normalize module - investigate why
+const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));
+
+const normalizeIgnored = (cwd = EMPTY_STR) => (path) => {
+ if (typeof path !== STRING_TYPE) return path;
+ return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
+};
+
+const getAbsolutePath = (path, cwd) => {
+ if (sysPath.isAbsolute(path)) {
+ return path;
+ }
+ if (path.startsWith(BANG)) {
+ return BANG + sysPath.join(cwd, path.slice(1));
+ }
+ return sysPath.join(cwd, path);
+};
+
+const undef = (opts, key) => opts[key] === undefined;
+
+/**
+ * Directory entry.
+ * @property {Path} path
+ * @property {Set} items
+ */
+class DirEntry {
+ /**
+ * @param {Path} dir
+ * @param {Function} removeWatcher
+ */
+ constructor(dir, removeWatcher) {
+ this.path = dir;
+ this._removeWatcher = removeWatcher;
+ /** @type {Set} */
+ this.items = new Set();
+ }
+
+ add(item) {
+ const {items} = this;
+ if (!items) return;
+ if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
+ }
+
+ async remove(item) {
+ const {items} = this;
+ if (!items) return;
+ items.delete(item);
+ if (items.size > 0) return;
+
+ const dir = this.path;
+ try {
+ await readdir(dir);
+ } catch (err) {
+ if (this._removeWatcher) {
+ this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
+ }
+ }
+ }
+
+ has(item) {
+ const {items} = this;
+ if (!items) return;
+ return items.has(item);
+ }
+
+ /**
+ * @returns {Array}
+ */
+ getChildren() {
+ const {items} = this;
+ if (!items) return;
+ return [...items.values()];
+ }
+
+ dispose() {
+ this.items.clear();
+ delete this.path;
+ delete this._removeWatcher;
+ delete this.items;
+ Object.freeze(this);
+ }
+}
+
+const STAT_METHOD_F = 'stat';
+const STAT_METHOD_L = 'lstat';
+class WatchHelper {
+ constructor(path, watchPath, follow, fsw) {
+ this.fsw = fsw;
+ this.path = path = path.replace(REPLACER_RE, EMPTY_STR);
+ this.watchPath = watchPath;
+ this.fullWatchPath = sysPath.resolve(watchPath);
+ this.hasGlob = watchPath !== path;
+ /** @type {object|boolean} */
+ if (path === EMPTY_STR) this.hasGlob = false;
+ this.globSymlink = this.hasGlob && follow ? undefined : false;
+ this.globFilter = this.hasGlob ? anymatch(path, undefined, ANYMATCH_OPTS) : false;
+ this.dirParts = this.getDirParts(path);
+ this.dirParts.forEach((parts) => {
+ if (parts.length > 1) parts.pop();
+ });
+ this.followSymlinks = follow;
+ this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
+ }
+
+ checkGlobSymlink(entry) {
+ // only need to resolve once
+ // first entry should always have entry.parentDir === EMPTY_STR
+ if (this.globSymlink === undefined) {
+ this.globSymlink = entry.fullParentDir === this.fullWatchPath ?
+ false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath};
+ }
+
+ if (this.globSymlink) {
+ return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath);
+ }
+
+ return entry.fullPath;
+ }
+
+ entryPath(entry) {
+ return sysPath.join(this.watchPath,
+ sysPath.relative(this.watchPath, this.checkGlobSymlink(entry))
+ );
+ }
+
+ filterPath(entry) {
+ const {stats} = entry;
+ if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
+ const resolvedPath = this.entryPath(entry);
+ const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ?
+ this.globFilter(resolvedPath) : true;
+ return matchesGlob &&
+ this.fsw._isntIgnored(resolvedPath, stats) &&
+ this.fsw._hasReadPermissions(stats);
+ }
+
+ getDirParts(path) {
+ if (!this.hasGlob) return [];
+ const parts = [];
+ const expandedPath = path.includes(BRACE_START) ? braces.expand(path) : [path];
+ expandedPath.forEach((path) => {
+ parts.push(sysPath.relative(this.watchPath, path).split(SLASH_OR_BACK_SLASH_RE));
+ });
+ return parts;
+ }
+
+ filterDir(entry) {
+ if (this.hasGlob) {
+ const entryParts = this.getDirParts(this.checkGlobSymlink(entry));
+ let globstar = false;
+ this.unmatchedGlob = !this.dirParts.some((parts) => {
+ return parts.every((part, i) => {
+ if (part === GLOBSTAR) globstar = true;
+ return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS);
+ });
+ });
+ }
+ return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
+ }
+}
+
+/**
+ * Watches files & directories for changes. Emitted events:
+ * `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
+ *
+ * new FSWatcher()
+ * .add(directories)
+ * .on('add', path => log('File', path, 'was added'))
+ */
+class FSWatcher extends EventEmitter {
+// Not indenting methods for history sake; for now.
+constructor(_opts) {
+ super();
+
+ const opts = {};
+ if (_opts) Object.assign(opts, _opts); // for frozen objects
+
+ /** @type {Map} */
+ this._watched = new Map();
+ /** @type {Map} */
+ this._closers = new Map();
+ /** @type {Set} */
+ this._ignoredPaths = new Set();
+
+ /** @type {Map} */
+ this._throttled = new Map();
+
+ /** @type {Map} */
+ this._symlinkPaths = new Map();
+
+ this._streams = new Set();
+ this.closed = false;
+
+ // Set up default options.
+ if (undef(opts, 'persistent')) opts.persistent = true;
+ if (undef(opts, 'ignoreInitial')) opts.ignoreInitial = false;
+ if (undef(opts, 'ignorePermissionErrors')) opts.ignorePermissionErrors = false;
+ if (undef(opts, 'interval')) opts.interval = 100;
+ if (undef(opts, 'binaryInterval')) opts.binaryInterval = 300;
+ if (undef(opts, 'disableGlobbing')) opts.disableGlobbing = false;
+ opts.enableBinaryInterval = opts.binaryInterval !== opts.interval;
+
+ // Enable fsevents on OS X when polling isn't explicitly enabled.
+ if (undef(opts, 'useFsEvents')) opts.useFsEvents = !opts.usePolling;
+
+ // If we can't use fsevents, ensure the options reflect it's disabled.
+ const canUseFsEvents = FsEventsHandler.canUse();
+ if (!canUseFsEvents) opts.useFsEvents = false;
+
+ // Use polling on Mac if not using fsevents.
+ // Other platforms use non-polling fs_watch.
+ if (undef(opts, 'usePolling') && !opts.useFsEvents) {
+ opts.usePolling = isMacos;
+ }
+
+ // Always default to polling on IBM i because fs.watch() is not available on IBM i.
+ if(isIBMi) {
+ opts.usePolling = true;
+ }
+
+ // Global override (useful for end-developers that need to force polling for all
+ // instances of chokidar, regardless of usage/dependency depth)
+ const envPoll = process.env.CHOKIDAR_USEPOLLING;
+ if (envPoll !== undefined) {
+ const envLower = envPoll.toLowerCase();
+
+ if (envLower === 'false' || envLower === '0') {
+ opts.usePolling = false;
+ } else if (envLower === 'true' || envLower === '1') {
+ opts.usePolling = true;
+ } else {
+ opts.usePolling = !!envLower;
+ }
+ }
+ const envInterval = process.env.CHOKIDAR_INTERVAL;
+ if (envInterval) {
+ opts.interval = Number.parseInt(envInterval, 10);
+ }
+
+ // Editor atomic write normalization enabled by default with fs.watch
+ if (undef(opts, 'atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
+ if (opts.atomic) this._pendingUnlinks = new Map();
+
+ if (undef(opts, 'followSymlinks')) opts.followSymlinks = true;
+
+ if (undef(opts, 'awaitWriteFinish')) opts.awaitWriteFinish = false;
+ if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
+ const awf = opts.awaitWriteFinish;
+ if (awf) {
+ if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
+ if (!awf.pollInterval) awf.pollInterval = 100;
+ this._pendingWrites = new Map();
+ }
+ if (opts.ignored) opts.ignored = arrify(opts.ignored);
+
+ let readyCalls = 0;
+ this._emitReady = () => {
+ readyCalls++;
+ if (readyCalls >= this._readyCount) {
+ this._emitReady = EMPTY_FN;
+ this._readyEmitted = true;
+ // use process.nextTick to allow time for listener to be bound
+ process.nextTick(() => this.emit(EV_READY));
+ }
+ };
+ this._emitRaw = (...args) => this.emit(EV_RAW, ...args);
+ this._readyEmitted = false;
+ this.options = opts;
+
+ // Initialize with proper watcher.
+ if (opts.useFsEvents) {
+ this._fsEventsHandler = new FsEventsHandler(this);
+ } else {
+ this._nodeFsHandler = new NodeFsHandler(this);
+ }
+
+ // You’re frozen when your heart’s not open.
+ Object.freeze(opts);
+}
+
+// Public methods
+
+/**
+ * Adds paths to be watched on an existing FSWatcher instance
+ * @param {Path|Array} paths_
+ * @param {String=} _origAdd private; for handling non-existent paths to be watched
+ * @param {Boolean=} _internal private; indicates a non-user add
+ * @returns {FSWatcher} for chaining
+ */
+add(paths_, _origAdd, _internal) {
+ const {cwd, disableGlobbing} = this.options;
+ this.closed = false;
+ let paths = unifyPaths(paths_);
+ if (cwd) {
+ paths = paths.map((path) => {
+ const absPath = getAbsolutePath(path, cwd);
+
+ // Check `path` instead of `absPath` because the cwd portion can't be a glob
+ if (disableGlobbing || !isGlob(path)) {
+ return absPath;
+ }
+ return normalizePath(absPath);
+ });
+ }
+
+ // set aside negated glob strings
+ paths = paths.filter((path) => {
+ if (path.startsWith(BANG)) {
+ this._ignoredPaths.add(path.slice(1));
+ return false;
+ }
+
+ // if a path is being added that was previously ignored, stop ignoring it
+ this._ignoredPaths.delete(path);
+ this._ignoredPaths.delete(path + SLASH_GLOBSTAR);
+
+ // reset the cached userIgnored anymatch fn
+ // to make ignoredPaths changes effective
+ this._userIgnored = undefined;
+
+ return true;
+ });
+
+ if (this.options.useFsEvents && this._fsEventsHandler) {
+ if (!this._readyCount) this._readyCount = paths.length;
+ if (this.options.persistent) this._readyCount *= 2;
+ paths.forEach((path) => this._fsEventsHandler._addToFsEvents(path));
+ } else {
+ if (!this._readyCount) this._readyCount = 0;
+ this._readyCount += paths.length;
+ Promise.all(
+ paths.map(async path => {
+ const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, 0, 0, _origAdd);
+ if (res) this._emitReady();
+ return res;
+ })
+ ).then(results => {
+ if (this.closed) return;
+ results.filter(item => item).forEach(item => {
+ this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
+ });
+ });
+ }
+
+ return this;
+}
+
+/**
+ * Close watchers or start ignoring events from specified paths.
+ * @param {Path|Array} paths_ - string or array of strings, file/directory paths and/or globs
+ * @returns {FSWatcher} for chaining
+*/
+unwatch(paths_) {
+ if (this.closed) return this;
+ const paths = unifyPaths(paths_);
+ const {cwd} = this.options;
+
+ paths.forEach((path) => {
+ // convert to absolute path unless relative path already matches
+ if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
+ if (cwd) path = sysPath.join(cwd, path);
+ path = sysPath.resolve(path);
+ }
+
+ this._closePath(path);
+
+ this._ignoredPaths.add(path);
+ if (this._watched.has(path)) {
+ this._ignoredPaths.add(path + SLASH_GLOBSTAR);
+ }
+
+ // reset the cached userIgnored anymatch fn
+ // to make ignoredPaths changes effective
+ this._userIgnored = undefined;
+ });
+
+ return this;
+}
+
+/**
+ * Close watchers and remove all listeners from watched paths.
+ * @returns {Promise}.
+*/
+close() {
+ if (this.closed) return this._closePromise;
+ this.closed = true;
+
+ // Memory management.
+ this.removeAllListeners();
+ const closers = [];
+ this._closers.forEach(closerList => closerList.forEach(closer => {
+ const promise = closer();
+ if (promise instanceof Promise) closers.push(promise);
+ }));
+ this._streams.forEach(stream => stream.destroy());
+ this._userIgnored = undefined;
+ this._readyCount = 0;
+ this._readyEmitted = false;
+ this._watched.forEach(dirent => dirent.dispose());
+ ['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => {
+ this[`_${key}`].clear();
+ });
+
+ this._closePromise = closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve();
+ return this._closePromise;
+}
+
+/**
+ * Expose list of watched paths
+ * @returns {Object} for chaining
+*/
+getWatched() {
+ const watchList = {};
+ this._watched.forEach((entry, dir) => {
+ const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
+ watchList[key || ONE_DOT] = entry.getChildren().sort();
+ });
+ return watchList;
+}
+
+emitWithAll(event, args) {
+ this.emit(...args);
+ if (event !== EV_ERROR) this.emit(EV_ALL, ...args);
+}
+
+// Common helpers
+// --------------
+
+/**
+ * Normalize and emit events.
+ * Calling _emit DOES NOT MEAN emit() would be called!
+ * @param {EventName} event Type of event
+ * @param {Path} path File or directory path
+ * @param {*=} val1 arguments to be passed with event
+ * @param {*=} val2
+ * @param {*=} val3
+ * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
+ */
+async _emit(event, path, val1, val2, val3) {
+ if (this.closed) return;
+
+ const opts = this.options;
+ if (isWindows) path = sysPath.normalize(path);
+ if (opts.cwd) path = sysPath.relative(opts.cwd, path);
+ /** @type Array */
+ const args = [event, path];
+ if (val3 !== undefined) args.push(val1, val2, val3);
+ else if (val2 !== undefined) args.push(val1, val2);
+ else if (val1 !== undefined) args.push(val1);
+
+ const awf = opts.awaitWriteFinish;
+ let pw;
+ if (awf && (pw = this._pendingWrites.get(path))) {
+ pw.lastChange = new Date();
+ return this;
+ }
+
+ if (opts.atomic) {
+ if (event === EV_UNLINK) {
+ this._pendingUnlinks.set(path, args);
+ setTimeout(() => {
+ this._pendingUnlinks.forEach((entry, path) => {
+ this.emit(...entry);
+ this.emit(EV_ALL, ...entry);
+ this._pendingUnlinks.delete(path);
+ });
+ }, typeof opts.atomic === 'number' ? opts.atomic : 100);
+ return this;
+ }
+ if (event === EV_ADD && this._pendingUnlinks.has(path)) {
+ event = args[0] = EV_CHANGE;
+ this._pendingUnlinks.delete(path);
+ }
+ }
+
+ if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) {
+ const awfEmit = (err, stats) => {
+ if (err) {
+ event = args[0] = EV_ERROR;
+ args[1] = err;
+ this.emitWithAll(event, args);
+ } else if (stats) {
+ // if stats doesn't exist the file must have been deleted
+ if (args.length > 2) {
+ args[2] = stats;
+ } else {
+ args.push(stats);
+ }
+ this.emitWithAll(event, args);
+ }
+ };
+
+ this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
+ return this;
+ }
+
+ if (event === EV_CHANGE) {
+ const isThrottled = !this._throttle(EV_CHANGE, path, 50);
+ if (isThrottled) return this;
+ }
+
+ if (opts.alwaysStat && val1 === undefined &&
+ (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)
+ ) {
+ const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
+ let stats;
+ try {
+ stats = await stat(fullPath);
+ } catch (err) {}
+ // Suppress event when fs_stat fails, to avoid sending undefined 'stat'
+ if (!stats || this.closed) return;
+ args.push(stats);
+ }
+ this.emitWithAll(event, args);
+
+ return this;
+}
+
+/**
+ * Common handler for errors
+ * @param {Error} error
+ * @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
+ */
+_handleError(error) {
+ const code = error && error.code;
+ if (error && code !== 'ENOENT' && code !== 'ENOTDIR' &&
+ (!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))
+ ) {
+ this.emit(EV_ERROR, error);
+ }
+ return error || this.closed;
+}
+
+/**
+ * Helper utility for throttling
+ * @param {ThrottleType} actionType type being throttled
+ * @param {Path} path being acted upon
+ * @param {Number} timeout duration of time to suppress duplicate actions
+ * @returns {Object|false} tracking object or false if action should be suppressed
+ */
+_throttle(actionType, path, timeout) {
+ if (!this._throttled.has(actionType)) {
+ this._throttled.set(actionType, new Map());
+ }
+
+ /** @type {Map} */
+ const action = this._throttled.get(actionType);
+ /** @type {Object} */
+ const actionPath = action.get(path);
+
+ if (actionPath) {
+ actionPath.count++;
+ return false;
+ }
+
+ let timeoutObject;
+ const clear = () => {
+ const item = action.get(path);
+ const count = item ? item.count : 0;
+ action.delete(path);
+ clearTimeout(timeoutObject);
+ if (item) clearTimeout(item.timeoutObject);
+ return count;
+ };
+ timeoutObject = setTimeout(clear, timeout);
+ const thr = {timeoutObject, clear, count: 0};
+ action.set(path, thr);
+ return thr;
+}
+
+_incrReadyCount() {
+ return this._readyCount++;
+}
+
+/**
+ * Awaits write operation to finish.
+ * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
+ * @param {Path} path being acted upon
+ * @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
+ * @param {EventName} event
+ * @param {Function} awfEmit Callback to be called when ready for event to be emitted.
+ */
+_awaitWriteFinish(path, threshold, event, awfEmit) {
+ let timeoutHandler;
+
+ let fullPath = path;
+ if (this.options.cwd && !sysPath.isAbsolute(path)) {
+ fullPath = sysPath.join(this.options.cwd, path);
+ }
+
+ const now = new Date();
+
+ const awaitWriteFinish = (prevStat) => {
+ fs.stat(fullPath, (err, curStat) => {
+ if (err || !this._pendingWrites.has(path)) {
+ if (err && err.code !== 'ENOENT') awfEmit(err);
+ return;
+ }
+
+ const now = Number(new Date());
+
+ if (prevStat && curStat.size !== prevStat.size) {
+ this._pendingWrites.get(path).lastChange = now;
+ }
+ const pw = this._pendingWrites.get(path);
+ const df = now - pw.lastChange;
+
+ if (df >= threshold) {
+ this._pendingWrites.delete(path);
+ awfEmit(undefined, curStat);
+ } else {
+ timeoutHandler = setTimeout(
+ awaitWriteFinish,
+ this.options.awaitWriteFinish.pollInterval,
+ curStat
+ );
+ }
+ });
+ };
+
+ if (!this._pendingWrites.has(path)) {
+ this._pendingWrites.set(path, {
+ lastChange: now,
+ cancelWait: () => {
+ this._pendingWrites.delete(path);
+ clearTimeout(timeoutHandler);
+ return event;
+ }
+ });
+ timeoutHandler = setTimeout(
+ awaitWriteFinish,
+ this.options.awaitWriteFinish.pollInterval
+ );
+ }
+}
+
+_getGlobIgnored() {
+ return [...this._ignoredPaths.values()];
+}
+
+/**
+ * Determines whether user has asked to ignore this path.
+ * @param {Path} path filepath or dir
+ * @param {fs.Stats=} stats result of fs.stat
+ * @returns {Boolean}
+ */
+_isIgnored(path, stats) {
+ if (this.options.atomic && DOT_RE.test(path)) return true;
+ if (!this._userIgnored) {
+ const {cwd} = this.options;
+ const ign = this.options.ignored;
+
+ const ignored = ign && ign.map(normalizeIgnored(cwd));
+ const paths = arrify(ignored)
+ .filter((path) => typeof path === STRING_TYPE && !isGlob(path))
+ .map((path) => path + SLASH_GLOBSTAR);
+ const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths);
+ this._userIgnored = anymatch(list, undefined, ANYMATCH_OPTS);
+ }
+
+ return this._userIgnored([path, stats]);
+}
+
+_isntIgnored(path, stat) {
+ return !this._isIgnored(path, stat);
+}
+
+/**
+ * Provides a set of common helpers and properties relating to symlink and glob handling.
+ * @param {Path} path file, directory, or glob pattern being watched
+ * @param {Number=} depth at any depth > 0, this isn't a glob
+ * @returns {WatchHelper} object containing helpers for this path
+ */
+_getWatchHelpers(path, depth) {
+ const watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path);
+ const follow = this.options.followSymlinks;
+
+ return new WatchHelper(path, watchPath, follow, this);
+}
+
+// Directory helpers
+// -----------------
+
+/**
+ * Provides directory tracking objects
+ * @param {String} directory path of the directory
+ * @returns {DirEntry} the directory's tracking object
+ */
+_getWatchedDir(directory) {
+ if (!this._boundRemove) this._boundRemove = this._remove.bind(this);
+ const dir = sysPath.resolve(directory);
+ if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove));
+ return this._watched.get(dir);
+}
+
+// File helpers
+// ------------
+
+/**
+ * Check for read permissions.
+ * Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405
+ * @param {fs.Stats} stats - object, result of fs_stat
+ * @returns {Boolean} indicates whether the file can be read
+*/
+_hasReadPermissions(stats) {
+ if (this.options.ignorePermissionErrors) return true;
+
+ // stats.mode may be bigint
+ const md = stats && Number.parseInt(stats.mode, 10);
+ const st = md & 0o777;
+ const it = Number.parseInt(st.toString(8)[0], 10);
+ return Boolean(4 & it);
+}
+
+/**
+ * Handles emitting unlink events for
+ * files and directories, and via recursion, for
+ * files and directories within directories that are unlinked
+ * @param {String} directory within which the following item is located
+ * @param {String} item base path of item/directory
+ * @returns {void}
+*/
+_remove(directory, item, isDirectory) {
+ // if what is being deleted is a directory, get that directory's paths
+ // for recursive deleting and cleaning of watched object
+ // if it is not a directory, nestedDirectoryChildren will be empty array
+ const path = sysPath.join(directory, item);
+ const fullPath = sysPath.resolve(path);
+ isDirectory = isDirectory != null
+ ? isDirectory
+ : this._watched.has(path) || this._watched.has(fullPath);
+
+ // prevent duplicate handling in case of arriving here nearly simultaneously
+ // via multiple paths (such as _handleFile and _handleDir)
+ if (!this._throttle('remove', path, 100)) return;
+
+ // if the only watched file is removed, watch for its return
+ if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) {
+ this.add(directory, item, true);
+ }
+
+ // This will create a new entry in the watched object in either case
+ // so we got to do the directory check beforehand
+ const wp = this._getWatchedDir(path);
+ const nestedDirectoryChildren = wp.getChildren();
+
+ // Recursively remove children directories / files.
+ nestedDirectoryChildren.forEach(nested => this._remove(path, nested));
+
+ // Check if item was on the watched list and remove it
+ const parent = this._getWatchedDir(directory);
+ const wasTracked = parent.has(item);
+ parent.remove(item);
+
+ // Fixes issue #1042 -> Relative paths were detected and added as symlinks
+ // (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
+ // but never removed from the map in case the path was deleted.
+ // This leads to an incorrect state if the path was recreated:
+ // https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
+ if (this._symlinkPaths.has(fullPath)) {
+ this._symlinkPaths.delete(fullPath);
+ }
+
+ // If we wait for this file to be fully written, cancel the wait.
+ let relPath = path;
+ if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
+ if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
+ const event = this._pendingWrites.get(relPath).cancelWait();
+ if (event === EV_ADD) return;
+ }
+
+ // The Entry will either be a directory that just got removed
+ // or a bogus entry to a file, in either case we have to remove it
+ this._watched.delete(path);
+ this._watched.delete(fullPath);
+ const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK;
+ if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);
+
+ // Avoid conflicts if we later create another file with the same name
+ if (!this.options.useFsEvents) {
+ this._closePath(path);
+ }
+}
+
+/**
+ * Closes all watchers for a path
+ * @param {Path} path
+ */
+_closePath(path) {
+ this._closeFile(path)
+ const dir = sysPath.dirname(path);
+ this._getWatchedDir(dir).remove(sysPath.basename(path));
+}
+
+/**
+ * Closes only file-specific watchers
+ * @param {Path} path
+ */
+_closeFile(path) {
+ const closers = this._closers.get(path);
+ if (!closers) return;
+ closers.forEach(closer => closer());
+ this._closers.delete(path);
+}
+
+/**
+ *
+ * @param {Path} path
+ * @param {Function} closer
+ */
+_addPathCloser(path, closer) {
+ if (!closer) return;
+ let list = this._closers.get(path);
+ if (!list) {
+ list = [];
+ this._closers.set(path, list);
+ }
+ list.push(closer);
+}
+
+_readdirp(root, opts) {
+ if (this.closed) return;
+ const options = {type: EV_ALL, alwaysStat: true, lstat: true, ...opts};
+ let stream = readdirp(root, options);
+ this._streams.add(stream);
+ stream.once(STR_CLOSE, () => {
+ stream = undefined;
+ });
+ stream.once(STR_END, () => {
+ if (stream) {
+ this._streams.delete(stream);
+ stream = undefined;
+ }
+ });
+ return stream;
+}
+
+}
+
+// Export FSWatcher class
+exports.FSWatcher = FSWatcher;
+
+/**
+ * Instantiates watcher with paths to be tracked.
+ * @param {String|Array} paths file/directory paths and/or globs
+ * @param {Object=} options chokidar opts
+ * @returns an instance of FSWatcher for chaining.
+ */
+const watch = (paths, options) => {
+ const watcher = new FSWatcher(options);
+ watcher.add(paths);
+ return watcher;
+};
+
+exports.watch = watch;
diff --git a/node_modules/chokidar/lib/constants.js b/node_modules/chokidar/lib/constants.js
new file mode 100644
index 00000000..1454f85b
--- /dev/null
+++ b/node_modules/chokidar/lib/constants.js
@@ -0,0 +1,65 @@
+'use strict';
+
+const {sep} = require('path');
+const {platform} = process;
+const os = require('os');
+
+exports.EV_ALL = 'all';
+exports.EV_READY = 'ready';
+exports.EV_ADD = 'add';
+exports.EV_CHANGE = 'change';
+exports.EV_ADD_DIR = 'addDir';
+exports.EV_UNLINK = 'unlink';
+exports.EV_UNLINK_DIR = 'unlinkDir';
+exports.EV_RAW = 'raw';
+exports.EV_ERROR = 'error';
+
+exports.STR_DATA = 'data';
+exports.STR_END = 'end';
+exports.STR_CLOSE = 'close';
+
+exports.FSEVENT_CREATED = 'created';
+exports.FSEVENT_MODIFIED = 'modified';
+exports.FSEVENT_DELETED = 'deleted';
+exports.FSEVENT_MOVED = 'moved';
+exports.FSEVENT_CLONED = 'cloned';
+exports.FSEVENT_UNKNOWN = 'unknown';
+exports.FSEVENT_TYPE_FILE = 'file';
+exports.FSEVENT_TYPE_DIRECTORY = 'directory';
+exports.FSEVENT_TYPE_SYMLINK = 'symlink';
+
+exports.KEY_LISTENERS = 'listeners';
+exports.KEY_ERR = 'errHandlers';
+exports.KEY_RAW = 'rawEmitters';
+exports.HANDLER_KEYS = [exports.KEY_LISTENERS, exports.KEY_ERR, exports.KEY_RAW];
+
+exports.DOT_SLASH = `.${sep}`;
+
+exports.BACK_SLASH_RE = /\\/g;
+exports.DOUBLE_SLASH_RE = /\/\//;
+exports.SLASH_OR_BACK_SLASH_RE = /[/\\]/;
+exports.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
+exports.REPLACER_RE = /^\.[/\\]/;
+
+exports.SLASH = '/';
+exports.SLASH_SLASH = '//';
+exports.BRACE_START = '{';
+exports.BANG = '!';
+exports.ONE_DOT = '.';
+exports.TWO_DOTS = '..';
+exports.STAR = '*';
+exports.GLOBSTAR = '**';
+exports.ROOT_GLOBSTAR = '/**/*';
+exports.SLASH_GLOBSTAR = '/**';
+exports.DIR_SUFFIX = 'Dir';
+exports.ANYMATCH_OPTS = {dot: true};
+exports.STRING_TYPE = 'string';
+exports.FUNCTION_TYPE = 'function';
+exports.EMPTY_STR = '';
+exports.EMPTY_FN = () => {};
+exports.IDENTITY_FN = val => val;
+
+exports.isWindows = platform === 'win32';
+exports.isMacos = platform === 'darwin';
+exports.isLinux = platform === 'linux';
+exports.isIBMi = os.type() === 'OS400';
diff --git a/node_modules/chokidar/lib/fsevents-handler.js b/node_modules/chokidar/lib/fsevents-handler.js
new file mode 100644
index 00000000..0f7f2cba
--- /dev/null
+++ b/node_modules/chokidar/lib/fsevents-handler.js
@@ -0,0 +1,524 @@
+'use strict';
+
+const fs = require('fs');
+const sysPath = require('path');
+const { promisify } = require('util');
+
+let fsevents;
+try {
+ fsevents = require('fsevents');
+} catch (error) {
+ if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error);
+}
+
+if (fsevents) {
+ // TODO: real check
+ const mtch = process.version.match(/v(\d+)\.(\d+)/);
+ if (mtch && mtch[1] && mtch[2]) {
+ const maj = Number.parseInt(mtch[1], 10);
+ const min = Number.parseInt(mtch[2], 10);
+ if (maj === 8 && min < 16) {
+ fsevents = undefined;
+ }
+ }
+}
+
+const {
+ EV_ADD,
+ EV_CHANGE,
+ EV_ADD_DIR,
+ EV_UNLINK,
+ EV_ERROR,
+ STR_DATA,
+ STR_END,
+ FSEVENT_CREATED,
+ FSEVENT_MODIFIED,
+ FSEVENT_DELETED,
+ FSEVENT_MOVED,
+ // FSEVENT_CLONED,
+ FSEVENT_UNKNOWN,
+ FSEVENT_TYPE_FILE,
+ FSEVENT_TYPE_DIRECTORY,
+ FSEVENT_TYPE_SYMLINK,
+
+ ROOT_GLOBSTAR,
+ DIR_SUFFIX,
+ DOT_SLASH,
+ FUNCTION_TYPE,
+ EMPTY_FN,
+ IDENTITY_FN
+} = require('./constants');
+
+const Depth = (value) => isNaN(value) ? {} : {depth: value};
+
+const stat = promisify(fs.stat);
+const lstat = promisify(fs.lstat);
+const realpath = promisify(fs.realpath);
+
+const statMethods = { stat, lstat };
+
+/**
+ * @typedef {String} Path
+ */
+
+/**
+ * @typedef {Object} FsEventsWatchContainer
+ * @property {Set} listeners
+ * @property {Function} rawEmitter
+ * @property {{stop: Function}} watcher
+ */
+
+// fsevents instance helper functions
+/**
+ * Object to hold per-process fsevents instances (may be shared across chokidar FSWatcher instances)
+ * @type {Map}
+ */
+const FSEventsWatchers = new Map();
+
+// Threshold of duplicate path prefixes at which to start
+// consolidating going forward
+const consolidateThreshhold = 10;
+
+const wrongEventFlags = new Set([
+ 69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912
+]);
+
+/**
+ * Instantiates the fsevents interface
+ * @param {Path} path path to be watched
+ * @param {Function} callback called when fsevents is bound and ready
+ * @returns {{stop: Function}} new fsevents instance
+ */
+const createFSEventsInstance = (path, callback) => {
+ const stop = fsevents.watch(path, callback);
+ return {stop};
+};
+
+/**
+ * Instantiates the fsevents interface or binds listeners to an existing one covering
+ * the same file tree.
+ * @param {Path} path - to be watched
+ * @param {Path} realPath - real path for symlinks
+ * @param {Function} listener - called when fsevents emits events
+ * @param {Function} rawEmitter - passes data to listeners of the 'raw' event
+ * @returns {Function} closer
+ */
+function setFSEventsListener(path, realPath, listener, rawEmitter) {
+ let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath;
+
+ const parentPath = sysPath.dirname(watchPath);
+ let cont = FSEventsWatchers.get(watchPath);
+
+ // If we've accumulated a substantial number of paths that
+ // could have been consolidated by watching one directory
+ // above the current one, create a watcher on the parent
+ // path instead, so that we do consolidate going forward.
+ if (couldConsolidate(parentPath)) {
+ watchPath = parentPath;
+ }
+
+ const resolvedPath = sysPath.resolve(path);
+ const hasSymlink = resolvedPath !== realPath;
+
+ const filteredListener = (fullPath, flags, info) => {
+ if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath);
+ if (
+ fullPath === resolvedPath ||
+ !fullPath.indexOf(resolvedPath + sysPath.sep)
+ ) listener(fullPath, flags, info);
+ };
+
+ // check if there is already a watcher on a parent path
+ // modifies `watchPath` to the parent path when it finds a match
+ let watchedParent = false;
+ for (const watchedPath of FSEventsWatchers.keys()) {
+ if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) {
+ watchPath = watchedPath;
+ cont = FSEventsWatchers.get(watchPath);
+ watchedParent = true;
+ break;
+ }
+ }
+
+ if (cont || watchedParent) {
+ cont.listeners.add(filteredListener);
+ } else {
+ cont = {
+ listeners: new Set([filteredListener]),
+ rawEmitter,
+ watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
+ if (!cont.listeners.size) return;
+ const info = fsevents.getInfo(fullPath, flags);
+ cont.listeners.forEach(list => {
+ list(fullPath, flags, info);
+ });
+
+ cont.rawEmitter(info.event, fullPath, info);
+ })
+ };
+ FSEventsWatchers.set(watchPath, cont);
+ }
+
+ // removes this instance's listeners and closes the underlying fsevents
+ // instance if there are no more listeners left
+ return () => {
+ const lst = cont.listeners;
+
+ lst.delete(filteredListener);
+ if (!lst.size) {
+ FSEventsWatchers.delete(watchPath);
+ if (cont.watcher) return cont.watcher.stop().then(() => {
+ cont.rawEmitter = cont.watcher = undefined;
+ Object.freeze(cont);
+ });
+ }
+ };
+}
+
+// Decide whether or not we should start a new higher-level
+// parent watcher
+const couldConsolidate = (path) => {
+ let count = 0;
+ for (const watchPath of FSEventsWatchers.keys()) {
+ if (watchPath.indexOf(path) === 0) {
+ count++;
+ if (count >= consolidateThreshhold) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+};
+
+// returns boolean indicating whether fsevents can be used
+const canUse = () => fsevents && FSEventsWatchers.size < 128;
+
+// determines subdirectory traversal levels from root to path
+const calcDepth = (path, root) => {
+ let i = 0;
+ while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++;
+ return i;
+};
+
+// returns boolean indicating whether the fsevents' event info has the same type
+// as the one returned by fs.stat
+const sameTypes = (info, stats) => (
+ info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() ||
+ info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() ||
+ info.type === FSEVENT_TYPE_FILE && stats.isFile()
+)
+
+/**
+ * @mixin
+ */
+class FsEventsHandler {
+
+/**
+ * @param {import('../index').FSWatcher} fsw
+ */
+constructor(fsw) {
+ this.fsw = fsw;
+}
+checkIgnored(path, stats) {
+ const ipaths = this.fsw._ignoredPaths;
+ if (this.fsw._isIgnored(path, stats)) {
+ ipaths.add(path);
+ if (stats && stats.isDirectory()) {
+ ipaths.add(path + ROOT_GLOBSTAR);
+ }
+ return true;
+ }
+
+ ipaths.delete(path);
+ ipaths.delete(path + ROOT_GLOBSTAR);
+}
+
+addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
+ const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD;
+ this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts);
+}
+
+async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
+ try {
+ const stats = await stat(path)
+ if (this.fsw.closed) return;
+ if (sameTypes(info, stats)) {
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ } else {
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ }
+ } catch (error) {
+ if (error.code === 'EACCES') {
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ } else {
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ }
+ }
+}
+
+handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts) {
+ if (this.fsw.closed || this.checkIgnored(path)) return;
+
+ if (event === EV_UNLINK) {
+ const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY
+ // suppress unlink events on never before seen files
+ if (isDirectory || watchedDir.has(item)) {
+ this.fsw._remove(parent, item, isDirectory);
+ }
+ } else {
+ if (event === EV_ADD) {
+ // track new directories
+ if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path);
+
+ if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) {
+ // push symlinks back to the top of the stack to get handled
+ const curDepth = opts.depth === undefined ?
+ undefined : calcDepth(fullPath, realPath) + 1;
+ return this._addToFsEvents(path, false, true, curDepth);
+ }
+
+ // track new paths
+ // (other than symlinks being followed, which will be tracked soon)
+ this.fsw._getWatchedDir(parent).add(item);
+ }
+ /**
+ * @type {'add'|'addDir'|'unlink'|'unlinkDir'}
+ */
+ const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event;
+ this.fsw._emit(eventName, path);
+ if (eventName === EV_ADD_DIR) this._addToFsEvents(path, false, true);
+ }
+}
+
+/**
+ * Handle symlinks encountered during directory scan
+ * @param {String} watchPath - file/dir path to be watched with fsevents
+ * @param {String} realPath - real path (in case of symlinks)
+ * @param {Function} transform - path transformer
+ * @param {Function} globFilter - path filter in case a glob pattern was provided
+ * @returns {Function} closer for the watcher instance
+*/
+_watchWithFsEvents(watchPath, realPath, transform, globFilter) {
+ if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return;
+ const opts = this.fsw.options;
+ const watchCallback = async (fullPath, flags, info) => {
+ if (this.fsw.closed) return;
+ if (
+ opts.depth !== undefined &&
+ calcDepth(fullPath, realPath) > opts.depth
+ ) return;
+ const path = transform(sysPath.join(
+ watchPath, sysPath.relative(watchPath, fullPath)
+ ));
+ if (globFilter && !globFilter(path)) return;
+ // ensure directories are tracked
+ const parent = sysPath.dirname(path);
+ const item = sysPath.basename(path);
+ const watchedDir = this.fsw._getWatchedDir(
+ info.type === FSEVENT_TYPE_DIRECTORY ? path : parent
+ );
+
+ // correct for wrong events emitted
+ if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) {
+ if (typeof opts.ignored === FUNCTION_TYPE) {
+ let stats;
+ try {
+ stats = await stat(path);
+ } catch (error) {}
+ if (this.fsw.closed) return;
+ if (this.checkIgnored(path, stats)) return;
+ if (sameTypes(info, stats)) {
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ } else {
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ }
+ } else {
+ this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ }
+ } else {
+ switch (info.event) {
+ case FSEVENT_CREATED:
+ case FSEVENT_MODIFIED:
+ return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ case FSEVENT_DELETED:
+ case FSEVENT_MOVED:
+ return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
+ }
+ }
+ };
+
+ const closer = setFSEventsListener(
+ watchPath,
+ realPath,
+ watchCallback,
+ this.fsw._emitRaw
+ );
+
+ this.fsw._emitReady();
+ return closer;
+}
+
+/**
+ * Handle symlinks encountered during directory scan
+ * @param {String} linkPath path to symlink
+ * @param {String} fullPath absolute path to the symlink
+ * @param {Function} transform pre-existing path transformer
+ * @param {Number} curDepth level of subdirectories traversed to where symlink is
+ * @returns {Promise}
+ */
+async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) {
+ // don't follow the same symlink more than once
+ if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return;
+
+ this.fsw._symlinkPaths.set(fullPath, true);
+ this.fsw._incrReadyCount();
+
+ try {
+ const linkTarget = await realpath(linkPath);
+ if (this.fsw.closed) return;
+ if (this.fsw._isIgnored(linkTarget)) {
+ return this.fsw._emitReady();
+ }
+
+ this.fsw._incrReadyCount();
+
+ // add the linkTarget for watching with a wrapper for transform
+ // that causes emitted paths to incorporate the link's path
+ this._addToFsEvents(linkTarget || linkPath, (path) => {
+ let aliasedPath = linkPath;
+ if (linkTarget && linkTarget !== DOT_SLASH) {
+ aliasedPath = path.replace(linkTarget, linkPath);
+ } else if (path !== DOT_SLASH) {
+ aliasedPath = sysPath.join(linkPath, path);
+ }
+ return transform(aliasedPath);
+ }, false, curDepth);
+ } catch(error) {
+ if (this.fsw._handleError(error)) {
+ return this.fsw._emitReady();
+ }
+ }
+}
+
+/**
+ *
+ * @param {Path} newPath
+ * @param {fs.Stats} stats
+ */
+emitAdd(newPath, stats, processPath, opts, forceAdd) {
+ const pp = processPath(newPath);
+ const isDir = stats.isDirectory();
+ const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp));
+ const base = sysPath.basename(pp);
+
+ // ensure empty dirs get tracked
+ if (isDir) this.fsw._getWatchedDir(pp);
+ if (dirObj.has(base)) return;
+ dirObj.add(base);
+
+ if (!opts.ignoreInitial || forceAdd === true) {
+ this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats);
+ }
+}
+
+initWatch(realPath, path, wh, processPath) {
+ if (this.fsw.closed) return;
+ const closer = this._watchWithFsEvents(
+ wh.watchPath,
+ sysPath.resolve(realPath || wh.watchPath),
+ processPath,
+ wh.globFilter
+ );
+ this.fsw._addPathCloser(path, closer);
+}
+
+/**
+ * Handle added path with fsevents
+ * @param {String} path file/dir path or glob pattern
+ * @param {Function|Boolean=} transform converts working path to what the user expects
+ * @param {Boolean=} forceAdd ensure add is emitted
+ * @param {Number=} priorDepth Level of subdirectories already traversed.
+ * @returns {Promise}
+ */
+async _addToFsEvents(path, transform, forceAdd, priorDepth) {
+ if (this.fsw.closed) {
+ return;
+ }
+ const opts = this.fsw.options;
+ const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN;
+
+ const wh = this.fsw._getWatchHelpers(path);
+
+ // evaluate what is at the path we're being asked to watch
+ try {
+ const stats = await statMethods[wh.statMethod](wh.watchPath);
+ if (this.fsw.closed) return;
+ if (this.fsw._isIgnored(wh.watchPath, stats)) {
+ throw null;
+ }
+ if (stats.isDirectory()) {
+ // emit addDir unless this is a glob parent
+ if (!wh.globFilter) this.emitAdd(processPath(path), stats, processPath, opts, forceAdd);
+
+ // don't recurse further if it would exceed depth setting
+ if (priorDepth && priorDepth > opts.depth) return;
+
+ // scan the contents of the dir
+ this.fsw._readdirp(wh.watchPath, {
+ fileFilter: entry => wh.filterPath(entry),
+ directoryFilter: entry => wh.filterDir(entry),
+ ...Depth(opts.depth - (priorDepth || 0))
+ }).on(STR_DATA, (entry) => {
+ // need to check filterPath on dirs b/c filterDir is less restrictive
+ if (this.fsw.closed) {
+ return;
+ }
+ if (entry.stats.isDirectory() && !wh.filterPath(entry)) return;
+
+ const joinedPath = sysPath.join(wh.watchPath, entry.path);
+ const {fullPath} = entry;
+
+ if (wh.followSymlinks && entry.stats.isSymbolicLink()) {
+ // preserve the current depth here since it can't be derived from
+ // real paths past the symlink
+ const curDepth = opts.depth === undefined ?
+ undefined : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1;
+
+ this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth);
+ } else {
+ this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd);
+ }
+ }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => {
+ this.fsw._emitReady();
+ });
+ } else {
+ this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd);
+ this.fsw._emitReady();
+ }
+ } catch (error) {
+ if (!error || this.fsw._handleError(error)) {
+ // TODO: Strange thing: "should not choke on an ignored watch path" will be failed without 2 ready calls -__-
+ this.fsw._emitReady();
+ this.fsw._emitReady();
+ }
+ }
+
+ if (opts.persistent && forceAdd !== true) {
+ if (typeof transform === FUNCTION_TYPE) {
+ // realpath has already been resolved
+ this.initWatch(undefined, path, wh, processPath);
+ } else {
+ let realPath;
+ try {
+ realPath = await realpath(wh.watchPath);
+ } catch (e) {}
+ this.initWatch(realPath, path, wh, processPath);
+ }
+ }
+}
+
+}
+
+module.exports = FsEventsHandler;
+module.exports.canUse = canUse;
diff --git a/node_modules/chokidar/lib/nodefs-handler.js b/node_modules/chokidar/lib/nodefs-handler.js
new file mode 100644
index 00000000..3a396430
--- /dev/null
+++ b/node_modules/chokidar/lib/nodefs-handler.js
@@ -0,0 +1,646 @@
+'use strict';
+
+const fs = require('fs');
+const sysPath = require('path');
+const { promisify } = require('util');
+const isBinaryPath = require('is-binary-path');
+const {
+ isWindows,
+ isLinux,
+ EMPTY_FN,
+ EMPTY_STR,
+ KEY_LISTENERS,
+ KEY_ERR,
+ KEY_RAW,
+ HANDLER_KEYS,
+ EV_CHANGE,
+ EV_ADD,
+ EV_ADD_DIR,
+ EV_ERROR,
+ STR_DATA,
+ STR_END,
+ BRACE_START,
+ STAR
+} = require('./constants');
+
+const THROTTLE_MODE_WATCH = 'watch';
+
+const open = promisify(fs.open);
+const stat = promisify(fs.stat);
+const lstat = promisify(fs.lstat);
+const close = promisify(fs.close);
+const fsrealpath = promisify(fs.realpath);
+
+const statMethods = { lstat, stat };
+
+// TODO: emit errors properly. Example: EMFILE on Macos.
+const foreach = (val, fn) => {
+ if (val instanceof Set) {
+ val.forEach(fn);
+ } else {
+ fn(val);
+ }
+};
+
+const addAndConvert = (main, prop, item) => {
+ let container = main[prop];
+ if (!(container instanceof Set)) {
+ main[prop] = container = new Set([container]);
+ }
+ container.add(item);
+};
+
+const clearItem = cont => key => {
+ const set = cont[key];
+ if (set instanceof Set) {
+ set.clear();
+ } else {
+ delete cont[key];
+ }
+};
+
+const delFromSet = (main, prop, item) => {
+ const container = main[prop];
+ if (container instanceof Set) {
+ container.delete(item);
+ } else if (container === item) {
+ delete main[prop];
+ }
+};
+
+const isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
+
+/**
+ * @typedef {String} Path
+ */
+
+// fs_watch helpers
+
+// object to hold per-process fs_watch instances
+// (may be shared across chokidar FSWatcher instances)
+
+/**
+ * @typedef {Object} FsWatchContainer
+ * @property {Set} listeners
+ * @property {Set} errHandlers
+ * @property {Set} rawEmitters
+ * @property {fs.FSWatcher=} watcher
+ * @property {Boolean=} watcherUnusable
+ */
+
+/**
+ * @type {Map}
+ */
+const FsWatchInstances = new Map();
+
+/**
+ * Instantiates the fs_watch interface
+ * @param {String} path to be watched
+ * @param {Object} options to be passed to fs_watch
+ * @param {Function} listener main event handler
+ * @param {Function} errHandler emits info about errors
+ * @param {Function} emitRaw emits raw event data
+ * @returns {fs.FSWatcher} new fsevents instance
+ */
+function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
+ const handleEvent = (rawEvent, evPath) => {
+ listener(path);
+ emitRaw(rawEvent, evPath, {watchedPath: path});
+
+ // emit based on events occurring for files from a directory's watcher in
+ // case the file's watcher misses it (and rely on throttling to de-dupe)
+ if (evPath && path !== evPath) {
+ fsWatchBroadcast(
+ sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath)
+ );
+ }
+ };
+ try {
+ return fs.watch(path, options, handleEvent);
+ } catch (error) {
+ errHandler(error);
+ }
+}
+
+/**
+ * Helper for passing fs_watch event data to a collection of listeners
+ * @param {Path} fullPath absolute path bound to fs_watch instance
+ * @param {String} type listener type
+ * @param {*=} val1 arguments to be passed to listeners
+ * @param {*=} val2
+ * @param {*=} val3
+ */
+const fsWatchBroadcast = (fullPath, type, val1, val2, val3) => {
+ const cont = FsWatchInstances.get(fullPath);
+ if (!cont) return;
+ foreach(cont[type], (listener) => {
+ listener(val1, val2, val3);
+ });
+};
+
+/**
+ * Instantiates the fs_watch interface or binds listeners
+ * to an existing one covering the same file system entry
+ * @param {String} path
+ * @param {String} fullPath absolute path
+ * @param {Object} options to be passed to fs_watch
+ * @param {Object} handlers container for event listener functions
+ */
+const setFsWatchListener = (path, fullPath, options, handlers) => {
+ const {listener, errHandler, rawEmitter} = handlers;
+ let cont = FsWatchInstances.get(fullPath);
+
+ /** @type {fs.FSWatcher=} */
+ let watcher;
+ if (!options.persistent) {
+ watcher = createFsWatchInstance(
+ path, options, listener, errHandler, rawEmitter
+ );
+ return watcher.close.bind(watcher);
+ }
+ if (cont) {
+ addAndConvert(cont, KEY_LISTENERS, listener);
+ addAndConvert(cont, KEY_ERR, errHandler);
+ addAndConvert(cont, KEY_RAW, rawEmitter);
+ } else {
+ watcher = createFsWatchInstance(
+ path,
+ options,
+ fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
+ errHandler, // no need to use broadcast here
+ fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
+ );
+ if (!watcher) return;
+ watcher.on(EV_ERROR, async (error) => {
+ const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
+ cont.watcherUnusable = true; // documented since Node 10.4.1
+ // Workaround for https://github.com/joyent/node/issues/4337
+ if (isWindows && error.code === 'EPERM') {
+ try {
+ const fd = await open(path, 'r');
+ await close(fd);
+ broadcastErr(error);
+ } catch (err) {}
+ } else {
+ broadcastErr(error);
+ }
+ });
+ cont = {
+ listeners: listener,
+ errHandlers: errHandler,
+ rawEmitters: rawEmitter,
+ watcher
+ };
+ FsWatchInstances.set(fullPath, cont);
+ }
+ // const index = cont.listeners.indexOf(listener);
+
+ // removes this instance's listeners and closes the underlying fs_watch
+ // instance if there are no more listeners left
+ return () => {
+ delFromSet(cont, KEY_LISTENERS, listener);
+ delFromSet(cont, KEY_ERR, errHandler);
+ delFromSet(cont, KEY_RAW, rawEmitter);
+ if (isEmptySet(cont.listeners)) {
+ // Check to protect against issue gh-730.
+ // if (cont.watcherUnusable) {
+ cont.watcher.close();
+ // }
+ FsWatchInstances.delete(fullPath);
+ HANDLER_KEYS.forEach(clearItem(cont));
+ cont.watcher = undefined;
+ Object.freeze(cont);
+ }
+ };
+};
+
+// fs_watchFile helpers
+
+// object to hold per-process fs_watchFile instances
+// (may be shared across chokidar FSWatcher instances)
+const FsWatchFileInstances = new Map();
+
+/**
+ * Instantiates the fs_watchFile interface or binds listeners
+ * to an existing one covering the same file system entry
+ * @param {String} path to be watched
+ * @param {String} fullPath absolute path
+ * @param {Object} options options to be passed to fs_watchFile
+ * @param {Object} handlers container for event listener functions
+ * @returns {Function} closer
+ */
+const setFsWatchFileListener = (path, fullPath, options, handlers) => {
+ const {listener, rawEmitter} = handlers;
+ let cont = FsWatchFileInstances.get(fullPath);
+
+ /* eslint-disable no-unused-vars, prefer-destructuring */
+ let listeners = new Set();
+ let rawEmitters = new Set();
+
+ const copts = cont && cont.options;
+ if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
+ // "Upgrade" the watcher to persistence or a quicker interval.
+ // This creates some unlikely edge case issues if the user mixes
+ // settings in a very weird way, but solving for those cases
+ // doesn't seem worthwhile for the added complexity.
+ listeners = cont.listeners;
+ rawEmitters = cont.rawEmitters;
+ fs.unwatchFile(fullPath);
+ cont = undefined;
+ }
+
+ /* eslint-enable no-unused-vars, prefer-destructuring */
+
+ if (cont) {
+ addAndConvert(cont, KEY_LISTENERS, listener);
+ addAndConvert(cont, KEY_RAW, rawEmitter);
+ } else {
+ // TODO
+ // listeners.add(listener);
+ // rawEmitters.add(rawEmitter);
+ cont = {
+ listeners: listener,
+ rawEmitters: rawEmitter,
+ options,
+ watcher: fs.watchFile(fullPath, options, (curr, prev) => {
+ foreach(cont.rawEmitters, (rawEmitter) => {
+ rawEmitter(EV_CHANGE, fullPath, {curr, prev});
+ });
+ const currmtime = curr.mtimeMs;
+ if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
+ foreach(cont.listeners, (listener) => listener(path, curr));
+ }
+ })
+ };
+ FsWatchFileInstances.set(fullPath, cont);
+ }
+ // const index = cont.listeners.indexOf(listener);
+
+ // Removes this instance's listeners and closes the underlying fs_watchFile
+ // instance if there are no more listeners left.
+ return () => {
+ delFromSet(cont, KEY_LISTENERS, listener);
+ delFromSet(cont, KEY_RAW, rawEmitter);
+ if (isEmptySet(cont.listeners)) {
+ FsWatchFileInstances.delete(fullPath);
+ fs.unwatchFile(fullPath);
+ cont.options = cont.watcher = undefined;
+ Object.freeze(cont);
+ }
+ };
+};
+
+/**
+ * @mixin
+ */
+class NodeFsHandler {
+
+/**
+ * @param {import("../index").FSWatcher} fsW
+ */
+constructor(fsW) {
+ this.fsw = fsW;
+ this._boundHandleError = (error) => fsW._handleError(error);
+}
+
+/**
+ * Watch file for changes with fs_watchFile or fs_watch.
+ * @param {String} path to file or dir
+ * @param {Function} listener on fs change
+ * @returns {Function} closer for the watcher instance
+ */
+_watchWithNodeFs(path, listener) {
+ const opts = this.fsw.options;
+ const directory = sysPath.dirname(path);
+ const basename = sysPath.basename(path);
+ const parent = this.fsw._getWatchedDir(directory);
+ parent.add(basename);
+ const absolutePath = sysPath.resolve(path);
+ const options = {persistent: opts.persistent};
+ if (!listener) listener = EMPTY_FN;
+
+ let closer;
+ if (opts.usePolling) {
+ options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ?
+ opts.binaryInterval : opts.interval;
+ closer = setFsWatchFileListener(path, absolutePath, options, {
+ listener,
+ rawEmitter: this.fsw._emitRaw
+ });
+ } else {
+ closer = setFsWatchListener(path, absolutePath, options, {
+ listener,
+ errHandler: this._boundHandleError,
+ rawEmitter: this.fsw._emitRaw
+ });
+ }
+ return closer;
+}
+
+/**
+ * Watch a file and emit add event if warranted.
+ * @param {Path} file Path
+ * @param {fs.Stats} stats result of fs_stat
+ * @param {Boolean} initialAdd was the file added at watch instantiation?
+ * @returns {Function} closer for the watcher instance
+ */
+_handleFile(file, stats, initialAdd) {
+ if (this.fsw.closed) {
+ return;
+ }
+ const dirname = sysPath.dirname(file);
+ const basename = sysPath.basename(file);
+ const parent = this.fsw._getWatchedDir(dirname);
+ // stats is always present
+ let prevStats = stats;
+
+ // if the file is already being watched, do nothing
+ if (parent.has(basename)) return;
+
+ const listener = async (path, newStats) => {
+ if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return;
+ if (!newStats || newStats.mtimeMs === 0) {
+ try {
+ const newStats = await stat(file);
+ if (this.fsw.closed) return;
+ // Check that change event was not fired because of changed only accessTime.
+ const at = newStats.atimeMs;
+ const mt = newStats.mtimeMs;
+ if (!at || at <= mt || mt !== prevStats.mtimeMs) {
+ this.fsw._emit(EV_CHANGE, file, newStats);
+ }
+ if (isLinux && prevStats.ino !== newStats.ino) {
+ this.fsw._closeFile(path)
+ prevStats = newStats;
+ this.fsw._addPathCloser(path, this._watchWithNodeFs(file, listener));
+ } else {
+ prevStats = newStats;
+ }
+ } catch (error) {
+ // Fix issues where mtime is null but file is still present
+ this.fsw._remove(dirname, basename);
+ }
+ // add is about to be emitted if file not already tracked in parent
+ } else if (parent.has(basename)) {
+ // Check that change event was not fired because of changed only accessTime.
+ const at = newStats.atimeMs;
+ const mt = newStats.mtimeMs;
+ if (!at || at <= mt || mt !== prevStats.mtimeMs) {
+ this.fsw._emit(EV_CHANGE, file, newStats);
+ }
+ prevStats = newStats;
+ }
+ }
+ // kick off the watcher
+ const closer = this._watchWithNodeFs(file, listener);
+
+ // emit an add event if we're supposed to
+ if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
+ if (!this.fsw._throttle(EV_ADD, file, 0)) return;
+ this.fsw._emit(EV_ADD, file, stats);
+ }
+
+ return closer;
+}
+
+/**
+ * Handle symlinks encountered while reading a dir.
+ * @param {Object} entry returned by readdirp
+ * @param {String} directory path of dir being read
+ * @param {String} path of this item
+ * @param {String} item basename of this item
+ * @returns {Promise} true if no more processing is needed for this entry.
+ */
+async _handleSymlink(entry, directory, path, item) {
+ if (this.fsw.closed) {
+ return;
+ }
+ const full = entry.fullPath;
+ const dir = this.fsw._getWatchedDir(directory);
+
+ if (!this.fsw.options.followSymlinks) {
+ // watch symlink directly (don't follow) and detect changes
+ this.fsw._incrReadyCount();
+ const linkPath = await fsrealpath(path);
+ if (this.fsw.closed) return;
+ if (dir.has(item)) {
+ if (this.fsw._symlinkPaths.get(full) !== linkPath) {
+ this.fsw._symlinkPaths.set(full, linkPath);
+ this.fsw._emit(EV_CHANGE, path, entry.stats);
+ }
+ } else {
+ dir.add(item);
+ this.fsw._symlinkPaths.set(full, linkPath);
+ this.fsw._emit(EV_ADD, path, entry.stats);
+ }
+ this.fsw._emitReady();
+ return true;
+ }
+
+ // don't follow the same symlink more than once
+ if (this.fsw._symlinkPaths.has(full)) {
+ return true;
+ }
+
+ this.fsw._symlinkPaths.set(full, true);
+}
+
+_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
+ // Normalize the directory name on Windows
+ directory = sysPath.join(directory, EMPTY_STR);
+
+ if (!wh.hasGlob) {
+ throttler = this.fsw._throttle('readdir', directory, 1000);
+ if (!throttler) return;
+ }
+
+ const previous = this.fsw._getWatchedDir(wh.path);
+ const current = new Set();
+
+ let stream = this.fsw._readdirp(directory, {
+ fileFilter: entry => wh.filterPath(entry),
+ directoryFilter: entry => wh.filterDir(entry),
+ depth: 0
+ }).on(STR_DATA, async (entry) => {
+ if (this.fsw.closed) {
+ stream = undefined;
+ return;
+ }
+ const item = entry.path;
+ let path = sysPath.join(directory, item);
+ current.add(item);
+
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
+ return;
+ }
+
+ if (this.fsw.closed) {
+ stream = undefined;
+ return;
+ }
+ // Files that present in current directory snapshot
+ // but absent in previous are added to watch list and
+ // emit `add` event.
+ if (item === target || !target && !previous.has(item)) {
+ this.fsw._incrReadyCount();
+
+ // ensure relativeness of path is preserved in case of watcher reuse
+ path = sysPath.join(dir, sysPath.relative(dir, path));
+
+ this._addToNodeFs(path, initialAdd, wh, depth + 1);
+ }
+ }).on(EV_ERROR, this._boundHandleError);
+
+ return new Promise(resolve =>
+ stream.once(STR_END, () => {
+ if (this.fsw.closed) {
+ stream = undefined;
+ return;
+ }
+ const wasThrottled = throttler ? throttler.clear() : false;
+
+ resolve();
+
+ // Files that absent in current directory snapshot
+ // but present in previous emit `remove` event
+ // and are removed from @watched[directory].
+ previous.getChildren().filter((item) => {
+ return item !== directory &&
+ !current.has(item) &&
+ // in case of intersecting globs;
+ // a path may have been filtered out of this readdir, but
+ // shouldn't be removed because it matches a different glob
+ (!wh.hasGlob || wh.filterPath({
+ fullPath: sysPath.resolve(directory, item)
+ }));
+ }).forEach((item) => {
+ this.fsw._remove(directory, item);
+ });
+
+ stream = undefined;
+
+ // one more time for any missed in case changes came in extremely quickly
+ if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler);
+ })
+ );
+}
+
+/**
+ * Read directory to add / remove files from `@watched` list and re-read it on change.
+ * @param {String} dir fs path
+ * @param {fs.Stats} stats
+ * @param {Boolean} initialAdd
+ * @param {Number} depth relative to user-supplied path
+ * @param {String} target child path targeted for watch
+ * @param {Object} wh Common watch helpers for this path
+ * @param {String} realpath
+ * @returns {Promise} closer for the watcher instance.
+ */
+async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
+ const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
+ const tracked = parentDir.has(sysPath.basename(dir));
+ if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
+ if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats);
+ }
+
+ // ensure dir is tracked (harmless if redundant)
+ parentDir.add(sysPath.basename(dir));
+ this.fsw._getWatchedDir(dir);
+ let throttler;
+ let closer;
+
+ const oDepth = this.fsw.options.depth;
+ if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
+ if (!target) {
+ await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
+ if (this.fsw.closed) return;
+ }
+
+ closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
+ // if current directory is removed, do nothing
+ if (stats && stats.mtimeMs === 0) return;
+
+ this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
+ });
+ }
+ return closer;
+}
+
+/**
+ * Handle added file, directory, or glob pattern.
+ * Delegates call to _handleFile / _handleDir after checks.
+ * @param {String} path to file or ir
+ * @param {Boolean} initialAdd was the file added at watch instantiation?
+ * @param {Object} priorWh depth relative to user-supplied path
+ * @param {Number} depth Child path actually targeted for watch
+ * @param {String=} target Child path actually targeted for watch
+ * @returns {Promise}
+ */
+async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
+ const ready = this.fsw._emitReady;
+ if (this.fsw._isIgnored(path) || this.fsw.closed) {
+ ready();
+ return false;
+ }
+
+ const wh = this.fsw._getWatchHelpers(path, depth);
+ if (!wh.hasGlob && priorWh) {
+ wh.hasGlob = priorWh.hasGlob;
+ wh.globFilter = priorWh.globFilter;
+ wh.filterPath = entry => priorWh.filterPath(entry);
+ wh.filterDir = entry => priorWh.filterDir(entry);
+ }
+
+ // evaluate what is at the path we're being asked to watch
+ try {
+ const stats = await statMethods[wh.statMethod](wh.watchPath);
+ if (this.fsw.closed) return;
+ if (this.fsw._isIgnored(wh.watchPath, stats)) {
+ ready();
+ return false;
+ }
+
+ const follow = this.fsw.options.followSymlinks && !path.includes(STAR) && !path.includes(BRACE_START);
+ let closer;
+ if (stats.isDirectory()) {
+ const absPath = sysPath.resolve(path);
+ const targetPath = follow ? await fsrealpath(path) : path;
+ if (this.fsw.closed) return;
+ closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
+ if (this.fsw.closed) return;
+ // preserve this symlink's target path
+ if (absPath !== targetPath && targetPath !== undefined) {
+ this.fsw._symlinkPaths.set(absPath, targetPath);
+ }
+ } else if (stats.isSymbolicLink()) {
+ const targetPath = follow ? await fsrealpath(path) : path;
+ if (this.fsw.closed) return;
+ const parent = sysPath.dirname(wh.watchPath);
+ this.fsw._getWatchedDir(parent).add(wh.watchPath);
+ this.fsw._emit(EV_ADD, wh.watchPath, stats);
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
+ if (this.fsw.closed) return;
+
+ // preserve this symlink's target path
+ if (targetPath !== undefined) {
+ this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
+ }
+ } else {
+ closer = this._handleFile(wh.watchPath, stats, initialAdd);
+ }
+ ready();
+
+ this.fsw._addPathCloser(path, closer);
+ return false;
+
+ } catch (error) {
+ if (this.fsw._handleError(error)) {
+ ready();
+ return path;
+ }
+ }
+}
+
+}
+
+module.exports = NodeFsHandler;
diff --git a/node_modules/chokidar/package.json b/node_modules/chokidar/package.json
new file mode 100644
index 00000000..f81f86a1
--- /dev/null
+++ b/node_modules/chokidar/package.json
@@ -0,0 +1,115 @@
+{
+ "_args": [
+ [
+ "chokidar@3.5.2",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "chokidar@3.5.2",
+ "_id": "chokidar@3.5.2",
+ "_inBundle": false,
+ "_integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "_location": "/chokidar",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "chokidar@3.5.2",
+ "name": "chokidar",
+ "escapedName": "chokidar",
+ "rawSpec": "3.5.2",
+ "saveSpec": null,
+ "fetchSpec": "3.5.2"
+ },
+ "_requiredBy": [
+ "/sass"
+ ],
+ "_resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+ "_spec": "3.5.2",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Paul Miller",
+ "url": "https://paulmillr.com"
+ },
+ "bugs": {
+ "url": "https://github.com/paulmillr/chokidar/issues"
+ },
+ "contributors": [
+ {
+ "name": "Paul Miller",
+ "url": "https://paulmillr.com"
+ },
+ {
+ "name": "Elan Shanker"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "description": "Minimal and efficient cross-platform file watching library",
+ "devDependencies": {
+ "@types/node": "^14",
+ "chai": "^4.3",
+ "dtslint": "^3.3.0",
+ "eslint": "^7.0.0",
+ "mocha": "^7.0.0",
+ "nyc": "^15.0.0",
+ "rimraf": "^3.0.0",
+ "sinon": "^9.0.1",
+ "sinon-chai": "^3.3.0",
+ "upath": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "files": [
+ "index.js",
+ "lib/*.js",
+ "types/index.d.ts"
+ ],
+ "homepage": "https://github.com/paulmillr/chokidar",
+ "keywords": [
+ "fs",
+ "watch",
+ "watchFile",
+ "watcher",
+ "watching",
+ "file",
+ "fsevents"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "chokidar",
+ "nyc": {
+ "include": [
+ "index.js",
+ "lib/*.js"
+ ],
+ "reporter": [
+ "html",
+ "text"
+ ]
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/paulmillr/chokidar.git"
+ },
+ "scripts": {
+ "dtslint": "dtslint types",
+ "lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
+ "mocha": "mocha --exit --timeout 90000",
+ "test": "npm run lint && npm run mocha"
+ },
+ "types": "./types/index.d.ts",
+ "version": "3.5.2"
+}
diff --git a/node_modules/chokidar/types/index.d.ts b/node_modules/chokidar/types/index.d.ts
new file mode 100644
index 00000000..af0b5585
--- /dev/null
+++ b/node_modules/chokidar/types/index.d.ts
@@ -0,0 +1,187 @@
+// TypeScript Version: 3.0
+
+///
+
+import * as fs from "fs";
+import { EventEmitter } from "events";
+
+export class FSWatcher extends EventEmitter implements fs.FSWatcher {
+ options: WatchOptions;
+
+ /**
+ * Constructs a new FSWatcher instance with optional WatchOptions parameter.
+ */
+ constructor(options?: WatchOptions);
+
+ /**
+ * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one
+ * string.
+ */
+ add(paths: string | ReadonlyArray): void;
+
+ /**
+ * Stop watching files, directories, or glob patterns. Takes an array of strings or just one
+ * string.
+ */
+ unwatch(paths: string | ReadonlyArray): void;
+
+ /**
+ * Returns an object representing all the paths on the file system being watched by this
+ * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless
+ * the `cwd` option was used), and the values are arrays of the names of the items contained in
+ * each directory.
+ */
+ getWatched(): {
+ [directory: string]: string[];
+ };
+
+ /**
+ * Removes all listeners from watched files.
+ */
+ close(): Promise;
+
+ on(event: 'add'|'addDir'|'change', listener: (path: string, stats?: fs.Stats) => void): this;
+
+ on(event: 'all', listener: (eventName: 'add'|'addDir'|'change'|'unlink'|'unlinkDir', path: string, stats?: fs.Stats) => void): this;
+
+ /**
+ * Error occurred
+ */
+ on(event: 'error', listener: (error: Error) => void): this;
+
+ /**
+ * Exposes the native Node `fs.FSWatcher events`
+ */
+ on(event: 'raw', listener: (eventName: string, path: string, details: any) => void): this;
+
+ /**
+ * Fires when the initial scan is complete
+ */
+ on(event: 'ready', listener: () => void): this;
+
+ on(event: 'unlink'|'unlinkDir', listener: (path: string) => void): this;
+
+ on(event: string, listener: (...args: any[]) => void): this;
+}
+
+export interface WatchOptions {
+ /**
+ * Indicates whether the process should continue to run as long as files are being watched. If
+ * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`,
+ * even if the process continues to run.
+ */
+ persistent?: boolean;
+
+ /**
+ * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to
+ * be ignored. The whole relative or absolute path is tested, not just filename. If a function
+ * with two arguments is provided, it gets called twice per path - once with a single argument
+ * (the path), second time with two arguments (the path and the
+ * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path).
+ */
+ ignored?: any;
+
+ /**
+ * If set to `false` then `add`/`addDir` events are also emitted for matching paths while
+ * instantiating the watching as chokidar discovers these file paths (before the `ready` event).
+ */
+ ignoreInitial?: boolean;
+
+ /**
+ * When `false`, only the symlinks themselves will be watched for changes instead of following
+ * the link references and bubbling events through the link's path.
+ */
+ followSymlinks?: boolean;
+
+ /**
+ * The base directory from which watch `paths` are to be derived. Paths emitted with events will
+ * be relative to this.
+ */
+ cwd?: string;
+
+ /**
+ * If set to true then the strings passed to .watch() and .add() are treated as literal path
+ * names, even if they look like globs. Default: false.
+ */
+ disableGlobbing?: boolean;
+
+ /**
+ * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU
+ * utilization, consider setting this to `false`. It is typically necessary to **set this to
+ * `true` to successfully watch files over a network**, and it may be necessary to successfully
+ * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides
+ * the `useFsEvents` default.
+ */
+ usePolling?: boolean;
+
+ /**
+ * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly
+ * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on
+ * OS X, `usePolling: true` becomes the default.
+ */
+ useFsEvents?: boolean;
+
+ /**
+ * If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that
+ * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is
+ * provided even in cases where it wasn't already available from the underlying watch events.
+ */
+ alwaysStat?: boolean;
+
+ /**
+ * If set, limits how many levels of subdirectories will be traversed.
+ */
+ depth?: number;
+
+ /**
+ * Interval of file system polling.
+ */
+ interval?: number;
+
+ /**
+ * Interval of file system polling for binary files. ([see list of binary extensions](https://gi
+ * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
+ */
+ binaryInterval?: number;
+
+ /**
+ * Indicates whether to watch files that don't have read permissions if possible. If watching
+ * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed
+ * silently.
+ */
+ ignorePermissionErrors?: boolean;
+
+ /**
+ * `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts
+ * that occur when using editors that use "atomic writes" instead of writing directly to the
+ * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change`
+ * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you,
+ * you can override it by setting `atomic` to a custom value, in milliseconds.
+ */
+ atomic?: boolean | number;
+
+ /**
+ * can be set to an object in order to adjust timing params:
+ */
+ awaitWriteFinish?: AwaitWriteFinishOptions | boolean;
+}
+
+export interface AwaitWriteFinishOptions {
+ /**
+ * Amount of time in milliseconds for a file size to remain constant before emitting its event.
+ */
+ stabilityThreshold?: number;
+
+ /**
+ * File size polling interval.
+ */
+ pollInterval?: number;
+}
+
+/**
+ * produces an instance of `FSWatcher`.
+ */
+export function watch(
+ paths: string | ReadonlyArray,
+ options?: WatchOptions
+): FSWatcher;
diff --git a/node_modules/fill-range/LICENSE b/node_modules/fill-range/LICENSE
new file mode 100644
index 00000000..9af4a67d
--- /dev/null
+++ b/node_modules/fill-range/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-present, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/fill-range/README.md b/node_modules/fill-range/README.md
new file mode 100644
index 00000000..8d756fe9
--- /dev/null
+++ b/node_modules/fill-range/README.md
@@ -0,0 +1,237 @@
+# fill-range [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [](https://www.npmjs.com/package/fill-range) [](https://npmjs.org/package/fill-range) [](https://npmjs.org/package/fill-range) [](https://travis-ci.org/jonschlinkert/fill-range)
+
+> Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save fill-range
+```
+
+## Usage
+
+Expands numbers and letters, optionally using a `step` as the last argument. _(Numbers may be defined as JavaScript numbers or strings)_.
+
+```js
+const fill = require('fill-range');
+// fill(from, to[, step, options]);
+
+console.log(fill('1', '10')); //=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
+console.log(fill('1', '10', { toRegex: true })); //=> [1-9]|10
+```
+
+**Params**
+
+* `from`: **{String|Number}** the number or letter to start with
+* `to`: **{String|Number}** the number or letter to end with
+* `step`: **{String|Number|Object|Function}** Optionally pass a [step](#optionsstep) to use.
+* `options`: **{Object|Function}**: See all available [options](#options)
+
+## Examples
+
+By default, an array of values is returned.
+
+**Alphabetical ranges**
+
+```js
+console.log(fill('a', 'e')); //=> ['a', 'b', 'c', 'd', 'e']
+console.log(fill('A', 'E')); //=> [ 'A', 'B', 'C', 'D', 'E' ]
+```
+
+**Numerical ranges**
+
+Numbers can be defined as actual numbers or strings.
+
+```js
+console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
+console.log(fill('1', '5')); //=> [ 1, 2, 3, 4, 5 ]
+```
+
+**Negative ranges**
+
+Numbers can be defined as actual numbers or strings.
+
+```js
+console.log(fill('-5', '-1')); //=> [ '-5', '-4', '-3', '-2', '-1' ]
+console.log(fill('-5', '5')); //=> [ '-5', '-4', '-3', '-2', '-1', '0', '1', '2', '3', '4', '5' ]
+```
+
+**Steps (increments)**
+
+```js
+// numerical ranges with increments
+console.log(fill('0', '25', 4)); //=> [ '0', '4', '8', '12', '16', '20', '24' ]
+console.log(fill('0', '25', 5)); //=> [ '0', '5', '10', '15', '20', '25' ]
+console.log(fill('0', '25', 6)); //=> [ '0', '6', '12', '18', '24' ]
+
+// alphabetical ranges with increments
+console.log(fill('a', 'z', 4)); //=> [ 'a', 'e', 'i', 'm', 'q', 'u', 'y' ]
+console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
+console.log(fill('a', 'z', 6)); //=> [ 'a', 'g', 'm', 's', 'y' ]
+```
+
+## Options
+
+### options.step
+
+**Type**: `number` (formatted as a string or number)
+
+**Default**: `undefined`
+
+**Description**: The increment to use for the range. Can be used with letters or numbers.
+
+**Example(s)**
+
+```js
+// numbers
+console.log(fill('1', '10', 2)); //=> [ '1', '3', '5', '7', '9' ]
+console.log(fill('1', '10', 3)); //=> [ '1', '4', '7', '10' ]
+console.log(fill('1', '10', 4)); //=> [ '1', '5', '9' ]
+
+// letters
+console.log(fill('a', 'z', 5)); //=> [ 'a', 'f', 'k', 'p', 'u', 'z' ]
+console.log(fill('a', 'z', 7)); //=> [ 'a', 'h', 'o', 'v' ]
+console.log(fill('a', 'z', 9)); //=> [ 'a', 'j', 's' ]
+```
+
+### options.strictRanges
+
+**Type**: `boolean`
+
+**Default**: `false`
+
+**Description**: By default, `null` is returned when an invalid range is passed. Enable this option to throw a `RangeError` on invalid ranges.
+
+**Example(s)**
+
+The following are all invalid:
+
+```js
+fill('1.1', '2'); // decimals not supported in ranges
+fill('a', '2'); // incompatible range values
+fill(1, 10, 'foo'); // invalid "step" argument
+```
+
+### options.stringify
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+**Description**: Cast all returned values to strings. By default, integers are returned as numbers.
+
+**Example(s)**
+
+```js
+console.log(fill(1, 5)); //=> [ 1, 2, 3, 4, 5 ]
+console.log(fill(1, 5, { stringify: true })); //=> [ '1', '2', '3', '4', '5' ]
+```
+
+### options.toRegex
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+**Description**: Create a regex-compatible source string, instead of expanding values to an array.
+
+**Example(s)**
+
+```js
+// alphabetical range
+console.log(fill('a', 'e', { toRegex: true })); //=> '[a-e]'
+// alphabetical with step
+console.log(fill('a', 'z', 3, { toRegex: true })); //=> 'a|d|g|j|m|p|s|v|y'
+// numerical range
+console.log(fill('1', '100', { toRegex: true })); //=> '[1-9]|[1-9][0-9]|100'
+// numerical range with zero padding
+console.log(fill('000001', '100000', { toRegex: true }));
+//=> '0{5}[1-9]|0{4}[1-9][0-9]|0{3}[1-9][0-9]{2}|0{2}[1-9][0-9]{3}|0[1-9][0-9]{4}|100000'
+```
+
+### options.transform
+
+**Type**: `function`
+
+**Default**: `undefined`
+
+**Description**: Customize each value in the returned array (or [string](#optionstoRegex)). _(you can also pass this function as the last argument to `fill()`)_.
+
+**Example(s)**
+
+```js
+// add zero padding
+console.log(fill(1, 5, value => String(value).padStart(4, '0')));
+//=> ['0001', '0002', '0003', '0004', '0005']
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 116 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 4 | [paulmillr](https://github.com/paulmillr) |
+| 2 | [realityking](https://github.com/realityking) |
+| 2 | [bluelovers](https://github.com/bluelovers) |
+| 1 | [edorivai](https://github.com/edorivai) |
+| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [GitHub Profile](https://github.com/jonschlinkert)
+* [Twitter Profile](https://twitter.com/jonschlinkert)
+* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
+
+Please consider supporting me on Patreon, or [start your own Patreon page](https://patreon.com/invite/bxpbvm)!
+
+
+
+
+
+### License
+
+Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 08, 2019._
\ No newline at end of file
diff --git a/node_modules/fill-range/index.js b/node_modules/fill-range/index.js
new file mode 100644
index 00000000..97ce35a5
--- /dev/null
+++ b/node_modules/fill-range/index.js
@@ -0,0 +1,249 @@
+/*!
+ * fill-range
+ *
+ * Copyright (c) 2014-present, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+const util = require('util');
+const toRegexRange = require('to-regex-range');
+
+const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
+
+const transform = toNumber => {
+ return value => toNumber === true ? Number(value) : String(value);
+};
+
+const isValidValue = value => {
+ return typeof value === 'number' || (typeof value === 'string' && value !== '');
+};
+
+const isNumber = num => Number.isInteger(+num);
+
+const zeros = input => {
+ let value = `${input}`;
+ let index = -1;
+ if (value[0] === '-') value = value.slice(1);
+ if (value === '0') return false;
+ while (value[++index] === '0');
+ return index > 0;
+};
+
+const stringify = (start, end, options) => {
+ if (typeof start === 'string' || typeof end === 'string') {
+ return true;
+ }
+ return options.stringify === true;
+};
+
+const pad = (input, maxLength, toNumber) => {
+ if (maxLength > 0) {
+ let dash = input[0] === '-' ? '-' : '';
+ if (dash) input = input.slice(1);
+ input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0'));
+ }
+ if (toNumber === false) {
+ return String(input);
+ }
+ return input;
+};
+
+const toMaxLen = (input, maxLength) => {
+ let negative = input[0] === '-' ? '-' : '';
+ if (negative) {
+ input = input.slice(1);
+ maxLength--;
+ }
+ while (input.length < maxLength) input = '0' + input;
+ return negative ? ('-' + input) : input;
+};
+
+const toSequence = (parts, options) => {
+ parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
+ parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
+
+ let prefix = options.capture ? '' : '?:';
+ let positives = '';
+ let negatives = '';
+ let result;
+
+ if (parts.positives.length) {
+ positives = parts.positives.join('|');
+ }
+
+ if (parts.negatives.length) {
+ negatives = `-(${prefix}${parts.negatives.join('|')})`;
+ }
+
+ if (positives && negatives) {
+ result = `${positives}|${negatives}`;
+ } else {
+ result = positives || negatives;
+ }
+
+ if (options.wrap) {
+ return `(${prefix}${result})`;
+ }
+
+ return result;
+};
+
+const toRange = (a, b, isNumbers, options) => {
+ if (isNumbers) {
+ return toRegexRange(a, b, { wrap: false, ...options });
+ }
+
+ let start = String.fromCharCode(a);
+ if (a === b) return start;
+
+ let stop = String.fromCharCode(b);
+ return `[${start}-${stop}]`;
+};
+
+const toRegex = (start, end, options) => {
+ if (Array.isArray(start)) {
+ let wrap = options.wrap === true;
+ let prefix = options.capture ? '' : '?:';
+ return wrap ? `(${prefix}${start.join('|')})` : start.join('|');
+ }
+ return toRegexRange(start, end, options);
+};
+
+const rangeError = (...args) => {
+ return new RangeError('Invalid range arguments: ' + util.inspect(...args));
+};
+
+const invalidRange = (start, end, options) => {
+ if (options.strictRanges === true) throw rangeError([start, end]);
+ return [];
+};
+
+const invalidStep = (step, options) => {
+ if (options.strictRanges === true) {
+ throw new TypeError(`Expected step "${step}" to be a number`);
+ }
+ return [];
+};
+
+const fillNumbers = (start, end, step = 1, options = {}) => {
+ let a = Number(start);
+ let b = Number(end);
+
+ if (!Number.isInteger(a) || !Number.isInteger(b)) {
+ if (options.strictRanges === true) throw rangeError([start, end]);
+ return [];
+ }
+
+ // fix negative zero
+ if (a === 0) a = 0;
+ if (b === 0) b = 0;
+
+ let descending = a > b;
+ let startString = String(start);
+ let endString = String(end);
+ let stepString = String(step);
+ step = Math.max(Math.abs(step), 1);
+
+ let padded = zeros(startString) || zeros(endString) || zeros(stepString);
+ let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
+ let toNumber = padded === false && stringify(start, end, options) === false;
+ let format = options.transform || transform(toNumber);
+
+ if (options.toRegex && step === 1) {
+ return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
+ }
+
+ let parts = { negatives: [], positives: [] };
+ let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
+ let range = [];
+ let index = 0;
+
+ while (descending ? a >= b : a <= b) {
+ if (options.toRegex === true && step > 1) {
+ push(a);
+ } else {
+ range.push(pad(format(a, index), maxLen, toNumber));
+ }
+ a = descending ? a - step : a + step;
+ index++;
+ }
+
+ if (options.toRegex === true) {
+ return step > 1
+ ? toSequence(parts, options)
+ : toRegex(range, null, { wrap: false, ...options });
+ }
+
+ return range;
+};
+
+const fillLetters = (start, end, step = 1, options = {}) => {
+ if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) {
+ return invalidRange(start, end, options);
+ }
+
+
+ let format = options.transform || (val => String.fromCharCode(val));
+ let a = `${start}`.charCodeAt(0);
+ let b = `${end}`.charCodeAt(0);
+
+ let descending = a > b;
+ let min = Math.min(a, b);
+ let max = Math.max(a, b);
+
+ if (options.toRegex && step === 1) {
+ return toRange(min, max, false, options);
+ }
+
+ let range = [];
+ let index = 0;
+
+ while (descending ? a >= b : a <= b) {
+ range.push(format(a, index));
+ a = descending ? a - step : a + step;
+ index++;
+ }
+
+ if (options.toRegex === true) {
+ return toRegex(range, null, { wrap: false, options });
+ }
+
+ return range;
+};
+
+const fill = (start, end, step, options = {}) => {
+ if (end == null && isValidValue(start)) {
+ return [start];
+ }
+
+ if (!isValidValue(start) || !isValidValue(end)) {
+ return invalidRange(start, end, options);
+ }
+
+ if (typeof step === 'function') {
+ return fill(start, end, 1, { transform: step });
+ }
+
+ if (isObject(step)) {
+ return fill(start, end, 0, step);
+ }
+
+ let opts = { ...options };
+ if (opts.capture === true) opts.wrap = true;
+ step = step || opts.step || 1;
+
+ if (!isNumber(step)) {
+ if (step != null && !isObject(step)) return invalidStep(step, opts);
+ return fill(start, end, 1, step);
+ }
+
+ if (isNumber(start) && isNumber(end)) {
+ return fillNumbers(start, end, step, opts);
+ }
+
+ return fillLetters(start, end, Math.max(Math.abs(step), 1), opts);
+};
+
+module.exports = fill;
diff --git a/node_modules/fill-range/package.json b/node_modules/fill-range/package.json
new file mode 100644
index 00000000..2f6f0705
--- /dev/null
+++ b/node_modules/fill-range/package.json
@@ -0,0 +1,117 @@
+{
+ "_args": [
+ [
+ "fill-range@7.0.1",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "fill-range@7.0.1",
+ "_id": "fill-range@7.0.1",
+ "_inBundle": false,
+ "_integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "_location": "/fill-range",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "fill-range@7.0.1",
+ "name": "fill-range",
+ "escapedName": "fill-range",
+ "rawSpec": "7.0.1",
+ "saveSpec": null,
+ "fetchSpec": "7.0.1"
+ },
+ "_requiredBy": [
+ "/braces"
+ ],
+ "_resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "_spec": "7.0.1",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/fill-range/issues"
+ },
+ "contributors": [
+ {
+ "name": "Edo Rivai",
+ "url": "edo.rivai.nl"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Paul Miller",
+ "url": "paulmillr.com"
+ },
+ {
+ "name": "Rouven Weßling",
+ "url": "www.rouvenwessling.de"
+ },
+ {
+ "url": "https://github.com/wtgtybhertgeghgtwtg"
+ }
+ ],
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "description": "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`",
+ "devDependencies": {
+ "gulp-format-md": "^2.0.0",
+ "mocha": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/fill-range",
+ "keywords": [
+ "alpha",
+ "alphabetical",
+ "array",
+ "bash",
+ "brace",
+ "expand",
+ "expansion",
+ "fill",
+ "glob",
+ "match",
+ "matches",
+ "matching",
+ "number",
+ "numerical",
+ "range",
+ "ranges",
+ "regex",
+ "sh"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "fill-range",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/fill-range.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "7.0.1"
+}
diff --git a/node_modules/fsevents/LICENSE b/node_modules/fsevents/LICENSE
new file mode 100644
index 00000000..5d70441c
--- /dev/null
+++ b/node_modules/fsevents/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+-----------
+
+Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/fsevents/README.md b/node_modules/fsevents/README.md
new file mode 100644
index 00000000..025c9a13
--- /dev/null
+++ b/node_modules/fsevents/README.md
@@ -0,0 +1,83 @@
+# fsevents [](https://nodei.co/npm/fsevents/)
+
+Native access to MacOS FSEvents in [Node.js](https://nodejs.org/)
+
+The FSEvents API in MacOS allows applications to register for notifications of
+changes to a given directory tree. It is a very fast and lightweight alternative
+to kqueue.
+
+This is a low-level library. For a cross-platform file watching module that
+uses fsevents, check out [Chokidar](https://github.com/paulmillr/chokidar).
+
+## Installation
+
+Supports only **Node.js v8.16 and higher**.
+
+```sh
+npm install fsevents
+```
+
+## Usage
+
+```js
+const fsevents = require('fsevents');
+const stop = fsevents.watch(__dirname, (path, flags, id) => {
+ const info = fsevents.getInfo(path, flags, id);
+}); // To start observation
+stop(); // To end observation
+```
+
+The callback passed as the second parameter to `.watch` get's called whenever the operating system detects a
+a change in the file system. It takes three arguments:
+
+###### `fsevents.watch(dirname: string, (path: string, flags: number, id: string) => void): () => Promise`
+
+ * `path: string` - the item in the filesystem that have been changed
+ * `flags: number` - a numeric value describing what the change was
+ * `id: string` - an unique-id identifying this specific event
+
+ Returns closer callback which when called returns a Promise resolving when the watcher process has been shut down.
+
+###### `fsevents.getInfo(path: string, flags: number, id: string): FsEventInfo`
+
+The `getInfo` function takes the `path`, `flags` and `id` arguments and converts those parameters into a structure
+that is easier to digest to determine what the change was.
+
+The `FsEventsInfo` has the following shape:
+
+```js
+/**
+ * @typedef {'created'|'modified'|'deleted'|'moved'|'root-changed'|'cloned'|'unknown'} FsEventsEvent
+ * @typedef {'file'|'directory'|'symlink'} FsEventsType
+ */
+{
+ "event": "created", // {FsEventsEvent}
+ "path": "file.txt",
+ "type": "file", // {FsEventsType}
+ "changes": {
+ "inode": true, // Had iNode Meta-Information changed
+ "finder": false, // Had Finder Meta-Data changed
+ "access": false, // Had access permissions changed
+ "xattrs": false // Had xAttributes changed
+ },
+ "flags": 0x100000000
+}
+```
+
+## Changelog
+
+- v2.3 supports Apple Silicon ARM CPUs
+- v2 supports node 8.16+ and reduces package size massively
+- v1.2.8 supports node 6+
+- v1.2.7 supports node 4+
+
+## Troubleshooting
+
+- I'm getting `EBADPLATFORM` `Unsupported platform for fsevents` error.
+- It's fine, nothing is broken. fsevents is macos-only. Other platforms are skipped. If you want to hide this warning, report a bug to NPM bugtracker asking them to hide ebadplatform warnings by default.
+
+## License
+
+The MIT License Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller — see LICENSE file.
+
+Visit our [GitHub page](https://github.com/fsevents/fsevents) and [NPM Page](https://npmjs.org/package/fsevents)
diff --git a/node_modules/fsevents/fsevents.d.ts b/node_modules/fsevents/fsevents.d.ts
new file mode 100644
index 00000000..2723c048
--- /dev/null
+++ b/node_modules/fsevents/fsevents.d.ts
@@ -0,0 +1,46 @@
+declare type Event = "created" | "cloned" | "modified" | "deleted" | "moved" | "root-changed" | "unknown";
+declare type Type = "file" | "directory" | "symlink";
+declare type FileChanges = {
+ inode: boolean;
+ finder: boolean;
+ access: boolean;
+ xattrs: boolean;
+};
+declare type Info = {
+ event: Event;
+ path: string;
+ type: Type;
+ changes: FileChanges;
+ flags: number;
+};
+declare type WatchHandler = (path: string, flags: number, id: string) => void;
+export declare function watch(path: string, handler: WatchHandler): () => Promise;
+export declare function watch(path: string, since: number, handler: WatchHandler): () => Promise;
+export declare function getInfo(path: string, flags: number): Info;
+export declare const constants: {
+ None: 0x00000000;
+ MustScanSubDirs: 0x00000001;
+ UserDropped: 0x00000002;
+ KernelDropped: 0x00000004;
+ EventIdsWrapped: 0x00000008;
+ HistoryDone: 0x00000010;
+ RootChanged: 0x00000020;
+ Mount: 0x00000040;
+ Unmount: 0x00000080;
+ ItemCreated: 0x00000100;
+ ItemRemoved: 0x00000200;
+ ItemInodeMetaMod: 0x00000400;
+ ItemRenamed: 0x00000800;
+ ItemModified: 0x00001000;
+ ItemFinderInfoMod: 0x00002000;
+ ItemChangeOwner: 0x00004000;
+ ItemXattrMod: 0x00008000;
+ ItemIsFile: 0x00010000;
+ ItemIsDir: 0x00020000;
+ ItemIsSymlink: 0x00040000;
+ ItemIsHardlink: 0x00100000;
+ ItemIsLastHardlink: 0x00200000;
+ OwnEvent: 0x00080000;
+ ItemCloned: 0x00400000;
+};
+export {};
diff --git a/node_modules/fsevents/fsevents.js b/node_modules/fsevents/fsevents.js
new file mode 100644
index 00000000..f1b31c95
--- /dev/null
+++ b/node_modules/fsevents/fsevents.js
@@ -0,0 +1,82 @@
+/*
+ ** © 2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
+ ** Licensed under MIT License.
+ */
+
+/* jshint node:true */
+"use strict";
+
+if (process.platform !== "darwin") {
+ throw new Error(`Module 'fsevents' is not compatible with platform '${process.platform}'`);
+}
+
+const Native = require("./fsevents.node");
+const events = Native.constants;
+
+function watch(path, since, handler) {
+ if (typeof path !== "string") {
+ throw new TypeError(`fsevents argument 1 must be a string and not a ${typeof path}`);
+ }
+ if ("function" === typeof since && "undefined" === typeof handler) {
+ handler = since;
+ since = Native.flags.SinceNow;
+ }
+ if (typeof since !== "number") {
+ throw new TypeError(`fsevents argument 2 must be a number and not a ${typeof since}`);
+ }
+ if (typeof handler !== "function") {
+ throw new TypeError(`fsevents argument 3 must be a function and not a ${typeof handler}`);
+ }
+
+ let instance = Native.start(Native.global, path, since, handler);
+ if (!instance) throw new Error(`could not watch: ${path}`);
+ return () => {
+ const result = instance ? Promise.resolve(instance).then(Native.stop) : Promise.resolve(undefined);
+ instance = undefined;
+ return result;
+ };
+}
+
+function getInfo(path, flags) {
+ return {
+ path,
+ flags,
+ event: getEventType(flags),
+ type: getFileType(flags),
+ changes: getFileChanges(flags),
+ };
+}
+
+function getFileType(flags) {
+ if (events.ItemIsFile & flags) return "file";
+ if (events.ItemIsDir & flags) return "directory";
+ if (events.ItemIsSymlink & flags) return "symlink";
+}
+function anyIsTrue(obj) {
+ for (let key in obj) {
+ if (obj[key]) return true;
+ }
+ return false;
+}
+function getEventType(flags) {
+ if (events.ItemRemoved & flags) return "deleted";
+ if (events.ItemRenamed & flags) return "moved";
+ if (events.ItemCreated & flags) return "created";
+ if (events.ItemModified & flags) return "modified";
+ if (events.RootChanged & flags) return "root-changed";
+ if (events.ItemCloned & flags) return "cloned";
+ if (anyIsTrue(flags)) return "modified";
+ return "unknown";
+}
+function getFileChanges(flags) {
+ return {
+ inode: !!(events.ItemInodeMetaMod & flags),
+ finder: !!(events.ItemFinderInfoMod & flags),
+ access: !!(events.ItemChangeOwner & flags),
+ xattrs: !!(events.ItemXattrMod & flags),
+ };
+}
+
+exports.watch = watch;
+exports.getInfo = getInfo;
+exports.constants = events;
diff --git a/node_modules/fsevents/fsevents.node b/node_modules/fsevents/fsevents.node
new file mode 100644
index 00000000..00fac7e8
Binary files /dev/null and b/node_modules/fsevents/fsevents.node differ
diff --git a/node_modules/fsevents/package.json b/node_modules/fsevents/package.json
new file mode 100644
index 00000000..af6da84a
--- /dev/null
+++ b/node_modules/fsevents/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "fsevents",
+ "version": "2.3.2",
+ "description": "Native Access to MacOS FSEvents",
+ "main": "fsevents.js",
+ "types": "fsevents.d.ts",
+ "os": [
+ "darwin"
+ ],
+ "files": [
+ "fsevents.d.ts",
+ "fsevents.js",
+ "fsevents.node"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ },
+ "scripts": {
+ "clean": "node-gyp clean && rm -f fsevents.node",
+ "build": "node-gyp clean && rm -f fsevents.node && node-gyp rebuild && node-gyp clean",
+ "test": "/bin/bash ./test.sh 2>/dev/null",
+ "prepublishOnly": "npm run build"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/fsevents/fsevents.git"
+ },
+ "keywords": [
+ "fsevents",
+ "mac"
+ ],
+ "contributors": [
+ {
+ "name": "Philipp Dunkel",
+ "email": "pip@pipobscure.com"
+ },
+ {
+ "name": "Ben Noordhuis",
+ "email": "info@bnoordhuis.nl"
+ },
+ {
+ "name": "Elan Shankar",
+ "email": "elan.shanker@gmail.com"
+ },
+ {
+ "name": "Miroslav Bajtoš",
+ "email": "mbajtoss@gmail.com"
+ },
+ {
+ "name": "Paul Miller",
+ "url": "https://paulmillr.com"
+ }
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/fsevents/fsevents/issues"
+ },
+ "homepage": "https://github.com/fsevents/fsevents",
+ "devDependencies": {
+ "node-gyp": "^6.1.0"
+ }
+}
diff --git a/node_modules/glob-parent/CHANGELOG.md b/node_modules/glob-parent/CHANGELOG.md
new file mode 100644
index 00000000..fb9de961
--- /dev/null
+++ b/node_modules/glob-parent/CHANGELOG.md
@@ -0,0 +1,110 @@
+### [5.1.2](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2) (2021-03-06)
+
+
+### Bug Fixes
+
+* eliminate ReDoS ([#36](https://github.com/gulpjs/glob-parent/issues/36)) ([f923116](https://github.com/gulpjs/glob-parent/commit/f9231168b0041fea3f8f954b3cceb56269fc6366))
+
+### [5.1.1](https://github.com/gulpjs/glob-parent/compare/v5.1.0...v5.1.1) (2021-01-27)
+
+
+### Bug Fixes
+
+* unescape exclamation mark ([#26](https://github.com/gulpjs/glob-parent/issues/26)) ([a98874f](https://github.com/gulpjs/glob-parent/commit/a98874f1a59e407f4fb1beb0db4efa8392da60bb))
+
+## [5.1.0](https://github.com/gulpjs/glob-parent/compare/v5.0.0...v5.1.0) (2021-01-27)
+
+
+### Features
+
+* add `flipBackslashes` option to disable auto conversion of slashes (closes [#24](https://github.com/gulpjs/glob-parent/issues/24)) ([#25](https://github.com/gulpjs/glob-parent/issues/25)) ([eecf91d](https://github.com/gulpjs/glob-parent/commit/eecf91d5e3834ed78aee39c4eaaae654d76b87b3))
+
+## [5.0.0](https://github.com/gulpjs/glob-parent/compare/v4.0.0...v5.0.0) (2021-01-27)
+
+
+### ⚠ BREAKING CHANGES
+
+* Drop support for node <6 & bump dependencies
+
+### Miscellaneous Chores
+
+* Drop support for node <6 & bump dependencies ([896c0c0](https://github.com/gulpjs/glob-parent/commit/896c0c00b4e7362f60b96e7fc295ae929245255a))
+
+## [4.0.0](https://github.com/gulpjs/glob-parent/compare/v3.1.0...v4.0.0) (2021-01-27)
+
+
+### ⚠ BREAKING CHANGES
+
+* question marks are valid path characters on Windows so avoid flagging as a glob when alone
+* Update is-glob dependency
+
+### Features
+
+* hoist regexps and strings for performance gains ([4a80667](https://github.com/gulpjs/glob-parent/commit/4a80667c69355c76a572a5892b0f133c8e1f457e))
+* question marks are valid path characters on Windows so avoid flagging as a glob when alone ([2a551dd](https://github.com/gulpjs/glob-parent/commit/2a551dd0dc3235e78bf3c94843d4107072d17841))
+* Update is-glob dependency ([e41fcd8](https://github.com/gulpjs/glob-parent/commit/e41fcd895d1f7bc617dba45c9d935a7949b9c281))
+
+## [3.1.0](https://github.com/gulpjs/glob-parent/compare/v3.0.1...v3.1.0) (2021-01-27)
+
+
+### Features
+
+* allow basic win32 backslash use ([272afa5](https://github.com/gulpjs/glob-parent/commit/272afa5fd070fc0f796386a5993d4ee4a846988b))
+* handle extglobs (parentheses) containing separators ([7db1bdb](https://github.com/gulpjs/glob-parent/commit/7db1bdb0756e55fd14619e8ce31aa31b17b117fd))
+* new approach to braces/brackets handling ([8269bd8](https://github.com/gulpjs/glob-parent/commit/8269bd89290d99fac9395a354fb56fdcdb80f0be))
+* pre-process braces/brackets sections ([9ef8a87](https://github.com/gulpjs/glob-parent/commit/9ef8a87f66b1a43d0591e7a8e4fc5a18415ee388))
+* preserve escaped brace/bracket at end of string ([8cfb0ba](https://github.com/gulpjs/glob-parent/commit/8cfb0ba84202d51571340dcbaf61b79d16a26c76))
+
+
+### Bug Fixes
+
+* trailing escaped square brackets ([99ec9fe](https://github.com/gulpjs/glob-parent/commit/99ec9fecc60ee488ded20a94dd4f18b4f55c4ccf))
+
+### [3.0.1](https://github.com/gulpjs/glob-parent/compare/v3.0.0...v3.0.1) (2021-01-27)
+
+
+### Features
+
+* use path-dirname ponyfill ([cdbea5f](https://github.com/gulpjs/glob-parent/commit/cdbea5f32a58a54e001a75ddd7c0fccd4776aacc))
+
+
+### Bug Fixes
+
+* unescape glob-escaped dirnames on output ([598c533](https://github.com/gulpjs/glob-parent/commit/598c533bdf49c1428bc063aa9b8db40c5a86b030))
+
+## [3.0.0](https://github.com/gulpjs/glob-parent/compare/v2.0.0...v3.0.0) (2021-01-27)
+
+
+### ⚠ BREAKING CHANGES
+
+* update is-glob dependency
+
+### Features
+
+* update is-glob dependency ([5c5f8ef](https://github.com/gulpjs/glob-parent/commit/5c5f8efcee362a8e7638cf8220666acd8784f6bd))
+
+## [2.0.0](https://github.com/gulpjs/glob-parent/compare/v1.3.0...v2.0.0) (2021-01-27)
+
+
+### Features
+
+* move up to dirname regardless of glob characters ([f97fb83](https://github.com/gulpjs/glob-parent/commit/f97fb83be2e0a9fc8d3b760e789d2ecadd6aa0c2))
+
+## [1.3.0](https://github.com/gulpjs/glob-parent/compare/v1.2.0...v1.3.0) (2021-01-27)
+
+## [1.2.0](https://github.com/gulpjs/glob-parent/compare/v1.1.0...v1.2.0) (2021-01-27)
+
+
+### Reverts
+
+* feat: make regex test strings smaller ([dc80fa9](https://github.com/gulpjs/glob-parent/commit/dc80fa9658dca20549cfeba44bbd37d5246fcce0))
+
+## [1.1.0](https://github.com/gulpjs/glob-parent/compare/v1.0.0...v1.1.0) (2021-01-27)
+
+
+### Features
+
+* make regex test strings smaller ([cd83220](https://github.com/gulpjs/glob-parent/commit/cd832208638f45169f986d80fcf66e401f35d233))
+
+## 1.0.0 (2021-01-27)
+
diff --git a/node_modules/glob-parent/LICENSE b/node_modules/glob-parent/LICENSE
new file mode 100644
index 00000000..63222d7a
--- /dev/null
+++ b/node_modules/glob-parent/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) 2015, 2019 Elan Shanker
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+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.
diff --git a/node_modules/glob-parent/README.md b/node_modules/glob-parent/README.md
new file mode 100644
index 00000000..36a27938
--- /dev/null
+++ b/node_modules/glob-parent/README.md
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+# glob-parent
+
+[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Azure Pipelines Build Status][azure-pipelines-image]][azure-pipelines-url] [![Travis Build Status][travis-image]][travis-url] [![AppVeyor Build Status][appveyor-image]][appveyor-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Gitter chat][gitter-image]][gitter-url]
+
+Extract the non-magic parent path from a glob string.
+
+## Usage
+
+```js
+var globParent = require('glob-parent');
+
+globParent('path/to/*.js'); // 'path/to'
+globParent('/root/path/to/*.js'); // '/root/path/to'
+globParent('/*.js'); // '/'
+globParent('*.js'); // '.'
+globParent('**/*.js'); // '.'
+globParent('path/{to,from}'); // 'path'
+globParent('path/!(to|from)'); // 'path'
+globParent('path/?(to|from)'); // 'path'
+globParent('path/+(to|from)'); // 'path'
+globParent('path/*(to|from)'); // 'path'
+globParent('path/@(to|from)'); // 'path'
+globParent('path/**/*'); // 'path'
+
+// if provided a non-glob path, returns the nearest dir
+globParent('path/foo/bar.js'); // 'path/foo'
+globParent('path/foo/'); // 'path/foo'
+globParent('path/foo'); // 'path' (see issue #3 for details)
+```
+
+## API
+
+### `globParent(maybeGlobString, [options])`
+
+Takes a string and returns the part of the path before the glob begins. Be aware of Escaping rules and Limitations below.
+
+#### options
+
+```js
+{
+ // Disables the automatic conversion of slashes for Windows
+ flipBackslashes: true
+}
+```
+
+## Escaping
+
+The following characters have special significance in glob patterns and must be escaped if you want them to be treated as regular path characters:
+
+- `?` (question mark) unless used as a path segment alone
+- `*` (asterisk)
+- `|` (pipe)
+- `(` (opening parenthesis)
+- `)` (closing parenthesis)
+- `{` (opening curly brace)
+- `}` (closing curly brace)
+- `[` (opening bracket)
+- `]` (closing bracket)
+
+**Example**
+
+```js
+globParent('foo/[bar]/') // 'foo'
+globParent('foo/\\[bar]/') // 'foo/[bar]'
+```
+
+## Limitations
+
+### Braces & Brackets
+This library attempts a quick and imperfect method of determining which path
+parts have glob magic without fully parsing/lexing the pattern. There are some
+advanced use cases that can trip it up, such as nested braces where the outer
+pair is escaped and the inner one contains a path separator. If you find
+yourself in the unlikely circumstance of being affected by this or need to
+ensure higher-fidelity glob handling in your library, it is recommended that you
+pre-process your input with [expand-braces] and/or [expand-brackets].
+
+### Windows
+Backslashes are not valid path separators for globs. If a path with backslashes
+is provided anyway, for simple cases, glob-parent will replace the path
+separator for you and return the non-glob parent path (now with
+forward-slashes, which are still valid as Windows path separators).
+
+This cannot be used in conjunction with escape characters.
+
+```js
+// BAD
+globParent('C:\\Program Files \\(x86\\)\\*.ext') // 'C:/Program Files /(x86/)'
+
+// GOOD
+globParent('C:/Program Files\\(x86\\)/*.ext') // 'C:/Program Files (x86)'
+```
+
+If you are using escape characters for a pattern without path parts (i.e.
+relative to `cwd`), prefix with `./` to avoid confusing glob-parent.
+
+```js
+// BAD
+globParent('foo \\[bar]') // 'foo '
+globParent('foo \\[bar]*') // 'foo '
+
+// GOOD
+globParent('./foo \\[bar]') // 'foo [bar]'
+globParent('./foo \\[bar]*') // '.'
+```
+
+## License
+
+ISC
+
+[expand-braces]: https://github.com/jonschlinkert/expand-braces
+[expand-brackets]: https://github.com/jonschlinkert/expand-brackets
+
+[downloads-image]: https://img.shields.io/npm/dm/glob-parent.svg
+[npm-url]: https://www.npmjs.com/package/glob-parent
+[npm-image]: https://img.shields.io/npm/v/glob-parent.svg
+
+[azure-pipelines-url]: https://dev.azure.com/gulpjs/gulp/_build/latest?definitionId=2&branchName=master
+[azure-pipelines-image]: https://dev.azure.com/gulpjs/gulp/_apis/build/status/glob-parent?branchName=master
+
+[travis-url]: https://travis-ci.org/gulpjs/glob-parent
+[travis-image]: https://img.shields.io/travis/gulpjs/glob-parent.svg?label=travis-ci
+
+[appveyor-url]: https://ci.appveyor.com/project/gulpjs/glob-parent
+[appveyor-image]: https://img.shields.io/appveyor/ci/gulpjs/glob-parent.svg?label=appveyor
+
+[coveralls-url]: https://coveralls.io/r/gulpjs/glob-parent
+[coveralls-image]: https://img.shields.io/coveralls/gulpjs/glob-parent/master.svg
+
+[gitter-url]: https://gitter.im/gulpjs/gulp
+[gitter-image]: https://badges.gitter.im/gulpjs/gulp.svg
diff --git a/node_modules/glob-parent/index.js b/node_modules/glob-parent/index.js
new file mode 100644
index 00000000..09e257ea
--- /dev/null
+++ b/node_modules/glob-parent/index.js
@@ -0,0 +1,42 @@
+'use strict';
+
+var isGlob = require('is-glob');
+var pathPosixDirname = require('path').posix.dirname;
+var isWin32 = require('os').platform() === 'win32';
+
+var slash = '/';
+var backslash = /\\/g;
+var enclosure = /[\{\[].*[\}\]]$/;
+var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
+var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
+
+/**
+ * @param {string} str
+ * @param {Object} opts
+ * @param {boolean} [opts.flipBackslashes=true]
+ * @returns {string}
+ */
+module.exports = function globParent(str, opts) {
+ var options = Object.assign({ flipBackslashes: true }, opts);
+
+ // flip windows path separators
+ if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) {
+ str = str.replace(backslash, slash);
+ }
+
+ // special case for strings ending in enclosure containing path separator
+ if (enclosure.test(str)) {
+ str += slash;
+ }
+
+ // preserves full path in case of trailing path separator
+ str += 'a';
+
+ // remove path parts that are globby
+ do {
+ str = pathPosixDirname(str);
+ } while (isGlob(str) || globby.test(str));
+
+ // remove escape chars and return result
+ return str.replace(escaped, '$1');
+};
diff --git a/node_modules/glob-parent/package.json b/node_modules/glob-parent/package.json
new file mode 100644
index 00000000..61ff4760
--- /dev/null
+++ b/node_modules/glob-parent/package.json
@@ -0,0 +1,93 @@
+{
+ "_args": [
+ [
+ "glob-parent@5.1.2",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "glob-parent@5.1.2",
+ "_id": "glob-parent@5.1.2",
+ "_inBundle": false,
+ "_integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "_location": "/glob-parent",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "glob-parent@5.1.2",
+ "name": "glob-parent",
+ "escapedName": "glob-parent",
+ "rawSpec": "5.1.2",
+ "saveSpec": null,
+ "fetchSpec": "5.1.2"
+ },
+ "_requiredBy": [
+ "/chokidar"
+ ],
+ "_resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "_spec": "5.1.2",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Gulp Team",
+ "email": "team@gulpjs.com",
+ "url": "https://gulpjs.com/"
+ },
+ "bugs": {
+ "url": "https://github.com/gulpjs/glob-parent/issues"
+ },
+ "contributors": [
+ {
+ "name": "Elan Shanker",
+ "url": "https://github.com/es128"
+ },
+ {
+ "name": "Blaine Bublitz",
+ "email": "blaine.bublitz@gmail.com"
+ }
+ ],
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "description": "Extract the non-magic parent path from a glob string.",
+ "devDependencies": {
+ "coveralls": "^3.0.11",
+ "eslint": "^2.13.1",
+ "eslint-config-gulp": "^3.0.1",
+ "expect": "^1.20.2",
+ "mocha": "^6.0.2",
+ "nyc": "^13.3.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "files": [
+ "LICENSE",
+ "index.js"
+ ],
+ "homepage": "https://github.com/gulpjs/glob-parent#readme",
+ "keywords": [
+ "glob",
+ "parent",
+ "strip",
+ "path",
+ "dirname",
+ "directory",
+ "base",
+ "wildcard"
+ ],
+ "license": "ISC",
+ "main": "index.js",
+ "name": "glob-parent",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/gulpjs/glob-parent.git"
+ },
+ "scripts": {
+ "azure-pipelines": "nyc mocha --async-only --reporter xunit -O output=test.xunit",
+ "coveralls": "nyc report --reporter=text-lcov | coveralls",
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --async-only"
+ },
+ "version": "5.1.2"
+}
diff --git a/node_modules/is-binary-path/index.d.ts b/node_modules/is-binary-path/index.d.ts
new file mode 100644
index 00000000..19dcd432
--- /dev/null
+++ b/node_modules/is-binary-path/index.d.ts
@@ -0,0 +1,17 @@
+/**
+Check if a file path is a binary file.
+
+@example
+```
+import isBinaryPath = require('is-binary-path');
+
+isBinaryPath('source/unicorn.png');
+//=> true
+
+isBinaryPath('source/unicorn.txt');
+//=> false
+```
+*/
+declare function isBinaryPath(filePath: string): boolean;
+
+export = isBinaryPath;
diff --git a/node_modules/is-binary-path/index.js b/node_modules/is-binary-path/index.js
new file mode 100644
index 00000000..ef7548c8
--- /dev/null
+++ b/node_modules/is-binary-path/index.js
@@ -0,0 +1,7 @@
+'use strict';
+const path = require('path');
+const binaryExtensions = require('binary-extensions');
+
+const extensions = new Set(binaryExtensions);
+
+module.exports = filePath => extensions.has(path.extname(filePath).slice(1).toLowerCase());
diff --git a/node_modules/is-binary-path/license b/node_modules/is-binary-path/license
new file mode 100644
index 00000000..401b1c73
--- /dev/null
+++ b/node_modules/is-binary-path/license
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2019 Sindre Sorhus (https://sindresorhus.com), Paul Miller (https://paulmillr.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/is-binary-path/package.json b/node_modules/is-binary-path/package.json
new file mode 100644
index 00000000..584ce695
--- /dev/null
+++ b/node_modules/is-binary-path/package.json
@@ -0,0 +1,75 @@
+{
+ "_args": [
+ [
+ "is-binary-path@2.1.0",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "is-binary-path@2.1.0",
+ "_id": "is-binary-path@2.1.0",
+ "_inBundle": false,
+ "_integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "_location": "/is-binary-path",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "is-binary-path@2.1.0",
+ "name": "is-binary-path",
+ "escapedName": "is-binary-path",
+ "rawSpec": "2.1.0",
+ "saveSpec": null,
+ "fetchSpec": "2.1.0"
+ },
+ "_requiredBy": [
+ "/chokidar"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "_spec": "2.1.0",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/is-binary-path/issues"
+ },
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "description": "Check if a file path is a binary file",
+ "devDependencies": {
+ "ava": "^1.4.1",
+ "tsd": "^0.7.2",
+ "xo": "^0.24.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "files": [
+ "index.js",
+ "index.d.ts"
+ ],
+ "homepage": "https://github.com/sindresorhus/is-binary-path#readme",
+ "keywords": [
+ "binary",
+ "extensions",
+ "extension",
+ "file",
+ "path",
+ "check",
+ "detect",
+ "is"
+ ],
+ "license": "MIT",
+ "name": "is-binary-path",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/is-binary-path.git"
+ },
+ "scripts": {
+ "test": "xo && ava && tsd"
+ },
+ "version": "2.1.0"
+}
diff --git a/node_modules/is-binary-path/readme.md b/node_modules/is-binary-path/readme.md
new file mode 100644
index 00000000..b4ab0251
--- /dev/null
+++ b/node_modules/is-binary-path/readme.md
@@ -0,0 +1,34 @@
+# is-binary-path [](https://travis-ci.org/sindresorhus/is-binary-path)
+
+> Check if a file path is a binary file
+
+
+## Install
+
+```
+$ npm install is-binary-path
+```
+
+
+## Usage
+
+```js
+const isBinaryPath = require('is-binary-path');
+
+isBinaryPath('source/unicorn.png');
+//=> true
+
+isBinaryPath('source/unicorn.txt');
+//=> false
+```
+
+
+## Related
+
+- [binary-extensions](https://github.com/sindresorhus/binary-extensions) - List of binary file extensions
+- [is-text-path](https://github.com/sindresorhus/is-text-path) - Check if a filepath is a text file
+
+
+## License
+
+MIT © [Sindre Sorhus](https://sindresorhus.com), [Paul Miller](https://paulmillr.com)
diff --git a/node_modules/is-extglob/LICENSE b/node_modules/is-extglob/LICENSE
new file mode 100644
index 00000000..842218cf
--- /dev/null
+++ b/node_modules/is-extglob/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016, Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/is-extglob/README.md b/node_modules/is-extglob/README.md
new file mode 100644
index 00000000..0416af5c
--- /dev/null
+++ b/node_modules/is-extglob/README.md
@@ -0,0 +1,107 @@
+# is-extglob [](https://www.npmjs.com/package/is-extglob) [](https://npmjs.org/package/is-extglob) [](https://travis-ci.org/jonschlinkert/is-extglob)
+
+> Returns true if a string has an extglob.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-extglob
+```
+
+## Usage
+
+```js
+var isExtglob = require('is-extglob');
+```
+
+**True**
+
+```js
+isExtglob('?(abc)');
+isExtglob('@(abc)');
+isExtglob('!(abc)');
+isExtglob('*(abc)');
+isExtglob('+(abc)');
+```
+
+**False**
+
+Escaped extglobs:
+
+```js
+isExtglob('\\?(abc)');
+isExtglob('\\@(abc)');
+isExtglob('\\!(abc)');
+isExtglob('\\*(abc)');
+isExtglob('\\+(abc)');
+```
+
+Everything else...
+
+```js
+isExtglob('foo.js');
+isExtglob('!foo.js');
+isExtglob('*.js');
+isExtglob('**/abc.js');
+isExtglob('abc/*.js');
+isExtglob('abc/(aaa|bbb).js');
+isExtglob('abc/[a-z].js');
+isExtglob('abc/{a,b}.js');
+isExtglob('abc/?.js');
+isExtglob('abc.js');
+isExtglob('abc/def/ghi.js');
+```
+
+## History
+
+**v2.0**
+
+Adds support for escaping. Escaped exglobs no longer return true.
+
+## About
+
+### Related projects
+
+* [has-glob](https://www.npmjs.com/package/has-glob): Returns `true` if an array has a glob pattern. | [homepage](https://github.com/jonschlinkert/has-glob "Returns `true` if an array has a glob pattern.")
+* [is-glob](https://www.npmjs.com/package/is-glob): Returns `true` if the given string looks like a glob pattern or an extglob pattern… [more](https://github.com/jonschlinkert/is-glob) | [homepage](https://github.com/jonschlinkert/is-glob "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a bet")
+* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Building docs
+
+_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
+
+To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
+
+```sh
+$ npm install -g verb verb-generate-readme && verb
+```
+
+### Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm install -d && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT license](https://github.com/jonschlinkert/is-extglob/blob/master/LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.31, on October 12, 2016._
\ No newline at end of file
diff --git a/node_modules/is-extglob/index.js b/node_modules/is-extglob/index.js
new file mode 100644
index 00000000..c1d986fc
--- /dev/null
+++ b/node_modules/is-extglob/index.js
@@ -0,0 +1,20 @@
+/*!
+ * is-extglob
+ *
+ * Copyright (c) 2014-2016, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+module.exports = function isExtglob(str) {
+ if (typeof str !== 'string' || str === '') {
+ return false;
+ }
+
+ var match;
+ while ((match = /(\\).|([@?!+*]\(.*\))/g.exec(str))) {
+ if (match[2]) return true;
+ str = str.slice(match.index + match[0].length);
+ }
+
+ return false;
+};
diff --git a/node_modules/is-extglob/package.json b/node_modules/is-extglob/package.json
new file mode 100644
index 00000000..a49cec12
--- /dev/null
+++ b/node_modules/is-extglob/package.json
@@ -0,0 +1,103 @@
+{
+ "_args": [
+ [
+ "is-extglob@2.1.1",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "is-extglob@2.1.1",
+ "_id": "is-extglob@2.1.1",
+ "_inBundle": false,
+ "_integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "_location": "/is-extglob",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "is-extglob@2.1.1",
+ "name": "is-extglob",
+ "escapedName": "is-extglob",
+ "rawSpec": "2.1.1",
+ "saveSpec": null,
+ "fetchSpec": "2.1.1"
+ },
+ "_requiredBy": [
+ "/is-glob"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "_spec": "2.1.1",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/is-extglob/issues"
+ },
+ "description": "Returns true if a string has an extglob.",
+ "devDependencies": {
+ "gulp-format-md": "^0.1.10",
+ "mocha": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/is-extglob",
+ "keywords": [
+ "bash",
+ "braces",
+ "check",
+ "exec",
+ "expression",
+ "extglob",
+ "glob",
+ "globbing",
+ "globstar",
+ "is",
+ "match",
+ "matches",
+ "pattern",
+ "regex",
+ "regular",
+ "string",
+ "test"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-extglob",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/is-extglob.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "has-glob",
+ "is-glob",
+ "micromatch"
+ ]
+ },
+ "reflinks": [
+ "verb",
+ "verb-generate-readme"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "2.1.1"
+}
diff --git a/node_modules/is-glob/LICENSE b/node_modules/is-glob/LICENSE
new file mode 100644
index 00000000..3f2eca18
--- /dev/null
+++ b/node_modules/is-glob/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/is-glob/README.md b/node_modules/is-glob/README.md
new file mode 100644
index 00000000..59444ebe
--- /dev/null
+++ b/node_modules/is-glob/README.md
@@ -0,0 +1,206 @@
+# is-glob [](https://www.npmjs.com/package/is-glob) [](https://npmjs.org/package/is-glob) [](https://npmjs.org/package/is-glob) [](https://travis-ci.org/micromatch/is-glob) [](https://ci.appveyor.com/project/micromatch/is-glob)
+
+> Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a better user experience.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-glob
+```
+
+You might also be interested in [is-valid-glob](https://github.com/jonschlinkert/is-valid-glob) and [has-glob](https://github.com/jonschlinkert/has-glob).
+
+## Usage
+
+```js
+var isGlob = require('is-glob');
+```
+
+### Default behavior
+
+**True**
+
+Patterns that have glob characters or regex patterns will return `true`:
+
+```js
+isGlob('!foo.js');
+isGlob('*.js');
+isGlob('**/abc.js');
+isGlob('abc/*.js');
+isGlob('abc/(aaa|bbb).js');
+isGlob('abc/[a-z].js');
+isGlob('abc/{a,b}.js');
+//=> true
+```
+
+Extglobs
+
+```js
+isGlob('abc/@(a).js');
+isGlob('abc/!(a).js');
+isGlob('abc/+(a).js');
+isGlob('abc/*(a).js');
+isGlob('abc/?(a).js');
+//=> true
+```
+
+**False**
+
+Escaped globs or extglobs return `false`:
+
+```js
+isGlob('abc/\\@(a).js');
+isGlob('abc/\\!(a).js');
+isGlob('abc/\\+(a).js');
+isGlob('abc/\\*(a).js');
+isGlob('abc/\\?(a).js');
+isGlob('\\!foo.js');
+isGlob('\\*.js');
+isGlob('\\*\\*/abc.js');
+isGlob('abc/\\*.js');
+isGlob('abc/\\(aaa|bbb).js');
+isGlob('abc/\\[a-z].js');
+isGlob('abc/\\{a,b}.js');
+//=> false
+```
+
+Patterns that do not have glob patterns return `false`:
+
+```js
+isGlob('abc.js');
+isGlob('abc/def/ghi.js');
+isGlob('foo.js');
+isGlob('abc/@.js');
+isGlob('abc/+.js');
+isGlob('abc/?.js');
+isGlob();
+isGlob(null);
+//=> false
+```
+
+Arrays are also `false` (If you want to check if an array has a glob pattern, use [has-glob](https://github.com/jonschlinkert/has-glob)):
+
+```js
+isGlob(['**/*.js']);
+isGlob(['foo.js']);
+//=> false
+```
+
+### Option strict
+
+When `options.strict === false` the behavior is less strict in determining if a pattern is a glob. Meaning that
+some patterns that would return `false` may return `true`. This is done so that matching libraries like [micromatch](https://github.com/micromatch/micromatch) have a chance at determining if the pattern is a glob or not.
+
+**True**
+
+Patterns that have glob characters or regex patterns will return `true`:
+
+```js
+isGlob('!foo.js', {strict: false});
+isGlob('*.js', {strict: false});
+isGlob('**/abc.js', {strict: false});
+isGlob('abc/*.js', {strict: false});
+isGlob('abc/(aaa|bbb).js', {strict: false});
+isGlob('abc/[a-z].js', {strict: false});
+isGlob('abc/{a,b}.js', {strict: false});
+//=> true
+```
+
+Extglobs
+
+```js
+isGlob('abc/@(a).js', {strict: false});
+isGlob('abc/!(a).js', {strict: false});
+isGlob('abc/+(a).js', {strict: false});
+isGlob('abc/*(a).js', {strict: false});
+isGlob('abc/?(a).js', {strict: false});
+//=> true
+```
+
+**False**
+
+Escaped globs or extglobs return `false`:
+
+```js
+isGlob('\\!foo.js', {strict: false});
+isGlob('\\*.js', {strict: false});
+isGlob('\\*\\*/abc.js', {strict: false});
+isGlob('abc/\\*.js', {strict: false});
+isGlob('abc/\\(aaa|bbb).js', {strict: false});
+isGlob('abc/\\[a-z].js', {strict: false});
+isGlob('abc/\\{a,b}.js', {strict: false});
+//=> false
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Related projects
+
+You might also be interested in these projects:
+
+* [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit")
+* [base](https://www.npmjs.com/package/base): Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks | [homepage](https://github.com/node-base/base "Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks")
+* [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.")
+* [verb](https://www.npmjs.com/package/verb): Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used… [more](https://github.com/verbose/verb) | [homepage](https://github.com/verbose/verb "Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes.")
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 47 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 5 | [doowb](https://github.com/doowb) |
+| 1 | [phated](https://github.com/phated) |
+| 1 | [danhper](https://github.com/danhper) |
+| 1 | [paulmillr](https://github.com/paulmillr) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [GitHub Profile](https://github.com/jonschlinkert)
+* [Twitter Profile](https://twitter.com/jonschlinkert)
+* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
+
+### License
+
+Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on March 27, 2019._
\ No newline at end of file
diff --git a/node_modules/is-glob/index.js b/node_modules/is-glob/index.js
new file mode 100644
index 00000000..55826510
--- /dev/null
+++ b/node_modules/is-glob/index.js
@@ -0,0 +1,48 @@
+/*!
+ * is-glob
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+var isExtglob = require('is-extglob');
+var chars = { '{': '}', '(': ')', '[': ']'};
+var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
+var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/;
+
+module.exports = function isGlob(str, options) {
+ if (typeof str !== 'string' || str === '') {
+ return false;
+ }
+
+ if (isExtglob(str)) {
+ return true;
+ }
+
+ var regex = strictRegex;
+ var match;
+
+ // optionally relax regex
+ if (options && options.strict === false) {
+ regex = relaxedRegex;
+ }
+
+ while ((match = regex.exec(str))) {
+ if (match[2]) return true;
+ var idx = match.index + match[0].length;
+
+ // if an open bracket/brace/paren is escaped,
+ // set the index to the next closing character
+ var open = match[1];
+ var close = open ? chars[open] : null;
+ if (open && close) {
+ var n = str.indexOf(close, idx);
+ if (n !== -1) {
+ idx = n + 1;
+ }
+ }
+
+ str = str.slice(idx);
+ }
+ return false;
+};
diff --git a/node_modules/is-glob/package.json b/node_modules/is-glob/package.json
new file mode 100644
index 00000000..68cd3291
--- /dev/null
+++ b/node_modules/is-glob/package.json
@@ -0,0 +1,125 @@
+{
+ "_args": [
+ [
+ "is-glob@4.0.1",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "is-glob@4.0.1",
+ "_id": "is-glob@4.0.1",
+ "_inBundle": false,
+ "_integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "_location": "/is-glob",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "is-glob@4.0.1",
+ "name": "is-glob",
+ "escapedName": "is-glob",
+ "rawSpec": "4.0.1",
+ "saveSpec": null,
+ "fetchSpec": "4.0.1"
+ },
+ "_requiredBy": [
+ "/chokidar",
+ "/glob-parent"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "_spec": "4.0.1",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/micromatch/is-glob/issues"
+ },
+ "contributors": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://twitter.com/doowb"
+ },
+ {
+ "name": "Daniel Perez",
+ "url": "https://tuvistavie.com"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ }
+ ],
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "description": "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a better user experience.",
+ "devDependencies": {
+ "gulp-format-md": "^0.1.10",
+ "mocha": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/micromatch/is-glob",
+ "keywords": [
+ "bash",
+ "braces",
+ "check",
+ "exec",
+ "expression",
+ "extglob",
+ "glob",
+ "globbing",
+ "globstar",
+ "is",
+ "match",
+ "matches",
+ "pattern",
+ "regex",
+ "regular",
+ "string",
+ "test"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-glob",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/micromatch/is-glob.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "layout": "default",
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "assemble",
+ "base",
+ "update",
+ "verb"
+ ]
+ },
+ "reflinks": [
+ "assemble",
+ "bach",
+ "base",
+ "composer",
+ "gulp",
+ "has-glob",
+ "is-valid-glob",
+ "micromatch",
+ "npm",
+ "scaffold",
+ "verb",
+ "vinyl"
+ ]
+ },
+ "version": "4.0.1"
+}
diff --git a/node_modules/is-number/LICENSE b/node_modules/is-number/LICENSE
new file mode 100644
index 00000000..9af4a67d
--- /dev/null
+++ b/node_modules/is-number/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-present, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/is-number/README.md b/node_modules/is-number/README.md
new file mode 100644
index 00000000..eb8149e8
--- /dev/null
+++ b/node_modules/is-number/README.md
@@ -0,0 +1,187 @@
+# is-number [](https://www.npmjs.com/package/is-number) [](https://npmjs.org/package/is-number) [](https://npmjs.org/package/is-number) [](https://travis-ci.org/jonschlinkert/is-number)
+
+> Returns true if the value is a finite number.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-number
+```
+
+## Why is this needed?
+
+In JavaScript, it's not always as straightforward as it should be to reliably check if a value is a number. It's common for devs to use `+`, `-`, or `Number()` to cast a string value to a number (for example, when values are returned from user input, regex matches, parsers, etc). But there are many non-intuitive edge cases that yield unexpected results:
+
+```js
+console.log(+[]); //=> 0
+console.log(+''); //=> 0
+console.log(+' '); //=> 0
+console.log(typeof NaN); //=> 'number'
+```
+
+This library offers a performant way to smooth out edge cases like these.
+
+## Usage
+
+```js
+const isNumber = require('is-number');
+```
+
+See the [tests](./test.js) for more examples.
+
+### true
+
+```js
+isNumber(5e3); // true
+isNumber(0xff); // true
+isNumber(-1.1); // true
+isNumber(0); // true
+isNumber(1); // true
+isNumber(1.1); // true
+isNumber(10); // true
+isNumber(10.10); // true
+isNumber(100); // true
+isNumber('-1.1'); // true
+isNumber('0'); // true
+isNumber('012'); // true
+isNumber('0xff'); // true
+isNumber('1'); // true
+isNumber('1.1'); // true
+isNumber('10'); // true
+isNumber('10.10'); // true
+isNumber('100'); // true
+isNumber('5e3'); // true
+isNumber(parseInt('012')); // true
+isNumber(parseFloat('012')); // true
+```
+
+### False
+
+Everything else is false, as you would expect:
+
+```js
+isNumber(Infinity); // false
+isNumber(NaN); // false
+isNumber(null); // false
+isNumber(undefined); // false
+isNumber(''); // false
+isNumber(' '); // false
+isNumber('foo'); // false
+isNumber([1]); // false
+isNumber([]); // false
+isNumber(function () {}); // false
+isNumber({}); // false
+```
+
+## Release history
+
+### 7.0.0
+
+* Refactor. Now uses `.isFinite` if it exists.
+* Performance is about the same as v6.0 when the value is a string or number. But it's now 3x-4x faster when the value is not a string or number.
+
+### 6.0.0
+
+* Optimizations, thanks to @benaadams.
+
+### 5.0.0
+
+**Breaking changes**
+
+* removed support for `instanceof Number` and `instanceof String`
+
+## Benchmarks
+
+As with all benchmarks, take these with a grain of salt. See the [benchmarks](./benchmark/index.js) for more detail.
+
+```
+# all
+v7.0 x 413,222 ops/sec ±2.02% (86 runs sampled)
+v6.0 x 111,061 ops/sec ±1.29% (85 runs sampled)
+parseFloat x 317,596 ops/sec ±1.36% (86 runs sampled)
+fastest is 'v7.0'
+
+# string
+v7.0 x 3,054,496 ops/sec ±1.05% (89 runs sampled)
+v6.0 x 2,957,781 ops/sec ±0.98% (88 runs sampled)
+parseFloat x 3,071,060 ops/sec ±1.13% (88 runs sampled)
+fastest is 'parseFloat,v7.0'
+
+# number
+v7.0 x 3,146,895 ops/sec ±0.89% (89 runs sampled)
+v6.0 x 3,214,038 ops/sec ±1.07% (89 runs sampled)
+parseFloat x 3,077,588 ops/sec ±1.07% (87 runs sampled)
+fastest is 'v6.0'
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Related projects
+
+You might also be interested in these projects:
+
+* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object "Returns true if an object was created by the `Object` constructor.")
+* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive. | [homepage](https://github.com/jonschlinkert/is-primitive "Returns `true` if the value is a primitive. ")
+* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
+* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of "Get the native type of a value.")
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 49 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 5 | [charlike-old](https://github.com/charlike-old) |
+| 1 | [benaadams](https://github.com/benaadams) |
+| 1 | [realityking](https://github.com/realityking) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
+* [GitHub Profile](https://github.com/jonschlinkert)
+* [Twitter Profile](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 15, 2018._
\ No newline at end of file
diff --git a/node_modules/is-number/index.js b/node_modules/is-number/index.js
new file mode 100644
index 00000000..27f19b75
--- /dev/null
+++ b/node_modules/is-number/index.js
@@ -0,0 +1,18 @@
+/*!
+ * is-number
+ *
+ * Copyright (c) 2014-present, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+module.exports = function(num) {
+ if (typeof num === 'number') {
+ return num - num === 0;
+ }
+ if (typeof num === 'string' && num.trim() !== '') {
+ return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
+ }
+ return false;
+};
diff --git a/node_modules/is-number/package.json b/node_modules/is-number/package.json
new file mode 100644
index 00000000..077e995a
--- /dev/null
+++ b/node_modules/is-number/package.json
@@ -0,0 +1,125 @@
+{
+ "_args": [
+ [
+ "is-number@7.0.0",
+ "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer"
+ ]
+ ],
+ "_from": "is-number@7.0.0",
+ "_id": "is-number@7.0.0",
+ "_inBundle": false,
+ "_integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "_location": "/is-number",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "is-number@7.0.0",
+ "name": "is-number",
+ "escapedName": "is-number",
+ "rawSpec": "7.0.0",
+ "saveSpec": null,
+ "fetchSpec": "7.0.0"
+ },
+ "_requiredBy": [
+ "/to-regex-range"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "_spec": "7.0.0",
+ "_where": "C:\\xampp\\htdocs\\Assembler\\filesystem-explorer",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/is-number/issues"
+ },
+ "contributors": [
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Olsten Larck",
+ "url": "https://i.am.charlike.online"
+ },
+ {
+ "name": "Rouven Weßling",
+ "url": "www.rouvenwessling.de"
+ }
+ ],
+ "description": "Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.",
+ "devDependencies": {
+ "ansi": "^0.3.1",
+ "benchmark": "^2.1.4",
+ "gulp-format-md": "^1.0.0",
+ "mocha": "^3.5.3"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/is-number",
+ "keywords": [
+ "cast",
+ "check",
+ "coerce",
+ "coercion",
+ "finite",
+ "integer",
+ "is",
+ "isnan",
+ "is-nan",
+ "is-num",
+ "is-number",
+ "isnumber",
+ "isfinite",
+ "istype",
+ "kind",
+ "math",
+ "nan",
+ "num",
+ "number",
+ "numeric",
+ "parseFloat",
+ "parseInt",
+ "test",
+ "type",
+ "typeof",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-number",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/is-number.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "related": {
+ "list": [
+ "is-plain-object",
+ "is-primitive",
+ "isobject",
+ "kind-of"
+ ]
+ },
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "7.0.0"
+}
diff --git a/node_modules/jquery/AUTHORS.txt b/node_modules/jquery/AUTHORS.txt
new file mode 100644
index 00000000..0d44990f
--- /dev/null
+++ b/node_modules/jquery/AUTHORS.txt
@@ -0,0 +1,331 @@
+John Resig
+Gilles van den Hoven
+Michael Geary
+Stefan Petre
+Yehuda Katz
+Corey Jewett
+Klaus Hartl
+Franck Marcia
+Jörn Zaefferer
+Paul Bakaus
+Brandon Aaron
+Mike Alsup
+Dave Methvin
+Ed Engelhardt
+Sean Catchpole