text:
.highlight table td { padding: 5px; }
.highlight table pre { margin: 0; }
.highlight .c, .highlight .cd {
  color: $comment;
  // font-style: italic;
}
.highlight .cm {
  color: $comment;
  // font-style: italic;
}
.highlight .c1 {
  color: $comment;
  // font-style: italic;
}
.highlight .cp {
  color: $comment;
  // font-weight: bold;
}
.highlight .cs {
  color: $comment;
  // font-weight: bold;
  // font-style: italic;
}
.highlight .err {
  color: #960050;
  background-color: #1e0010;
}
.highlight .gi {
  color: $text-color;
  background-color: #324932;
}
.highlight .gd {
  color: $text-color;
  background-color: #493131;
}
.highlight .ge {
  color: #000000;
  // font-style: italic;
}
.highlight .gr {
  color: #aa0000;
}
.highlight .gt {
  color: #aa0000;
}
.highlight .gh {
  color: #999999;
}
.highlight .go {
  color: #888888;
}
.highlight .gp {
  color: #555555;
}
.highlight .gs {
  // font-weight: bold;
}
.highlight .gu {
  color: #aaaaaa;
}
.highlight .k, .highlight .kv {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kc {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kd {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kp {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kr {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kt {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .kn {
  color: $code-red;
  // font-weight: bold;
}
.highlight .ow {
  color: $code-red;
  // font-weight: bold;
}
.highlight .o {
  color: $code-red;
  // font-weight: bold;
}
.highlight .mf {
  color: #ae81ff;
}
.highlight .mh {
  color: #ae81ff;
}
.highlight .il {
  color: #ae81ff;
}
.highlight .mi {
  color: #ae81ff;
}
.highlight .mo {
  color: #ae81ff;
}
.highlight .m, .highlight .mb, .highlight .mx {
  color: #ae81ff;
}
.highlight .se {
  color: #ae81ff;
}
.highlight .sb {
  color: $code-str;
}
.highlight .sc {
  color: $code-str;
}
.highlight .sd {
  color: $code-str;
}
.highlight .s2 {
  color: $code-str;
}
.highlight .sh {
  color: $code-str;
}
.highlight .si {
  color: $code-str;
}
.highlight .sx {
  color: $code-str;
}
.highlight .sr {
  color: $code-str;
}
.highlight .s1 {
  color: $code-str;
}
.highlight .ss {
  color: $code-str;
}
.highlight .s {
  color: $code-str;
}
.highlight .na {
  color: $code-purple;
}
.highlight .nc {
  color: $code-purple;
  // font-weight: bold;
}
.highlight .nd {
  color: $code-purple;
  // font-weight: bold;
}
.highlight .ne {
  color: $code-purple;
  // font-weight: bold;
}
.highlight .nf {
  color: $code-purple;
  // font-weight: bold;
}
.highlight .no {
  color: $code-blue;
}
.highlight .bp {
  color: $code-blue;
}
.highlight .nb {
  color: $code-blue;
}
.highlight .ni {
  color: $code-blue;
}
.highlight .nn {
  color: $code-blue;
}
.highlight .vc {
  color: $code-blue;
}
.highlight .vg {
  color: $code-blue;
}
.highlight .vi {
  color: $code-blue;
}
.highlight .nv {
  color: $code-blue;
}
.highlight .w {
  color: $code-blue;
}
.highlight .nl {
  color: $code-blue;
  // font-weight: bold;
}
.highlight .nt {
  color: $code-red;
}


text:
::-webkit-scrollbar {
	width: 6px;
	height: 6px;
}

::-webkit-scrollbar-button {
	width: 0px;
	height: 0px;
}

::-webkit-scrollbar-thumb {
	background: $text;
	border: none;
	border-radius: 6px;
}

::-webkit-scrollbar-thumb:hover {
	background: $text;
}

::-webkit-scrollbar-thumb:active {
	background: $text;
}

::-webkit-scrollbar-track {
	background: transparent;
	border: none;
}

::-webkit-scrollbar-track:hover {
	background: transparent;
}

::-webkit-scrollbar-track:active {
	background: transparent;
}

::-webkit-scrollbar-corner {
	background: transparent;
}

repo_name: leofavre/canivete

repo_language: JavaScript

mime_type: text/x-java


text:
.content {
	line-height: $grid;
	-webkit-overflow-scrolling: touch;
	-ms-overflow-style: -ms-autohiding-scrollbar;

	display: block;
	overflow: hidden;

	h1 {

	}

	&__logo {
		display: block;
		height: $grid * 16;
		margin-top: $grid;
		margin-left: -3.25rem;

		@media #{$smaller-than-landscape-tablet} {
			margin-top: $grid * 4;
			margin-left: auto;
			margin-right: auto;
			margin-bottom: $grid * -2;
		}

		@media #{$smaller-than-landscape-phone} {
			height: 65.45454545vw;
		}
	}

	&__main h2 {
		position: relative;
		left: ($grid * -3);
		margin: ($grid * 10) 0 0;
		padding: 0;
		font-size: 24rem;
		font-family: "Acropolis A", "Acropolis B", cursive;
		font-style: italic;
		font-weight: 800;
		color: $content-h2-color;
		text-transform: lowercase;
		line-height: 15rem;
		width: 61rem;
		overflow: visible;

		display: flex;
		flex-flow: row wrap;
		align-items: center;
		height: 96vh;

		overflow-wrap: break-word;
		word-wrap: break-word;

		&#about {
			margin-bottom: -1.5rem;
		}

		&#bem {
			@media #{$content-almost-touches-margins} { left: -6.25vw; }
		}

		&#classname {
			width: 65rem;
			@media #{$smaller-than-desktop} { width: 57rem; }
			@media #{$smaller-than-landscape-tablet} { width: 48rem; }
			@media #{$content-almost-touches-margins} { width: 96vw; }
		}

		&#collection {
			width: 58rem;
			@media #{$smaller-than-desktop} { width: 51rem; }
			@media #{$smaller-than-landscape-tablet} { width: 46rem; }
			@media #{$content-almost-touches-margins} { width: 96vw; }
		}

		&#dom {
			@media #{$content-almost-touches-margins} { width: 104vw; left: -6.25vw; }
		}

		&#geometry {
			width: 50rem;
			@media #{$smaller-than-desktop} { width: 44rem; left: 0; }
			@media #{$content-almost-touches-margins} { width: 94vw; }
		}

		&#promise {
			width: 50rem;
			@media #{$smaller-than-desktop} { width: 44rem; left: 0.75rem; }
			@media #{$content-almost-touches-margins} { width: 90vw; left: 1.5625vw; }
		}

		&#random {
			@media #{$content-almost-touches-margins} { width: 104vw; }
		}

		&#sort {
			@media #{$content-almost-touches-margins} { left: -4.6875vw; }
		}

		&#time {
			width: 45rem;
			@media #{$smaller-than-desktop} { width: 40rem; }
			@media #{$smaller-than-landscape-tablet} { width: 37rem; }
			@media #{$content-almost-touches-margins} { width: 76vw; }
		}

		@media #{$smaller-than-desktop} {
			left: ($grid * -2);
			font-size: 21rem;
			line-height: 13.5rem;
			width: 54rem;
		}

		@media #{$smaller-than-landscape-tablet} {
			margin-top: ($grid * 6);
			padding-top: ($grid * 6);
			width: 48rem;
			height: auto;

			font-size: 18.75rem;
			line-height: 0.65;
		}

		@media #{$content-almost-touches-margins} {
			font-size: 42vw;
			width: 100vw;
			left: -3.125vw;
		}

		@media #{$smaller-than-portrait-tablet} {
			margin-top: 0;
			padding-top: ($grid * 4);
			color: $content-h2-small-color;
		}
	}

	&__main h2 > * {
		position: relative;
		top: -0.05em;
		margin: auto 0;
		min-width: 0;
	}

	&__main h2 + blockquote {
		position: relative;
		margin-top: -33vh;
		margin-bottom: calc(33vh + #{$grid * 4});

		@media #{$smaller-than-landscape-tablet} {
			// margin-top: ($grid * -4);
			// margin-bottom: ($grid * 8);
			margin-top: 0;
			margin-bottom: 0;
		}

		@media #{$smaller-than-portrait-tablet} {
			margin-top: 0;
			margin-bottom: 0;
		}
	}

	&__main h3 {
		position: relative;
		top: 0;
		margin: ($grid * 8) ($grid * -2) ($grid * 2) ($grid * -2);
		font-size: 2rem;
		line-height: ($grid * 2);

		@media #{$smaller-than-portrait-tablet} {
			margin-top: ($grid * 1);
			margin-left: ($grid * -1);
			margin-right: ($grid * -1);
			margin-bottom: ($grid * 1.25);

			font-size: 1.35rem;
			line-height: ($grid * 1.35);
		}
	}

	&__main h3 a {
		position: relative;
		display: block;
		padding: (0.875rem + $grid) ($grid * 2) (0.625rem + $grid) ($grid * 2);

		@media #{$bigger-than-big-screen} {
			padding-top: 42px;
			padding-bottom: 39px;
		}

		@media #{$smaller-than-landscape-tablet} {
			padding-top: calc(#{$nav-mobile-height - 2} + #{0.875rem + $grid});
		}

		@media #{$smaller-than-portrait-tablet} {
			padding-top: calc(#{$nav-mobile-height - 2} + #{0.375rem + $grid});
			padding-left: $grid;
			padding-right: $grid;
		}

		&::before {
			content: "";
			position: absolute;
			bottom: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background: $content-h3-bg-color;
			z-index: -1;

			@media #{$smaller-than-landscape-tablet} {
				height: calc(100% - #{$nav-mobile-height - 2});
			}
		}
	}

	&__main h3 code {
		position: relative;
		color: $content-h3-color;
		font-weight: 500;
	}

	h4 {
		position: relative;
		top: 0.4375rem;
		font-size: 1.0625rem;
		font-weight: 800;
		line-height: $grid;
		letter-spacing: 0.045em;
		margin: ($grid * 2) 0 $grid;

		@media #{$bigger-than-big-screen} {
			top: 8px;
		}

		@media #{$smaller-than-portrait-tablet} {
			margin-top: $grid * 1.5;
		}
	}

	table + h4 {
		margin-top: $grid;
	}

	h5 {

	}

	h6 {

	}

	p,
	li {
		position: relative;
		top: 0.5rem;
		margin: $grid 0 $grid;
		font-size: 1rem;
		letter-spacing: -0.025em;
		line-height: $grid;
		max-width: 40rem;

		@media #{$bigger-than-big-screen} {
			top: calc(0.5rem - 1px);
		}

		@media #{$smaller-than-portrait-tablet} {
			line-height: $grid * 1.25;
		}

		@media #{$smaller-than-landscape-phone} {
			width: calc(100% + #{$grid * 0.5});
			hyphens: auto;
		}
	}

	p code {
		background-color: $small-code-bg-color;
		color: $small-code-color;
		white-space: nowrap;
		letter-spacing: normal;
	}

	p a code {
		@extend %a-hairline;
	}

	p a:hover code,
	p a:active code {
		background-color: mix($small-code-bg-color, $link-hover-color, 50%);
	}

	p a:hover code {
		@media (hover: none) { background-color: transparent; }
	}

	p a:active code {
		@media (hover: none) { background-color: mix($small-code-bg-color, $link-hover-color, 50%); }
	}

	strong {
		font-weight: 600;
	}

	blockquote {
		margin: 0;
	}

	blockquote p {
		@extend %subtitle;
		hyphens: none;
	}

	a {
		position: relative;
		color: inherit;
		text-decoration: none;
		z-index: 2;

		@extend %a-hairline;
		background-color: transparent;
	}

	%a-hairline {
		background-position: 0 1.05em;
		background-size: 1rem 1rem;
		background-image: linear-gradient(to bottom, $link-hairline-color 1px, transparent 1px);
		background-repeat: repeat-x;
	}

	a:hover,
	a:active {
		background-color: $link-hover-color;
	}

	a:hover {
		@media (hover: none) { background-color: transparent; }
	}

	a:active {
		@media (hover: none) { background-color: $link-hover-color; }
	}

	a[name] {
		border: none;
		background: transparent;
	}

	a[name]:hover,
	a[name]:active {
		background: transparent;
	}

	ul,
	ol {
		position: relative;
		top: 0.5rem;
		margin: $grid 0;
		padding-left: $grid;
		line-height: $grid;

		@media #{$bigger-than-big-screen} {
			top: calc(0.5rem - 1px);
		}
	}

	li {
		position: relative;
		list-style: none;
		top: 0;
		margin-top: 0;
		margin-bottom: 0;

		&::before {
			content: "";
			position: absolute;
			top: 0.625rem;
			left: -1.375rem;
			display: inline-block;
			width: 0.375rem;
			height: 0.375rem;
			background-color: $text-color;
			transform: rotate(45deg);
			transform-origin: center;
		}
	}

	pre {
		position: relative;
		display: block;
		top: 0.5625rem;
		color: $big-code-color;
		background-color: $big-code-bg-color;
		margin: $grid ($grid * -2) ($grid * 2) ($grid * -2);
		padding: $grid $grid $grid ($grid * 2);
		line-height: $grid;
		overflow: auto;
		font-size: 0.9375rem;

		@media #{$bigger-than-big-screen} {
			top: 9px;
			padding-top: 27px;
			padding-bottom: 27px;
		}

		@media #{$smaller-than-portrait-tablet} {
			margin-left: $grid * -1;
			margin-right: $grid * -1;
			padding-top: (0.25rem + $grid);
			padding-left: $grid;
			padding-right: $grid;
			font-size: 0.875rem;
			line-height: $grid;
		}
	}

	pre code,
	pre span {
		line-height: 1;
	}

	code {
		font-family: "Fira Code", monospace;
		tab-size: 4;
		line-height: 1;
		font-weight: 400;
	}

	textarea {

	}

	table {
		position: relative;
		top: -0.25rem;
		width: 100%;
		border-collapse: collapse;
		margin-bottom: $grid;

		@media #{$bigger-than-big-screen} {
			top: -5px;
		}

		@media #{$smaller-than-portrait-tablet} {
			display: block;
		}
	}

	thead {
		@media #{$smaller-than-portrait-tablet} {
			display: none;
		}
	}

	tbody {
		@media #{$smaller-than-portrait-tablet} {
			display: block;
		}
	}

	tr {
		position: relative;

		@media #{$smaller-than-portrait-tablet} {
			display: block;
			padding-top: 0.75rem;
			padding-bottom: 0.75rem;
		}
	}

	tbody::after {
		@media #{$smaller-than-portrait-tablet} {
			content: "";
			position: absolute;
			bottom: 0;
			left: 0;
			width: 100%;
			height: 1px;
			background: $table-hairline-color;
			transform: scaleY(0.5);
			transform-origin: 0 0;
		}
	}

	tbody tr::after {
		content: "";
		position: absolute;
		top: auto;
		left: 0;
		width: 100%;
		height: 1px;
		background: $table-hairline-color;
		transform: scaleY(0.5);
		transform-origin: 0 0;

		@media #{$smaller-than-portrait-tablet} {
			top: 0;
		}
	}

	th {
		position: relative;
		text-align: left;
		padding: 0 $grid 0 0;

		font-size: 0.5625rem;
		text-transform: uppercase;
		letter-spacing: 0.35em;
		font-weight: 500;
	}

	td {
		font-size: 0.875rem;
		font-family: "Whitney SSm A", "Whitney SSm B", sans-serif;
		vertical-align: top;
		padding: ($grid / 2) $grid ($grid / 2) 0;

		@media #{$bigger-than-big-screen} {
			padding-top: 14px;
			padding-bottom: 13px;
		}

		@media #{$smaller-than-portrait-tablet} {
			position: relative;
			display: block;
			width: auto;
			padding: 0.25rem 0;

			&:empty {
				display: none;
			}

			&::before {
				position: relative;
				display: block;

				font-size: 0.5625rem;
				font-weight: 600;
				text-transform: uppercase;
				letter-spacing: 0.175em;
				font-weight: 500;
			}

			$fake-col-width: 3.75rem;
			$fake-col-left: -3.75rem;

			&:nth-child(1):nth-last-child(3),
			&:nth-child(1):nth-last-child(4) {
				margin-left: $fake-col-width;

				&::before {
					position: absolute;
					left: $fake-col-left;
					margin-top: 0.0625rem;
					content: "name";
				}
			}

			&:nth-child(2):nth-last-child(3) {
				margin-left: $fake-col-width;

				&::before {
					position: absolute;
					left: $fake-col-left;
					margin-top: 0.0625rem;
					content: "default";
				}
			}

			&:nth-child(1):nth-last-child(1),
			&:nth-child(1):nth-last-child(2),
			&:nth-child(2):nth-last-child(2),
			&:nth-child(3):nth-last-child(2) {
				margin-left: $fake-col-width + 0.3125rem;

				&::before {
					position: absolute;
					left: $fake-col-left - 0.3125rem;
					margin-top: 0.0625rem;
					content: "type";
				}
			}

			&:nth-child(2):nth-last-child(1),
			&:nth-child(3):nth-last-child(1),
			&:nth-child(4):nth-last-child(1) {
				&::before {
					// content: "description";
				}
			}
		}
	}

	td strong {
		position: relative;
		top: -0.125rem;
		font-family: "Whitney Narrow SSm A", "Whitney Narrow SSm B", sans-serif;
		font-size: 0.5625rem;
		text-transform: uppercase;
		letter-spacing: 0.115em;
		font-weight: 600;
		line-height: 0.875rem;
		margin-left: 0.125rem;
		padding: 1px 0.25rem;
		background: $optional-bg-color;
		color: $optional-color;
	}

	td code {
		font-weight: 400;
		padding: 1px 0.25rem;
		background-color: $small-code-bg-color;
		color: $small-code-color;
		white-space: nowrap;
	}

	td em {
		font-style: normal;
	}

	td:nth-last-child(1) {
		padding-right: 0;
	}

	td:nth-last-child(n + 2) {
		white-space: nowrap;
	}

	[id^="return"] + table,
	[id^="parameter"] + table {
		td:nth-child(1):nth-last-child(4),
		td:nth-child(2):nth-last-child(3),
		td:nth-child(3):nth-last-child(2) {
			min-width: 20%;
		}

		td:nth-child(1):nth-last-child(3),
		td:nth-child(2):nth-last-child(2),
		td:nth-child(1):nth-last-child(2) {
			width: 20%;

			@media #{$smaller-than-portrait-tablet} {
				width: auto;
			}
		}
	}
}

.content__inner {
	margin-left: calc(#{$nav-width} + (0.25 * (100vw - #{$nav-width + $full-content-width + $content-left-padding + $content-right-padding})));
	margin-bottom: 50vh;
	padding: 0 $content-right-padding 0 $content-left-padding;
	max-width: $full-content-width;

	@media #{$way-bigger-than-big-screen} {
		margin-left: auto;
		margin-right: auto;
	}

	@media #{$content-touches-menu} {
		margin-left: $nav-width;
	}

	@media #{$smaller-than-landscape-tablet} {
		margin-left: auto;
		margin-right: auto;
		padding: 0 $content-right-padding;
	}

	@media #{$smaller-than-portrait-tablet} {
		padding: 0 $grid;
	}

	&--grid {
		@extend %grid;
	}
}

%subtitle {
    position: relative;
    left: -0.25rem;
    margin: $grid 0;
    font-family: "Whitney Narrow A", "Whitney Narrow B", sans-serif;
    font-style: normal;
    font-weight: 300;
    font-size: 2.5rem;
    line-height: ($grid * 2);
    word-spacing: 0.15em;

    @media #{$smaller-than-desktop} {
        font-size: 2rem;
    }

    @media #{$smaller-than-portrait-tablet} {
        font-size: 1.75rem;
        line-height: ($grid * 1.5);
    }

    a {
		@media #{$bigger-than-desktop} {
			background-image: linear-gradient(to bottom, $link-hairline-color 2px, transparent 1px);
		}
	}
}

.content__intro {
	@media #{$bigger-than-landscape-tablet} {
		> *:nth-child(1), {
			// margin-top: 0;
		}
	}

	h2 {
		position: relative;
		display: block;
		margin: ($grid * 2) 0 -0.25rem;
		padding: ($grid + 0.25rem) 0 0;
		height: auto;
		left: -0.5rem;
		color: $intro-h2-color;
		font-size: 4.5rem;
		font-family: "Acropolis A", "Acropolis B", cursive;
		font-style: italic;
		font-weight: 800;
		line-height: 1;
		text-transform: lowercase;

		@media #{$smaller-than-landscape-tablet} {
			margin: 0 0 -0.25rem;
			padding: ($grid * 4 + 0.25rem) 0 0;
		}

		@media #{$smaller-than-portrait-tablet} {
			color: $intro-h2-small-color;
		}
	}

	h2 > * {
		position: static;
	}

	h3 {
		@extend %subtitle;
		margin-top: $grid;
		margin-bottom: ($grid * 2) - 0.625rem;
		padding-top: ($grid * 1) + 0.625rem;
		word-spacing: normal;
		background: none;
		font-weight: 300;
		color: $intro-h3-color;
		letter-spacing: -0.015em;
		word-spacing: normal;

		@media #{$smaller-than-desktop} {
			margin-top: 4.6875rem;
			margin-bottom: 2.1875rem;
		}

		@media #{$smaller-than-landscape-tablet} {
			margin-top: 0;
			padding-top: 5.3125rem;
		}

		a {
			display: inline;
			padding: 0;
		}
	}

	hr {
		display: none;
	}

	pre {
		margin-left: 0;
		margin-right: 0;
		padding-left: $grid;
		padding-right: $grid;

		@media #{$smaller-than-portrait-tablet} {
			margin-left: $grid * -1;
			margin-right: $grid * -1;
			padding-top: (0.25rem + $grid);
			padding-left: $grid;
			padding-right: $grid;
		}
	}

	h2 ~ p strong a {
		display: inline-block;
		margin-top: 1rem;
		padding: 1rem 1.125rem;
		border: 1px solid $button-color;
		color: $button-color;
		font-family: "Acropolis A", "Acropolis B", cursive;
		font-style: italic;
		font-weight: 800;
		text-transform: lowercase;
		font-size: 1.75rem;
		line-height: 1.5rem;

		position: relative;
		left: -0.25rem;
		margin: $grid 0;
		font-family: "Whitney Narrow A", "Whitney Narrow B", sans-serif;
		font-style: normal;
		font-weight: 300;
		font-size: 2.5rem;
		line-height: 3rem;
		word-spacing: 0.15em;

		&::after {
			display: none;
		}
	}

	#using-canivete-with-rollup + p,
	#using-canivete-with-webpack + p,
	h3 ~ .highlighter-rouge + p {
		text-indent: $grid * -1;
		margin-left: $grid;
		width: calc(100% - #{$grid * 0.5});

		strong {
			display: inline-block;
			width: 1.1875rem;
			text-indent: 0;
		}
	}

	a[href*="github.com/leofavre/canivete"] {
		display: inline-block;
		padding: 0.75rem 1rem 0.75rem 3.75rem;
		background: $text-color;
		color: #fff;
		font-weight: 600;

		&:before {
			content: "";
			position: absolute;
			top: 0;
			left: 0.8rem;
			display: block;
			vertical-align: middle;
			width: 2.35rem;
			height: 100%;
			background: url(../media/github.svg) no-repeat center;
			background-size: contain;
		}

		&:hover,
		&:active {
			background-color: $logo-hover-color;
		}

		&:hover {
			@media (hover: none) { background-color: $text-color; }
		}

		&:active {
			@media (hover: none) { background-color: $logo-hover-color; }
		}
	}
}


text:
html {
    font-size: 16px;
}

@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Regular.eot");
    src: url("../fonts/eot/FiraCode-Regular.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Regular.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Regular.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Regular.ttf") format("truetype");
    font-weight: 400;
    font-style: normal;
}

@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Medium.eot");
    src: url("../fonts/eot/FiraCode-Medium.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Medium.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Medium.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Medium.ttf") format("truetype");
    font-weight: 500;
    font-style: normal;
}


text:
%grid {
	background-position: 0.3125rem 0;
	background-size: $grid $grid;
	background-image: linear-gradient(to bottom, lightblue 1px, transparent 1px);
	background-attachment: scroll;
}


text:
html {
	height: auto;
	font-size: 16px;
	-ms-text-size-adjust: 100%;
	-webkit-text-size-adjust: 100%;
	-webkit-tap-highlight-color: rgba(0,0,0,0);
	-webkit-tap-highlight-color: transparent;

	@media #{$bigger-than-big-screen} {
		font-size: 18px;
	}

	@media #{$smaller-than-landscape-phone} {
		font-size: 15px;
	}
}

html.mode--native-scroll {
	height: auto;
}

html.mode--local-scroll {
	height: 100%;
}


text:
$grid: 1.5rem;
$full-content-width: 42rem;
$nav-width: 14.5rem;
$nav-mobile-height: 40px;

$content-left-padding: ($grid * 3);
$content-right-padding: ($grid * 2);

// breakpoints

$way-bigger-than-big-screen: "only screen and (min-width: 1672px)";
$bigger-than-big-screen: "only screen and (min-width: 1400px)";
$bigger-than-desktop: "only screen and (min-width: 1101px)";
$smaller-than-desktop: "only screen and (max-width: 1100px)";
$bigger-than-landscape-tablet: "only screen and (min-width: 1024px)";
$smaller-than-landscape-tablet: "only screen and (max-width: 1023px)";
$content-touches-menu: "only screen and (max-width: #{$nav-width + $full-content-width + $content-left-padding + $content-right-padding})";
$content-almost-touches-margins: "only screen and (max-width: 820px)";
$smaller-than-portrait-tablet: "only screen and (max-width: 700px)";
$smaller-than-landscape-phone: "only screen and (max-width: 550px)";
$portrait-phone: "only screen and (max-width: 400px)";

// bubble gum color scheme

$background-color: mix(#fbfbf7, white, 100%);
$text-color: #3d464d;

$logo-color: #fb1e70;
$logo-hover-color: #c02e75;

$intro-h2-color: $text-color;
$intro-h2-small-color: $text-color;
$intro-h3-color: $text-color;

$button-color: $logo-color;
$button-hover-color: $logo-hover-color;

$content-h2-color: #c7efef;
$content-h2-small-color: mix(#7ae1e6, $content-h2-color, 15%);
$content-h3-color: #7ae1e6;
$content-h3-bg-color: $text-color;

$small-code-color: $text-color;
$small-code-bg-color: $content-h2-color;
$optional-color: $background-color;
$optional-bg-color: $text-color;

$link-hairline-color: $logo-color;
$link-hover-color: rgba($link-hairline-color, 0.2);

$menu-hairline-color: mix($text-color, $background-color, 50%);
$table-hairline-color: $menu-hairline-color;

$big-code-color: $text-color;
$big-code-bg-color: mix(#f0f3f5, black, 95%);

$code-blue: #07a;
$code-red: $logo-color;
$code-str: #46a805;
$code-purple: #813dff;
$comment: $menu-hairline-color;

$menu-item-color: $text-color;
$menu-item-selected-bg: $big-code-bg-color;
$menu-item-hover-bg: mix($menu-item-selected-bg, $background-color, 25%);

$nav-mobile-header-background: #32383e;
$nav-switch-color: $background-color;


text:
.page {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	font-family: "Whitney SSm A", "Whitney SSm B", sans-serif;

	margin: 0;
	color: $text-color;
	background: $background-color;

	height: auto;

	.mode--native-scroll & {
		height: auto;
	}

	.mode--local-scroll & {
		height: 100%;
	}
}

.page__inner {
	position: relative;
	display: block;
	margin: 0 auto;
	width: 100%;
	height: auto;
	overflow: auto;

	-webkit-overflow-scrolling: touch;
	-ms-overflow-style: -ms-autohiding-scrollbar;

	.mode--native-scroll & {
		height: auto;
		overflow: auto;
	}

	.mode--local-scroll & {
		height: 100%;
		overflow-x: hidden;
		overflow-y: auto;
	}
}


text:
.nav {
	position: fixed;

	flex: 0 0 $nav-width;
	margin: 0;
	padding: $grid ($grid * 0.5) $grid ($grid * 1.5);
	height: 100%;
	box-sizing: border-box;

	display: flex;
	flex-flow: row wrap;

	overflow-x: hidden;
	overflow-y: auto;

	white-space: nowrap;
	-webkit-overflow-scrolling: touch;
	-ms-overflow-style: -ms-autohiding-scrollbar;
	z-index: 5;

	&--grid {
		@extend %grid;
	}

	@media #{$smaller-than-landscape-tablet} {
		display: block;
		margin: 0;
		padding: 0;
		width: 0;
		height: 100%;
		overflow: visible;
	}

	h1 {
		position: relative;
		left: -0.5rem;
		margin: $grid 0;
		font-size: $grid * 1.5;
		line-height: $grid;
		font-family: "Acropolis A", "Acropolis B", cursive;
		font-style: italic;
		font-weight: 800;
		text-transform: lowercase;
		color: $logo-color;

		span {
			display: inline-block;
			cursor: pointer;
			color: $logo-color;
		}

		span:hover,
		span:active {
			color: $logo-hover-color;
		}

		span:hover {
			@media (hover: none) { color: $logo-color; }
		}

		span:active {
			@media (hover: none) { color: $logo-hover-color; }
		}

		@media #{$bigger-than-big-screen} {
			top: 1px;
		}

		@media #{$smaller-than-landscape-tablet} {
			position: fixed;
			display: block;
			top: 0;
			left: 0;
			margin: 0;
			padding: 0;
			width: 100%;
			background: $nav-mobile-header-background;
			box-sizing: border-box;
			font-size: 24px;
			text-align: center;
			line-height: $nav-mobile-height;
			// letter-spacing: 0.01em;
			z-index: 10;
		}
	}

	ul {
		position: relative;
		top: 0.5rem;
		margin: 0 0 0 -2.25rem;
		padding: 0;

		@media #{$bigger-than-big-screen} {
			top: 8px;
		}
	}

	ul ul {
		top: 0;
		margin: 0;
		overflow: hidden;
	}

	ul ~ ul::before {
		content: "";
		position: relative;
		display: block;
		margin: $grid auto $grid 2.25rem;
		width: 118px;
		height: 1px;
		background: $menu-hairline-color;
		transform: scaleY(0.5);
		transform-origin: 0 0;
	}

	li {
		position: relative;
		list-style: none;
		line-height: $grid;
		font-size: 14px;

		@media #{$bigger-than-big-screen} {
			top: 2px;
		}
	}

	li strong {
		position: relative;
		top: -0.0625rem;
		font-size: 0.875rem;
		font-weight: 800;
		line-height: $grid;
		letter-spacing: 0.045em;
		color: $menu-item-color;

		@media #{$bigger-than-big-screen} {
			top: -3px;

			a {
				margin-bottom: -2px;
				padding-top: 1px;
				padding-bottom: 1px;
			}
		}
	}

	li li {
		top: -0.0625rem;

		@media #{$bigger-than-big-screen} {
			top: -1px;
		}
	}

	ul ~ ul li li:nth-last-child(1) {
		margin-bottom: $grid;
	}

	ul ~ ul li li a {
		// text-indent: $grid / 2;
	}

	li a {
		padding-left: 2.25rem;
		padding-right: 2.25rem;
	}

	a {
		position: relative;
		display: block;
		width: calc(100% - #{$grid / 2});
		color: inherit;
		text-decoration: none;
		box-sizing: border-box;
	}

	a::before {
		content: "";
		display: block;
		margin-bottom: ($grid * -1);
		margin-left: -2.25rem;
		width: calc(100% + 4.5rem);
		height: $grid;
	}

	a:hover::before,
	a:active::before {
		background-color: $menu-item-hover-bg;
	}

	a:hover::before {
		@media (hover: none) { background-color: transparent; }
	}

	a:active::before {
		@media (hover: none) { background-color: $menu-item-hover-bg; }
	}

	a.selected::before {
		background-color: $menu-item-selected-bg;
	}
}

.nav__inner {
	margin-right: auto;

	&--grid {
		@extend %grid;
	}
}

.nav__drawer {
	@media #{$smaller-than-landscape-tablet} {
		position: fixed;
		top: $nav-mobile-height - 2;
		left: 0;
		height: calc(100% - #{$nav-mobile-height - 2});
		background: #fff;
		overflow: auto;

		box-shadow: 5px 0 2px -2px rgba(#000, 0.1);

		transform: translateX(0%);
		transition: transform 0.22s ease-in-out;

		ul {
			margin: 0;
		}

		> ul:nth-child(1) {
			margin-top: $grid;
		}
	}

	&--off {
		@media #{$smaller-than-landscape-tablet} {
			transform: translateX(-105%);
		}
	}
}

.nav__switch {
	position: absolute;
	display: none;
	top: 0;
	left: 0;
	padding: 12px;
	z-index: 15;

	@media #{$smaller-than-landscape-tablet} {
		display: block;
		cursor: pointer;

		> * {
			fill: $nav-switch-color;
			transition: transform 0.12s ease-in-out;

			&:nth-child(1) {
				transform: rotate(45deg) translate(4px, -4px);
				transform-origin: 0% 0%;
			}

			&:nth-child(2) {
				transform: scaleY(0);
				transform-origin: center;
			}

			&:nth-child(3) {
				transform: rotate(-45deg) translate(4px, 4px);
				transform-origin: 0% 100%;
			}
		}
	}
}

.nav__switch--off {
	@media #{$smaller-than-landscape-tablet} {
		> * {
			&:nth-child(1) {
				transform: rotate(0) translate(0px, 0px);
				transform-origin: 0% 0%;
			}

			&:nth-child(2) {
				transform: scaleY(1);
				transform-origin: center;
			}

			&:nth-child(3) {
				transform: rotate(0) translate(0px, 0px);
				transform-origin: 0% 100%;
			}
		}
	}
}


text:
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  return typeof obj;
} : function (obj) {
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};

/**
 * Copyright (c) 2007-2015 Ariel Flesler - aflesler ○ gmail • com | http://flesler.blogspot.com
 * Licensed under MIT
 * @author Ariel Flesler
 * @version 2.1.3
 */
(function (f) {
  "use strict";
  "function" === typeof define && define.amd ? define(["jquery"], f) : "undefined" !== typeof module && module.exports ? module.exports = f(require("jquery")) : f(jQuery);
})(function ($) {
  "use strict";
  function n(a) {
    return !a.nodeName || -1 !== $.inArray(a.nodeName.toLowerCase(), ["iframe", "#document", "html", "body"]);
  }function h(a) {
    return $.isFunction(a) || $.isPlainObject(a) ? a : { top: a, left: a };
  }var p = $.scrollTo = function (a, d, b) {
    return $(window).scrollTo(a, d, b);
  };p.defaults = { axis: "xy", duration: 0, limit: !0 };$.fn.scrollTo = function (a, d, b) {
    "object" === (typeof d === "undefined" ? "undefined" : _typeof(d)) && (b = d, d = 0);"function" === typeof b && (b = { onAfter: b });"max" === a && (a = 9E9);b = $.extend({}, p.defaults, b);d = d || b.duration;var u = b.queue && 1 < b.axis.length;u && (d /= 2);b.offset = h(b.offset);b.over = h(b.over);return this.each(function () {
      function k(a) {
        var k = $.extend({}, b, { queue: !0, duration: d, complete: a && function () {
            a.call(q, e, b);
          } });r.animate(f, k);
      }if (null !== a) {
        var l = n(this),
            q = l ? this.contentWindow || window : this,
            r = $(q),
            e = a,
            f = {},
            t;switch (typeof e === "undefined" ? "undefined" : _typeof(e)) {case "number":case "string":
            if (/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(e)) {
              e = h(e);break;
            }e = l ? $(e) : $(e, q);case "object":
            if (e.length === 0) return;if (e.is || e.style) t = (e = $(e)).offset();}var v = $.isFunction(b.offset) && b.offset(q, e) || b.offset;$.each(b.axis.split(""), function (a, c) {
          var d = "x" === c ? "Left" : "Top",
              m = d.toLowerCase(),
              g = "scroll" + d,
              h = r[g](),
              n = p.max(q, c);t ? (f[g] = t[m] + (l ? 0 : h - r.offset()[m]), b.margin && (f[g] -= parseInt(e.css("margin" + d), 10) || 0, f[g] -= parseInt(e.css("border" + d + "Width"), 10) || 0), f[g] += v[m] || 0, b.over[m] && (f[g] += e["x" === c ? "width" : "height"]() * b.over[m])) : (d = e[m], f[g] = d.slice && "%" === d.slice(-1) ? parseFloat(d) / 100 * n : d);b.limit && /^\d+$/.test(f[g]) && (f[g] = 0 >= f[g] ? 0 : Math.min(f[g], n));!a && 1 < b.axis.length && (h === f[g] ? f = {} : u && (k(b.onAfterFirst), f = {}));
        });k(b.onAfter);
      }
    });
  };p.max = function (a, d) {
    var b = "x" === d ? "Width" : "Height",
        h = "scroll" + b;if (!n(a)) return a[h] - $(a)[b.toLowerCase()]();var b = "client" + b,
        k = a.ownerDocument || a.document,
        l = k.documentElement,
        k = k.body;return Math.max(l[h], k[h]) - Math.min(l[b], k[b]);
  };$.Tween.propHooks.scrollLeft = $.Tween.propHooks.scrollTop = { get: function get$$1(a) {
      return $(a.elem)[a.prop]();
    }, set: function set$$1(a) {
      var d = this.get(a);if (a.options.interrupt && a._last && a._last !== d) return $(a.elem).stop();var b = Math.round(a.now);d !== b && ($(a.elem)[a.prop](b), a._last = this.get(a));
    } };return p;
});

/*global jQuery */
/*!
 * Kerning.js
 * Version: 0.2.1
 * Copyright 2011 Joshua Gross
 * MIT license
 *
 * Usage:
 *  Include this file anywhere in your HTML
 *
 *
 *  Then, add any of the attributes following to your CSS, under any
 *  selectors you want modified:
 *    -letter-kern, -letter-size, -letter-weight, -letter-color, -letter-transform
 *    -word-kern, -word-size, -word-weight, -word-color, -word-transform
 *
 *  To use pairings (e.g., modify 'a' if 'ab' is paired):
 *    -letter-pairs('xy': [value], …)
 *    -word-pairs('cat mouse': [value], …)
 *
 *  To use multiple transforms, you need to use transform "groups":
 *    -transform-group([transform] [transform] …)
 *
 *  Sometimes you need to want to use a different size or weight, depending on what
 *  font has loaded:
 *    font-size: [default size];
 *    font-size: if-font('font name': [size], 'font name': [size], …);
 *  (The first line is a fallback should Kerning.js not load. This is recommended.)
 *
 *  That's it! Attributes will be applied automagically.
 *
 * Examples:
 *  Alter first 3 letters:
 *    -letter-size: 100px 20px 30px;
 *
 *  Modify letter pairs:
 *    -letter-kern: -letter-pairs('ab': 1px,
 *                                'bc': 300px,
 *                                's ': 100px);
 *
 *  Transform the first two letters:
 *    -letter-transform: -transform-group(rotate3d(0,0,1,10deg) translate3d(0,10px,0))
 *                       -transform-group(translate3d(0,-10px,0) rotate3d(0,0,1,-10deg));
 *
 *  Modify word pairs:
 *    -word-size: -word-pairs('this is': 10em);
 *
 *  Modify the first 3 words:
 *    -word-size: 10em 0.1em 0.2em;
 *
 *  Using repeat rules:
 *    -letter-color: -letter-repeat(even: #f0f0f0, odd: #cccccc);
 *    -letter-color: -letter-repeat(2n+1: #f0f0f0);
 *
 *  Using conditionals:
 *    -letter-kern: if-font('Helvetica Neue': 0 1px 1px, 'Helvetica': 0 -1px 0);
 *
 *  Using conditionals on existing properties for weight or size:
 *    font-size: 9.5em;
 *    font-size: if-font('Helvetica Neue': 10em, 'Helvetica': 9em);
 */
(function ($) {
  (function () {
    function parsedeclarations(e) {
      var t = munged[e].replace(/^{|}$/g, "");t = munge(t);var n = {};$.each(t.split(";"), function (e, t) {
        t = t.split(":");if (t.length < 2) return;n[restore(t[0])] = restore(t.slice(1).join(":"));
      });return n;
    }function munge(e, t) {
      e = e.replace(REatcomment, "$1").replace(REcomment_string, function (e, t) {
        if (!t) return "";var n = "%s`" + ++uid + "`s%";munged[uid] = t.replace(/^\\/, "");return n;
      });var n = t ? REfull : REbraces;while (match = n.exec(e)) {
        replacement = "%b`" + ++uid + "`b%";munged[uid] = match[0];e = e.replace(n, replacement);
      }return e;
    }function restore(e) {
      if (e === undefined) return e;while (match = REmunged.exec(e)) {
        e = e.replace(REmunged, munged[match[1]]);
      }return $.trim(e);
    }function processAtRule(e, t) {
      var n = e.split(/\s+/);var r = n.shift();if (r == "media") {
        var i = restore(n.pop()).slice(1, -1);if ($.parsecss.mediumApplies(n.join(" "))) {
          $.parsecss(i, t);
        }
      } else if (r == "import") {
        var s = restore(n.shift());if ($.parsecss.mediumApplies(n.join(" "))) {
          s = s.replace(/^url\(|\)$/gi, "").replace(/^["']|["']$/g, "");$.get(s, function (e) {
            $.parsecss(e, t);
          });
        }
      }
    }function styleAttributes(e, t) {
      var n = "",
          r,
          i = {};e = e.replace(RESGMLcomment, "").replace(REnotATag, "$1");munge(e).replace(REtag, function (e, t, s) {
        t = t.toLowerCase();if (i[t]) ++i[t];else i[t] = 1;if (r = /\bstyle\s*=\s*(%s`\d+`s%)/i.exec(s)) {
          var o = /\bid\s*=\s*(\S+)/i.exec(s);if (o) o = "#" + restore(o[1]).replace(/^['"]|['"]$/g, "");else o = t + ":eq(" + (i[t] - 1) + ")";n += [o, "{", restore(r[1]).replace(/^['"]|['"]$/g, ""), "}"].join("");
        }
      });$.parsecss(n, t);
    }var hasSameOrigin = function hasSameOrigin(e) {
      return e === e.replace(/^([^\/]*)\/\/([^@]*@)?([^\/:]+)(:\d+)?.*/, function (t, n, r, i, s, o, u) {
        n = n === "" ? window.location.protocol : n;s = s === undefined ? "" : s.substring(1);if (n !== window.location.protocol) return "";if (i !== window.location.hostname) return "";if (s !== window.location.port) return "";return e;
      });
    };$.fn.findandfilter = function (e) {
      var t = this.filter(e).add(this.find(e));t.prevObject = t.prevObject.prevObject;return t;
    };$.fn.parsecss = function (e, t) {
      var n = function n(t) {
        $.parsecss(t, e);
      };this.findandfilter("style").each(function () {
        n(this.innerHTML);
      }).end().findandfilter('link[type="text/css"],link[rel="stylesheet"]').each(function () {
        if (!this.disabled && hasSameOrigin(this.href) && $.parsecss.mediumApplies(this.media)) $.get(this.href, n);
      }).end();if (t) {
        $.get(location.pathname + location.search, "text", function (t) {
          styleAttributes(t, e);
        });
      }return this;
    };$.parsecss = function (e, t) {
      var n = {};e = munge(e).replace(/@(([^;`]|`[^b]|`b[^%])*(`b%)?);?/g, function (e, n) {
        processAtRule($.trim(n), t);return "";
      });$.each(e.split("`b%"), function (e, t) {
        t = t.split("%b`");if (t.length < 2) return;t[0] = restore(t[0]);n[t[0]] = $.extend(n[t[0]] || {}, parsedeclarations(t[1]));
      });t(n);
    };$.parsecss.mediumApplies = window.media && window.media.query || function (e) {
      if (!e) return true;if (e in media) return media[e];var t = $('').appendTo("head");return media[e] = [$("body").css("z-index") == 1, t.remove()][0];
    };$.parsecss.isValidSelector = function (e) {
      var t = $("").appendTo("head")[0];return [t.styleSheet ? !/UNKNOWN/i.test(t.styleSheet.cssText) : !!t.sheet.cssRules.length, $(t).remove()][0];
    };$.parsecss.parseArguments = function (str) {
      if (!str) return [];var ret = [],
          mungedArguments = munge(str, true).split(/\s+/);for (var i = 0; i < mungedArguments.length; ++i) {
        var a = restore(mungedArguments[i]);try {
          ret.push(eval("(" + a + ")"));
        } catch (err) {
          ret.push(a);
        }
      }return ret;
    };$.parsecss.styleAttributes = styleAttributes;var media = {};var munged = {};var REbraces = /{[^{}]*}/;var REfull = /\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/;var REatcomment = /\/\*@((?:[^\*]|\*[^\/])*)\*\//g;var REcomment_string = /(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g;var REmunged = /%\w`(\d+)`\w%/;var uid = 0;var _show = { show: $.fn.show, hide: $.fn.hide };$.each(["show", "hide"], function () {
      var e = this,
          t = _show[e],
          n = e + "Default";$.fn[e] = function () {
        if (arguments.length > 0) return t.apply(this, arguments);return this.each(function () {
          var e = $.data(this, n),
              r = $(this);if (e) {
            $.removeData(this, n);e.call(r);r.queue(function () {
              r.data(n, e).dequeue();
            });
          } else {
            t.call(r);
          }
        });
      };$.fn[n] = function () {
        var t = $.makeArray(arguments),
            r = t[0];if ($.fn[r]) {
          t.shift();var i = $.fn[r];
        } else if ($.effects && $.effects[r]) {
          if (_typeof(t[1]) != "object") t.splice(1, 0, {});i = _show[e];
        } else {
          i = _show[e];
        }return this.data(n, function () {
          i.apply(this, t);
        });
      };
    });var RESGMLcomment = //g;var REnotATag = /(>)[^<]*/g;var REtag = /<(\w+)([^>]*)>/g;
  })();(function () {
    function e(e, t, n, r) {
      var i = e.text().split(t),
          s = "";if (i.length) {
        $(i).each(function (e, t) {
          s += '' + t + "" + r;
        });e.empty().append(s);
      }
    }var t = { init: function init() {
        return this.each(function () {
          e($(this), "", "char", "");
        });
      }, words: function words() {
        return this.each(function () {
          e($(this), " ", "word", " ");
        });
      }, lines: function lines() {
        return this.each(function () {
          var t = "eefec303079ad17405c889e092e105b0";e($(this).children("br").replaceWith(t).end(), t, "line", "");
        });
      } };$.fn.lettering = function (e) {
      if (e && t[e]) {
        return t[e].apply(this, [].slice.call(arguments, 1));
      } else if (e === "letters" || !e) {
        return t.init.apply(this, [].slice.call(arguments, 0));
      }$.error("Method " + e + " does not exist on jQuery.lettering");return this;
    };
  })();var unstack = function () {
    var e = { init: function init(e) {
        var t = $(e).css("font-family").match(/[^'",;\s][^'",;]*/g) || [];return this.analyzeStack(t, e);
      }, analyzeStack: function analyzeStack(t, n) {
        var r = ["monospace", "sans-serif", "serif", "cursive", "fantasy"];var i = r[0];var s = t.length;var o = t[s - 1];if ($.inArray(o, r)) {
          t.push(i);s++;
        }if (o == i) {
          i = r[1];
        }for (var u = 0; u < s - 1; u++) {
          font = t[u];if (e.testFont(font, i)) {
            return font;
          }
        }
      }, testFont: function testFont(e, t) {
        var n = $('mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmml');$("body").prepend(n);var r = n.width();n.css("font-family", e + "," + t);var i = n.width();n.remove();return i != r;
      } };return function (t) {
      return e.init(t);
    };
  }();window.Kerning = new function () {
    var e = this,
        t = navigator.platform,
        n = ["webkitTransform" in document.documentElement.style && "webkit", navigator.userAgent.indexOf("MSIE") > -1 && "ms", "MozTransform" in document.documentElement.style && "moz", window.opera && "o"].reduce(function (e, t) {
      return e + (t || "");
    }),
        r = [t.match(/Mac/) && "mac", t.match(/Win/) && "win", t.match(/Linux/) && "linux"].reduce(function (e, t) {
      return e + (t || "");
    });var i = { _pairs: function _pairs(e, t, n) {
        var r = n.match(/^-(letter|word)-pairs\(([\s\S]+)\)$/i);if (!r || r[1] !== e) return false;var i = e === "word" ? t.children("span") : t.find("span > span"),
            s = n.match(/translate|rotate|skew|perspective/i),
            o = $.trim(r[2].replace(/,\s+?'/g, ",'").replace(/:\s+?(\d)/g, ":$1")).split(s ? ")," : ","),
            u,
            a,
            f,
            l = [];if (!o) return;$.each(o, function (t, n) {
          u = n.split(":");u[0] = u[0].replace(/^['"](.+)['"]$/g, "$1");if (e === "word") a = u[0].split(" ");else a = u[0];f = function f(t) {
            var n = $(this).text().match(new RegExp(a[0])),
                r,
                i;if (a[1] !== " ") {
              i = ($(this).next().html() || "").match(new RegExp(a[1]));
            } else {
              r = e == "word" ? $(this).next('[class^="word"]') : $(this).parent().next('[class^="word"]');i = !$(this).next().length && r.length;
            }return n && i;
          };l.push([u[1], i.filter(f)]);
        });return l;
      }, _repeats: function _repeats(e, t, n) {
        var r = n.match(/^-(letter|word)-repeats\(([\s\S]+)\)$/i);if (!r || r[1] !== e) return false;var i = e === "word" ? t.children("span") : t.find("span > span"),
            s = n.match(/translate|rotate|skew|perspective/i),
            o = $.trim(r[2].replace(/,\s+?'/g, ",'").replace(/:\s+?(\d)/g, ":$1")).split(s ? ")," : ","),
            u,
            a,
            f,
            l = [];if (!o) return;$.each(o, function (e, t) {
          u = t.split(":");if (s && u[1].substring(u[1].length - 1) !== ")") u[1] += ")";l.push([$.trim(u[1]), i.filter(":nth-child(" + $.trim(u[0]) + ")")]);
        });return l;
      }, _conditional: function _conditional(e, t, n) {
        var r = n.match(/^(?:-(letter|word)-)?if-font\(([\s\S]+)\)$/i);if (!r) return;var i = e === "all" ? t : e === "word" ? t.children("span") : t.find("span > span"),
            s = n.match(/translate|rotate|skew|perspective/i),
            o = r[2].replace(/\n/g, "").match(/['"][^'"]+['"]:\s*.+?(\)|(?=\w),\s['"]|$)/g),
            u,
            a = {},
            f = [];if (!o) return;t.each(function (e, t) {
          var n = unstack(t).replace(/^['"](.+)['"]$/g, "$1");if (!a[n]) a[n] = [t];else a[n].push(t);
        });$.each(o, function (e, t) {
          u = t.match(/['"]([^'"]+)['"]:\s*(.+)$/);if (!u) return true;u = u.splice(1);if (u[0] in a) f.push([$.trim(u[1]), $(a[u[0]])]);
        });return f;
      }, _applyAttribute: function _applyAttribute(e, t, n, r) {
        var s = i._conditional(e, t, r);if (!s || !s.length) s = [[r, t]];$.each(s, function (t, r) {
          var s = r[0],
              o = r[1];var u = i._pairs(e, o, s) || i._repeats(e, o, s);if (u) {
            $.each(u, function (e, t) {
              if (typeof n !== "string") {
                var r = {};$.each(n, function (e, n) {
                  r[n] = t[0];
                });t[1].css(r);
              } else {
                t[1].css(n, t[0]);
              }
            });
          } else {
            var a, f, l;if (l = s.match(/-transform-group\(([\s\S]+?\([^)]+\))*?\)/g)) {
              a = $.map(l, function (e, t) {
                return e.replace(/-transform-group\(([\s\S]+)\)$/, "$1");
              });
            } else {
              a = s.replace(/[\n\s]+/g, " ").split(" ");
            }o.each(function (t, r) {
              f = e === "all" ? $(r) : e === "word" ? $(r).children("span") : $(r).find("span > span");$.each(a, function (e, t) {
                if (typeof n !== "string") {
                  var r = {};$.each(n, function (e, n) {
                    r[n] = t;
                  });f.eq(e).css(r);
                } else {
                  f.eq(e).css(n, t);
                }
              });
            });
          }
        });
      }, kern: function kern(e, t, n) {
        i._applyAttribute(e, t, "margin-right", n);
      }, size: function size(e, t, n) {
        i._applyAttribute(e, t, "font-size", n);
      }, weight: function weight(e, t, n) {
        i._applyAttribute(e, t, "font-weight", n);
      }, color: function color(e, t, n) {
        i._applyAttribute(e, t, "color", n);
      }, backgroundcolor: function backgroundcolor(e, t, n) {
        i._applyAttribute(e, t, "background-color", n);
      }, transform: function transform(e, t, n) {
        var r = ["-webkit-transform", "-moz-transform", "-ms-transform", "-o-transform", "transform"];i._applyAttribute(e, t, r, n);
      } };this._parse = function (t, s) {
      if (!e._parsedCSS) e._parsedCSS = t;for (var o in t) {
        for (var u in t[o]) {
          var a,
              f,
              l = t[o][u];if (a = u.match(new RegExp("^(-" + n + "|-" + r + ")?-(letter|word)-(kern|transform|size|color|backgroundcolor|weight)", "i"))) {
            var c = a[2].toLowerCase(),
                h = a[3].toLowerCase();f = $(o);if (s) f = f.not(".kerningjs");f.not(".kerningjs").addClass("kerningjs").css("visibility", "inherit").lettering("words").children("span").css("display", "inline-block").lettering().children("span").css("display", "inline-block");if (i[h]) i[h].call(this, c, f, l);
          } else if ((a = u.match(/font-(size|weight)/i)) && l.match(/if-font/i)) {
            var h = a[1].toLowerCase();f = $(o);if (s) f = f.not(".kerningjs");f.not(".kerningjs").addClass("kerningjs").css("visibility", "inherit");if (i[h]) i[h].call(this, "all", f, l);
          }
        }
      }
    };this.live = function () {
      $(document).bind("DOMNodeInserted", function (t) {
        if (t.target) e.refresh(true);
      });
    };this.refresh = function (t) {
      if (e._parsedCSS) e._parse(e._parsedCSS, t);
    };$(function () {
      $(document).parsecss(e._parse, true);
    });
  }();
})(jQuery);

/*! https://mths.be/scrollingelement v1.5.2 by @diegoperini & @mathias | MIT license */
if (!('scrollingElement' in document)) (function () {

	function computeStyle(element) {
		if (window.getComputedStyle) {
			// Support Firefox < 4 which throws on a single parameter.
			return getComputedStyle(element, null);
		}
		// Support Internet Explorer < 9.
		return element.currentStyle;
	}

	function isBodyElement(element) {
		// The `instanceof` check gives the correct result for e.g. `body` in a
		// non-HTML namespace.
		if (window.HTMLBodyElement) {
			return element instanceof HTMLBodyElement;
		}
		// Fall back to a `tagName` check for old browsers.
		return (/body/i.test(element.tagName)
		);
	}

	function getNextBodyElement(frameset) {
		// We use this function to be correct per spec in case `document.body` is
		// a `frameset` but there exists a later `body`. Since `document.body` is
		// a `frameset`, we know the root is an `html`, and there was no `body`
		// before the `frameset`, so we just need to look at siblings after the
		// `frameset`.
		var current = frameset;
		while (current = current.nextSibling) {
			if (current.nodeType == 1 && isBodyElement(current)) {
				return current;
			}
		}
		// No `body` found.
		return null;
	}

	// Note: standards mode / quirks mode can be toggled at runtime via
	// `document.write`.
	var isCompliantCached;
	var isCompliant = function isCompliant() {
		var isStandardsMode = /^CSS1/.test(document.compatMode);
		if (!isStandardsMode) {
			// In quirks mode, the result is equivalent to the non-compliant
			// standards mode behavior.
			return false;
		}
		if (isCompliantCached === void 0) {
			// When called for the first time, check whether the browser is
			// standard-compliant, and cache the result.
			var iframe = document.createElement('iframe');
			iframe.style.height = '1px';
			(document.body || document.documentElement || document).appendChild(iframe);
			var doc = iframe.contentWindow.document;
			doc.write('<!DOCTYPE html>
x
');
			doc.close();
			isCompliantCached = doc.documentElement.scrollHeight > doc.body.scrollHeight;
			iframe.parentNode.removeChild(iframe);
		}
		return isCompliantCached;
	};

	function isRendered(style) {
		return style.display != 'none' && !(style.visibility == 'collapse' && /^table-(.+-group|row|column)$/.test(style.display));
	}

	function isScrollable(body) {
		// A `body` element is scrollable if `body` and `html` both have
		// non-`visible` overflow and are both being rendered.
		var bodyStyle = computeStyle(body);
		var htmlStyle = computeStyle(document.documentElement);
		return bodyStyle.overflow != 'visible' && htmlStyle.overflow != 'visible' && isRendered(bodyStyle) && isRendered(htmlStyle);
	}

	var scrollingElement = function scrollingElement() {
		if (isCompliant()) {
			return document.documentElement;
		}
		var body = document.body;
		// Note: `document.body` could be a `frameset` element, or `null`.
		// `tagName` is uppercase in HTML, but lowercase in XML.
		var isFrameset = body && !/body/i.test(body.tagName);
		body = isFrameset ? getNextBodyElement(body) : body;
		// If `body` is itself scrollable, it is not the `scrollingElement`.
		return body && isScrollable(body) ? null : body;
	};

	if (Object.defineProperty) {
		// Support modern browsers that lack a native implementation.
		Object.defineProperty(document, 'scrollingElement', {
			'get': scrollingElement
		});
	} else if (document.__defineGetter__) {
		// Support Firefox ≤ 3.6.9, Safari ≤ 4.1.3.
		document.__defineGetter__('scrollingElement', scrollingElement);
	} else {
		// IE ≤ 4 lacks `attachEvent`, so it only gets this one assignment. IE ≤ 7
		// gets it too, but the value is updated later (see `propertychange`).
		document.scrollingElement = scrollingElement();
		document.attachEvent && document.attachEvent('onpropertychange', function () {
			// This is for IE ≤ 7 only.
			// A `propertychange` event fires when `<body>` is parsed because
			// `document.activeElement` then changes.
			if (window.event.propertyName == 'activeElement') {
				document.scrollingElement = scrollingElement();
			}
		});
	}
})();

var _isElement = function _isElement(arg) {
	return arg != null && (typeof arg === "undefined" ? "undefined" : _typeof(arg)) === "object" && arg.nodeType === Node.ELEMENT_NODE && _typeof(arg.style) === "object" && _typeof(arg.ownerDocument) === "object";
};

var _isElementOrDocumentOrWindow = function _isElementOrDocumentOrWindow(arg) {
  return _isElement(arg) || arg === document || arg === window;
};

var _throwErrorIf = function _throwErrorIf(condition, error) {
	if (condition) {
		throw new Error(error);
	}
};

/**
 * Returns all parents of a DOM element,
 * from the closest to the most distant.
 *
 * @category DOM
 * @param  {HTMLElement} domEl The DOM element.
 * @return {Array.<HTMLElement>} The DOM element parents.
 *
 * @example
 * let domChild = document.createElement("div"),
 * 	domParent = document.createElement("div"),
 * 	domGrandparent = document.createElement("div"),
 * 	body = document.body,
 * 	html = document.querySelector("html");
 *
 * domParent.appendChild(domChild);
 * domGrandparent.appendChild(domParent);
 * body.appendChild(domGrandparent);
 *
 * parents(domChild);
 * // => [domParent, domGrandparent, body, html, document]
 */
var parents = function parents(domEl) {
  var memo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

  if (memo.length === 0) {
    _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement is expected as parameter.");
  }

  var parentNode = domEl.parentNode;
  return !parentNode ? memo : parents(parentNode, memo.concat([parentNode]));
};

/**
 * The same as [`parents()`](#parents), except it includes
 * the DOM element itself.
 *
 * @category DOM
 * @param  {HTMLElement} domEl The DOM element.
 * @return {Array.<HTMLElement>} The DOM element and its parents.
 *
 * @example
 * let domChild = document.createElement("div"),
 * 	domParent = document.createElement("div"),
 * 	domGrandparent = document.createElement("div"),
 * 	body = document.body,
 * 	html = document.querySelector("html");
 *
 * domParent.appendChild(domChild);
 * domGrandparent.appendChild(domParent);
 * body.appendChild(domGrandparent);
 *
 * selfAndParents(domChild);
 * // => [domChild, domParent, domGrandparent, body, html, document]
 */
var selfAndParents = function selfAndParents(domEl) {
  _throwErrorIf(!_isElementOrDocumentOrWindow(domEl), "An HTMLElement is expected as parameter.");
  return [domEl].concat(parents(domEl));
};

/**
 * Returns an array with all DOM elements affected by an event.
 * The function serves as a polyfill for
 * [`Event.composedPath()`](https://dom.spec.whatwg.org/#dom-event-composedpath).
 *
 * @category Event
 * @param {Event} evt The triggered event.
 * @return {Array.<HTMLElement>} The DOM elements affected by the event.
 *
 * @example
 * let domChild = document.createElement("div"),
 * 	domParent = document.createElement("div"),
 * 	domGrandparent = document.createElement("div"),
 * 	body = document.body,
 * 	html = document.querySelector("html");
 *
 * domParent.appendChild(domChild);
 * domGrandparent.appendChild(domParent);
 * body.appendChild(domGrandparent);
 *
 * domChild.addEventListener("click", dealWithClick);
 * const dealWithClick = evt => getEventPath(evt);
 *
 * // when domChild is clicked:
 * // => [domChild, domParent, domGrandparent, body, html, document, window]
 */
var getEventPath = function getEventPath(evt) {
  var path = evt.composedPath && evt.composedPath() || evt.path,
      target = evt.target;

  if (target == null) {
    return undefined;
  }

  if (path != null) {
    path = !path.includes(window) ? path.concat([window]) : path;
    return path;
  }

  if (target === window) {
    return [window];
  }

  return selfAndParents(target).concat([window]);
};

/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value === 'undefined' ? 'undefined' : _typeof(value);
  return value != null && (type == 'object' || type == 'function');
}

/** Detect free variable `global` from Node.js. */
var freeGlobal = (typeof global === 'undefined' ? 'undefined' : _typeof(global)) == 'object' && global && global.Object === Object && global;

/** Detect free variable `self`. */
var freeSelf = (typeof self === 'undefined' ? 'undefined' : _typeof(self)) == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();

/**
 * Gets the timestamp of the number of milliseconds that have elapsed since
 * the Unix epoch (1 January 1970 00:00:00 UTC).
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Date
 * @returns {number} Returns the timestamp.
 * @example
 *
 * _.defer(function(stamp) {
 *   console.log(_.now() - stamp);
 * }, _.now());
 * // => Logs the number of milliseconds it took for the deferred invocation.
 */
var now = function now() {
  return root.Date.now();
};

/** Built-in value references. */
var _Symbol = root.Symbol;

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;

/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag$1),
      tag = value[symToStringTag$1];

  try {
    value[symToStringTag$1] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag$1] = tag;
    } else {
      delete value[symToStringTag$1];
    }
  }
  return result;
}

/** Used for built-in method references. */
var objectProto$1 = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString$1 = objectProto$1.toString;

/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return nativeObjectToString$1.call(value);
}

/** `Object#toString` result references. */
var nullTag = '[object Null]';
var undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
}

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'object';
}

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * _.isSymbol(Symbol.iterator);
 * // => true
 *
 * _.isSymbol('abc');
 * // => false
 */
function isSymbol(value) {
  return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
}

/** Used as references for various `Number` constants. */
var NAN = 0 / 0;

/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;

/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;

/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;

/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;

/**
 * Converts `value` to a number.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to process.
 * @returns {number} Returns the number.
 * @example
 *
 * _.toNumber(3.2);
 * // => 3.2
 *
 * _.toNumber(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toNumber(Infinity);
 * // => Infinity
 *
 * _.toNumber('3.2');
 * // => 3.2
 */
function toNumber(value) {
  if (typeof value == 'number') {
    return value;
  }
  if (isSymbol(value)) {
    return NAN;
  }
  if (isObject(value)) {
    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
    value = isObject(other) ? other + '' : other;
  }
  if (typeof value != 'string') {
    return value === 0 ? value : +value;
  }
  value = value.replace(reTrim, '');
  var isBinary = reIsBinary.test(value);
  return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
}

/** Error message constants. */
var FUNC_ERROR_TEXT$1 = 'Expected a function';

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
var nativeMin = Math.min;

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked. The debounced function comes with a `cancel` method to cancel
 * delayed `func` invocations and a `flush` method to immediately invoke them.
 * Provide `options` to indicate whether `func` should be invoked on the
 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
 * with the last arguments provided to the debounced function. Subsequent
 * calls to the debounced function return the result of the last `func`
 * invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the debounced function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.debounce` and `_.throttle`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to debounce.
 * @param {number} [wait=0] The number of milliseconds to delay.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=false]
 *  Specify invoking on the leading edge of the timeout.
 * @param {number} [options.maxWait]
 *  The maximum time `func` is allowed to be delayed before it's invoked.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new debounced function.
 * @example
 *
 * // Avoid costly calculations while the window size is in flux.
 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 *
 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
 * jQuery(element).on('click', _.debounce(sendMail, 300, {
 *   'leading': true,
 *   'trailing': false
 * }));
 *
 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
 * var source = new EventSource('/stream');
 * jQuery(source).on('message', debounced);
 *
 * // Cancel the trailing debounced invocation.
 * jQuery(window).on('popstate', debounced.cancel);
 */
function debounce(func, wait, options) {
  var lastArgs,
      lastThis,
      maxWait,
      result,
      timerId,
      lastCallTime,
      lastInvokeTime = 0,
      leading = false,
      maxing = false,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT$1);
  }
  wait = toNumber(wait) || 0;
  if (isObject(options)) {
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }

  function invokeFunc(time) {
    var args = lastArgs,
        thisArg = lastThis;

    lastArgs = lastThis = undefined;
    lastInvokeTime = time;
    result = func.apply(thisArg, args);
    return result;
  }

  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time;
    // Start the timer for the trailing edge.
    timerId = setTimeout(timerExpired, wait);
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result;
  }

  function remainingWait(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime,
        result = wait - timeSinceLastCall;

    return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
  }

  function shouldInvoke(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime;

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
  }

  function timerExpired() {
    var time = now();
    if (shouldInvoke(time)) {
      return trailingEdge(time);
    }
    // Restart the timer.
    timerId = setTimeout(timerExpired, remainingWait(time));
  }

  function trailingEdge(time) {
    timerId = undefined;

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time);
    }
    lastArgs = lastThis = undefined;
    return result;
  }

  function cancel() {
    if (timerId !== undefined) {
      clearTimeout(timerId);
    }
    lastInvokeTime = 0;
    lastArgs = lastCallTime = lastThis = timerId = undefined;
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(now());
  }

  function debounced() {
    var time = now(),
        isInvoking = shouldInvoke(time);

    lastArgs = arguments;
    lastThis = this;
    lastCallTime = time;

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime);
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = setTimeout(timerExpired, wait);
        return invokeFunc(lastCallTime);
      }
    }
    if (timerId === undefined) {
      timerId = setTimeout(timerExpired, wait);
    }
    return result;
  }
  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced;
}

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a throttled function that only invokes `func` at most once per
 * every `wait` milliseconds. The throttled function comes with a `cancel`
 * method to cancel delayed `func` invocations and a `flush` method to
 * immediately invoke them. Provide `options` to indicate whether `func`
 * should be invoked on the leading and/or trailing edge of the `wait`
 * timeout. The `func` is invoked with the last arguments provided to the
 * throttled function. Subsequent calls to the throttled function return the
 * result of the last `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the throttled function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.throttle` and `_.debounce`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to throttle.
 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=true]
 *  Specify invoking on the leading edge of the timeout.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new throttled function.
 * @example
 *
 * // Avoid excessively updating the position while scrolling.
 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
 *
 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
 * jQuery(element).on('click', throttled);
 *
 * // Cancel the trailing throttled invocation.
 * jQuery(window).on('popstate', throttled.cancel);
 */
function throttle(func, wait, options) {
  var leading = true,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  return debounce(func, wait, {
    'leading': leading,
    'maxWait': wait,
    'trailing': trailing
  });
}

var $window = $(window);
var logo = $("h1").find("span").get(0);
var navSwitch = $(".nav__switch").get(0);
var navDrawer = $(".nav__drawer").get(0);
var navButtons = $(".nav").find("a").get();
var navCollapsableButtons = $(navButtons).get().filter(function (domEl) {
	return $(domEl).parent().is("strong");
});
var chapterAnchors = $(".content").find("h2, h3 > a").get().filter(function (domEl) {
	return !!getHash(domEl);
});

function getScrollTarget() {
	return useNativeScroll() ? getScrollingElement() : $(".page__inner").get(0);
}

function getCurrentlyReadableChapter() {
	var anchorsAboveTheFold = chapterAnchors.filter(isAboveTheFold);
	return getLastItem(anchorsAboveTheFold);
}

function getHash(domEl) {
	return domEl.href ? domEl.href.split("#")[1] : "" || domEl.getAttribute("id") || domEl.getAttribute("name");
}

function getChapterByHash(hash) {
	return $("[id=\"" + hash + "\"], [name=\"" + hash + "\"]")[0];
}

function getNavButtonByHash(hash) {
	return $(navButtons).filter("a[href$=\"#" + hash + "\"]")[0];
}

function getLastItem(arr) {
	return arr[arr.length - 1];
}

function getScrollingElement() {
	return document.scrollingElement;
}

function useNativeScroll() {
	return $("html").hasClass("mode--native-scroll");
}

function isAboveTheFold(domEl) {
	return domEl.getBoundingClientRect().top < $window.height() * 0.35;
}

function ignoreScrollNavigationEvents() {
	for (var _len = arguments.length, domEls = Array(_len), _key = 0; _key < _len; _key++) {
		domEls[_key] = arguments[_key];
	}

	$(domEls).off("scroll");
}

function listenForScrollNavigationEvents() {
	for (var _len2 = arguments.length, domEls = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
		domEls[_key2] = arguments[_key2];
	}

	$(domEls).on("scroll", throttle(markNav, 100)).on("scroll", debounce(markBrowser, 350));
}

function updateScrollAnchorEvents() {
	$(logo).on("click", scrollToTop);
	$("a[href^=\"#\"]").on("click", scrollToChapter);
}

function listenForLayoutChange() {
	$window.on("resize", debounce(updateScrollBehaviour, 300)).on("resize", debounce(markNav, 350)).on("resize", debounce(markBrowser, 350));
}

function removeNonBreakingSpacesFromTds() {
	$("td").each(function () {
		var $this = $(this);
		$this.html($this.html().replace(/ /g, ''));
	});
}

function updateScrollBehaviour() {
	ignoreScrollNavigationEvents(getScrollTarget(), window);
	listenForScrollNavigationEvents(getScrollTarget(), window);
}

function updateDrawerBehaviour() {
	var $scrollTarget = $(getScrollTarget());

	$(navSwitch).on("click", toggleDrawer);
	$scrollTarget.on("click", closeDrawerOnClickOutside);
}

function changeDrawer(func) {
	func();

	if (!isDrawerVisible()) {
		$(navDrawer).one("transitionend", moveDrawerScrollToTop);
	}
}

function toggleDrawer() {
	changeDrawer(function () {
		$(navDrawer).toggleClass("nav__drawer--off");
		$(navSwitch).toggleClass("nav__switch--off");
	});
}

function closeDrawer() {
	changeDrawer(function () {
		$(navDrawer).addClass("nav__drawer--off");
		$(navSwitch).addClass("nav__switch--off");
	});
}

function closeDrawerOnClickOutside(evt) {
	if (isEventOutsideDrawer(evt)) {
		closeDrawer();
	}
}

function isEventOutsideDrawer(evt) {
	var eventPath = getEventPath(evt);
	return !eventPath.includes(navSwitch) && !eventPath.includes(navDrawer);
}

function isDrawerVisible() {
	return !$(navDrawer).hasClass("nav__drawer--off");
}

function moveDrawerScrollToTop() {
	navDrawer.scrollTop = 0;
}

function scrollTo(position, evt) {
	var $scrollTarget = $(getScrollTarget());

	if (position != null) {
		evt.preventDefault();
		$scrollTarget.scrollTo(position, 600, {
			axis: "y"
		});
	}
}

function scrollToTop(evt) {
	scrollTo(0, evt);
}

function scrollToChapter(evt) {
	var chapter = getChapterByHash(getHash(evt.target));
	scrollTo(chapter, evt);
}

function markNav() {
	$(navButtons).removeClass("selected");

	var currentlyReadableChapter = getCurrentlyReadableChapter();

	if (currentlyReadableChapter) {
		var hash = getHash(currentlyReadableChapter);
		var markedNavButton = getNavButtonByHash(hash);
		$(markedNavButton).addClass("selected");
	}
}

function markBrowser() {
	var currentlyReadableChapter = getCurrentlyReadableChapter();

	if (history.replaceState) {
		if (currentlyReadableChapter) {
			var hash = "#" + getHash(currentlyReadableChapter);

			if (document.location.hash !== hash) {
				history.replaceState(null, null, hash);
			}
		} else {
			if (document.location.hash !== "") {
				history.replaceState(null, null, document.location.origin + document.location.pathname);
			}
		}
	}
}

updateDrawerBehaviour();
updateScrollAnchorEvents();
updateScrollBehaviour();
listenForLayoutChange();
removeNonBreakingSpacesFromTds();


text:
<%if(docs) docs.forEach(category => {-%>
* **[<%-category.name%>](#<%-category.href%>)**
<%-include menuLoop.ejs-%>

<%})-%>


text:
<%if(item.returnsTable && item.returnsTable.length > 0){-%>

#### Return

<%item.returnsTable.forEach(param => {-%>
<%-param%>
<%})-%>
<%}-%>


text:
<%if(item.typedefParams && item.typedefParams.length > 0){-%>
<%typedefs.filter(typedef => item.typedefParams.includes(typedef.name)).forEach(typedef => {-%>

#### <%-typedef.nameCapitalized%>

<%if(typedef.propertiesTable) typedef.propertiesTable.forEach(property => {-%>
<%-property%>
<%})-%>
<%})-%>
<%}-%>


text:
<%if(item.examples && item.examples.length > 0){-%>

#### Example<%if(item.examples.length > 1){%>s<%}%>
<%item.examples.forEach(example => {-%>

```javascript
<%-example%>
```
<%})-%>
<%}-%>


text:
### <a name="<%-item.href%>">`<%-item.signature%>`</a>

<%-item.description%>
<%-include itemParameters.ejs-%>
<%-include itemTypeDefParams.ejs-%>
<%-include itemReturns.ejs-%>
<%-include itemTypeDefReturns.ejs-%>
<%-include itemExamples.ejs-%>


text:
---
layout: default
title: Canivete
---

<%if(docs) docs.forEach(category => {-%>
## *<%-category.name%>*

><%if(category.items) category.items.forEach((item, index, arr) => {-%>
[<%-item.name%>](#<%-item.href%>)<%if(index < arr.length - 2){-%>, <%}-%><%if(index === arr.length - 2){-%> & <%}-%>
<%})%>

<%-include contentLoop.ejs-%>
<%})-%>


text:
<%if(category.items) category.items.forEach(item => {-%>
  * [<%-item.name%>](#<%-item.href%>)
<%})-%>


text:
<%if(category.items) category.items.forEach(item => {-%>
<%-include contentItem.ejs%>
<%})-%>


text:
<%if(item.paramsTable && item.paramsTable.length > 0){-%>

#### Parameter<%if(item.params && item.params.length > 1){%>s<%}%>

<%item.paramsTable.forEach(param => {-%>
<%-param%>
<%})-%>
<%}-%>


text:
<%if(item.typedefReturns && item.typedefReturns.length > 0){-%>
<%typedefs.filter(typedef => item.typedefReturns.includes(typedef.name)).forEach(typedef => {-%>

#### <%-typedef.nameCapitalized%>

<%if(typedef.propertiesTable) typedef.propertiesTable.forEach(property => {-%>
<%-property%>
<%})-%>
<%})-%>
<%}-%>


text:
{% capture usage %}{% include usage.md %}{% endcapture %}
{{ usage | markdownify }}


text:
## *Usage*

Since ES6 modules have virtually no browser support at this time, a module bundler is needed to handle Canivete dependencies.

Here's how to use Canivete with [Rollup](#using-canivete-with-rollup) and [Webpack](#using-canivete-with-webpack).


text:
/*! https://mths.be/scrollingelement v1.5.2 by @diegoperini & @mathias | MIT license */
if (!('scrollingElement' in document))(function() {

	function computeStyle(element) {
		if (window.getComputedStyle) {
			// Support Firefox < 4 which throws on a single parameter.
			return getComputedStyle(element, null);
		}
		// Support Internet Explorer < 9.
		return element.currentStyle;
	}

	function isBodyElement(element) {
		// The `instanceof` check gives the correct result for e.g. `body` in a
		// non-HTML namespace.
		if (window.HTMLBodyElement) {
			return element instanceof HTMLBodyElement;
		}
		// Fall back to a `tagName` check for old browsers.
		return /body/i.test(element.tagName);
	}

	function getNextBodyElement(frameset) {
		// We use this function to be correct per spec in case `document.body` is
		// a `frameset` but there exists a later `body`. Since `document.body` is
		// a `frameset`, we know the root is an `html`, and there was no `body`
		// before the `frameset`, so we just need to look at siblings after the
		// `frameset`.
		var current = frameset;
		while (current = current.nextSibling) {
			if (current.nodeType == 1 && isBodyElement(current)) {
				return current;
			}
		}
		// No `body` found.
		return null;
	}

	// Note: standards mode / quirks mode can be toggled at runtime via
	// `document.write`.
	var isCompliantCached;
	var isCompliant = function() {
		var isStandardsMode = /^CSS1/.test(document.compatMode);
		if (!isStandardsMode) {
			// In quirks mode, the result is equivalent to the non-compliant
			// standards mode behavior.
			return false;
		}
		if (isCompliantCached === void 0) {
			// When called for the first time, check whether the browser is
			// standard-compliant, and cache the result.
			var iframe = document.createElement('iframe');
			iframe.style.height = '1px';
			(document.body || document.documentElement || document).appendChild(iframe);
			var doc = iframe.contentWindow.document;
			doc.write('<!DOCTYPE html>
x
');
			doc.close();
			isCompliantCached = doc.documentElement.scrollHeight > doc.body.scrollHeight;
			iframe.parentNode.removeChild(iframe);
		}
		return isCompliantCached;
	};

	function isRendered(style) {
		return style.display != 'none' && !(style.visibility == 'collapse' &&
			/^table-(.+-group|row|column)$/.test(style.display));
	}

	function isScrollable(body) {
		// A `body` element is scrollable if `body` and `html` both have
		// non-`visible` overflow and are both being rendered.
		var bodyStyle = computeStyle(body);
		var htmlStyle = computeStyle(document.documentElement);
		return bodyStyle.overflow != 'visible' && htmlStyle.overflow != 'visible' &&
			isRendered(bodyStyle) && isRendered(htmlStyle);
	}

	var scrollingElement = function() {
		if (isCompliant()) {
			return document.documentElement;
		}
		var body = document.body;
		// Note: `document.body` could be a `frameset` element, or `null`.
		// `tagName` is uppercase in HTML, but lowercase in XML.
		var isFrameset = body && !/body/i.test(body.tagName);
		body = isFrameset ? getNextBodyElement(body) : body;
		// If `body` is itself scrollable, it is not the `scrollingElement`.
		return body && isScrollable(body) ? null : body;
	};

	if (Object.defineProperty) {
		// Support modern browsers that lack a native implementation.
		Object.defineProperty(document, 'scrollingElement', {
			'get': scrollingElement
		});
	}
	else if (document.__defineGetter__) {
		// Support Firefox ≤ 3.6.9, Safari ≤ 4.1.3.
		document.__defineGetter__('scrollingElement', scrollingElement);
	}
	else {
		// IE ≤ 4 lacks `attachEvent`, so it only gets this one assignment. IE ≤ 7
		// gets it too, but the value is updated later (see `propertychange`).
		document.scrollingElement = scrollingElement();
		document.attachEvent && document.attachEvent('onpropertychange', function() {
			// This is for IE ≤ 7 only.
			// A `propertychange` event fires when `<body>` is parsed because
			// `document.activeElement` then changes.
			if (window.event.propertyName == 'activeElement') {
				document.scrollingElement = scrollingElement();
			}
		});
	}
}());


text:
## Credits

* **Canivete** Coding and documentation by [Leonardo Favre](https://leofavre.com/)
* **Website** Coding and layout by [Leonardo Favre](https://leofavre.com/) and symbol by [Andre Levy a.k.a. Zhion](http://www.zhion.com.br)
* **Typography** [Acropolis](https://www.typography.com/fonts/acropolis/overview/) and [Whitney](https://www.typography.com/fonts/whitney/overview/) by [Hoefler & Co.](https://www.typography.com/) and [Fira Code](https://github.com/tonsky/FiraCode) by [Nikita Prokopov](https://github.com/tonsky/)

Canivete is released under The Unlicense license.


text:
## *About*

Canivete (Brazilian Portuguese for swiss army knife) is an on-going personal project by [Leonardo Favre](https://leofavre.com/), a place to keep generic, multi-purpose Javascript functions.

Due to its multi-purpose nature, Canivete is not distributed as a single file, instead, one should import its functions as needed, using [ES6 module syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import).

[Babel](https://babeljs.io/) and [Babel Polyfill](https://babeljs.io/docs/usage/polyfill/) are recommended to make Canivete work in older browsers.

[Canivete on GitHub](https://github.com/leofavre/canivete/)


text:
### Using Canivete with [Webpack](https://webpack.js.org/)

**1.** In your project root folder, install Canivete via NPM.

```shell
$ npm install --save leofavre/canivete
```

**2.** Install Webpack via NPM.

```shell
$ npm install --save-dev webpack
```

**3.** Create a file named "webpack.config.js" with the following content:

```js
const path = require("path");

module.exports = {
	"entry": "./index.js",
	"output": {
		"path": path.resolve(__dirname, "dist"),
		"filename": "app.js"
	}
};
```

**4.** Create a file named "index.js". Import any dependencies from Canivete using ES6 module syntax before the rest of your code, like this:

```js
import toAverage from "canivete/dist/toAverage";

const myArray = [8, 10, 12, 14, 16];
alert(myArray.reduce(toAverage));
// => 12
```

**5.** Use the following shell command to build your project:

```shell
$ ./node_modules/.bin/webpack
```


text:
{% capture canivete-with-rollup %}{% include canivete-with-rollup.md %}{% endcapture %}
{{ canivete-with-rollup | markdownify }}


text:
### Using Canivete with [Rollup](https://rollupjs.org/)

**1.** In your project root folder, install Canivete via NPM.

```shell
$ npm install --save leofavre/canivete
```

**2.** Install Rollup globally via NPM.

```shell
$ npm install --global rollup
```

**3.** Install the Node Resolve Pulgin for Rollup via NPM.

```shell
$ npm install --save-dev rollup-plugin-node-resolve
```

**4.** Create a file named "rollup.config.js" with the following content:

```js
import nodeResolve from "rollup-plugin-node-resolve";

export default {
	"entry": "./index.js",
	"dest": "./dist/app.js",
	"plugins": [nodeResolve()],
	"format": "es"
};
```

**5.** Create a file named "index.js". Import any dependencies from Canivete using ES6 module syntax before the rest of your code, like this:

```js
import toAverage from "canivete/dist/toAverage";

const myArray = [8, 10, 12, 14, 16];
alert(myArray.reduce(toAverage));
// => 12
```

**6.** Use the following shell command to build your project:

```shell
$ rollup -c
```


text:
{% capture canivete-with-webpack %}{% include canivete-with-webpack.md %}{% endcapture %}
{{ canivete-with-webpack | markdownify }}


text:
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.23.0/polyfill.min.js" integrity="sha256-2nu8qdFj9AM9XRd75EAp3XRGEKiRprWL4hPMhYkj150=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="js/index.js"></script>

<script>
	(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

	ga('create', 'UA-101469583-1', 'auto');
	ga('send', 'pageview');
</script>


text:
About

Usage

Credits


text:
{% capture menu %}{% include menu.md %}{% endcapture %}
{{ menu | markdownify }}


text:
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>{{ page.title }}</title>
	<link href="https://cloud.typography.com/7861976/6348172/css/fonts.css" rel="stylesheet" type="text/css">
	<link href="css/styles.css" rel="stylesheet">
	<link href="css/kern.css" rel="stylesheet">
	<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
	<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
	<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
	<link rel="manifest" href="/manifest.json">
	<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
	<meta name="theme-color" content="#ffffff">

	<meta property="og:type" content="website">
	<meta property="og:title" content="Canivete">
	<meta property="og:description" content="Canivete (Brazilian Portuguese for swiss army knife) is an on-going personal project by Leonardo Favre, a place to keep generic, multi-purpose Javascript functions.">
	<meta property="og:url" content="https://canivete.leofavre.com">
	<meta property="og:image" content="https://canivete.leofavre.com/canivete.png">
</head>


text:
{% capture logo %}{% include logo.md %}{% endcapture %}
{{ logo | markdownify }}


text:
* **[BEM](#bem)**
  * [formatBemClass](#formatbemclass)
  * [modifyBemClass](#modifybemclass)
  * [modifyBemClassCompact](#modifybemclasscompact)

* **[ClassName](#classname)**
  * [addClass](#addclass)
  * [hasClass](#hasclass)
  * [removeClass](#removeclass)

* **[Collection](#collection)**
  * [deepGroupBy](#deepgroupby)

* **[DOM](#dom)**
  * [getAttr](#getattr)
  * [parents](#parents)
  * [removeAttr](#removeattr)
  * [removeAttrs](#removeattrs)
  * [selfAndParents](#selfandparents)
  * [setAttr](#setattr)
  * [setAttrs](#setattrs)

* **[Event](#event)**
  * [getEventPath](#geteventpath)
  * [ignore](#ignore)
  * [listen](#listen)
  * [trigger](#trigger)

* **[Geometry](#geometry)**
  * [getClippingInfo](#getclippinginfo)
  * [getDistanceBetweenCoords](#getdistancebetweencoords)

* **[Promise](#promise)**
  * [eventAsPromise](#eventaspromise)
  * [waitInPromise](#waitinpromise)

* **[Random](#random)**
  * [oneOutOf](#oneoutof)

* **[Reduce](#reduce)**
  * [toAverage](#toaverage)
  * [toAverageProp](#toaverageprop)
  * [toClosest](#toclosest)
  * [toClosestProp](#toclosestprop)
  * [toLargestProp](#tolargestprop)
  * [toSmallestProp](#tosmallestprop)
  * [toSum](#tosum)

* **[Sort](#sort)**
  * [byAlphabeticalOrder](#byalphabeticalorder)
  * [byProps](#byprops)

* **[String](#string)**
  * [afterFirst](#afterfirst)
  * [afterLast](#afterlast)
  * [beforeFirst](#beforefirst)
  * [beforeLast](#beforelast)

* **[Time](#time)**
  * [timeSince](#timesince)


text:
{% capture about %}{% include about.md %}{% endcapture %}
{{ about | markdownify }}


text:
{% capture credits %}{% include credits.md %}{% endcapture %}
{{ credits | markdownify }}


text:
const exec = require("child_process").exec;
const flow = require("lodash/flow");
const groupBy = require("lodash/groupBy");
const fs = require("fs");

// Basic functions, like creating or removing folders or loading JSON files.

function asPromise(func, path, data, options, errMsg = "", processOut = arg => arg, processErr = processOut) {
	return new Promise((resolve, reject) => {
		let callback = (err, stdout, stderr) => {
			if (err != null) {
				console.log(errMsg);
				reject(processErr(stderr));
			}
			else {
				resolve(processOut(stdout));
			}
		};

		let args = (data == null) ? [path, options, callback] : [path, data, options, callback];

		func(...args);
	});
}

const writeFileAsPromise = (path, data, options) => asPromise(fs.writeFile, path, data, options, "Failed to save file.");

const readJsonAsPromise = (path, options) => asPromise(fs.readFile, path, null, options, "Failed to read JSON.", JSON.parse);

const execAsPromise = (path, options) => asPromise(exec, path, null, options, "Failed to execute shell command.");

const createDir = path => () => execAsPromise(`mkdir -p ${path}`);

const removeDir = path => () => execAsPromise(`rm -r ${path}`);

const byAlphabeticalOrder = (strA, strB) => (strA > strB) ? +1 : (strA < strB) ? -1 : 0;

const removeNewslines = str => str; // str.replace(/(?:\r\n|\r|\n)/g, " ");

const exportSiteUsingJekyll = path => () => execAsPromise(`cd ${path} && bundle exec jekyll build --incremental`);

const exportScriptWithRollUp = () => () => execAsPromise(`./node_modules/.bin/rollup -c rollup.docs.config.js`);

const capitalizeFirstLetter = str => str.charAt(0).toUpperCase() + str.slice(1);

const capitalizeFirstLetterAfter = char => str => {
	let splitStr = str.split(char),
		beforeChar = splitStr[0],
		afterChar = splitStr[1];

	if (afterChar != null) {
		return `${beforeChar}${char}${capitalizeFirstLetter(afterChar)}`;
	}

	return str;
};

const replaceAsteriskForWord = str => str.replace(/\*/, "All");

const escapeHtmlTag = str => str.replace(/\</g, "<").replace(/\>/g, ">");

// Functions for generating, parsing and exporting jsdoc to a single markdown file.

const exportJsDocsAsJson = (path, data, template = "./node_modules/jsdoc-json") => () => {
	return execAsPromise(`./node_modules/.bin/jsdoc ${path} -d ${data} -t ${template}`);
};

const readJsonFile = path => () => readJsonAsPromise(path);

const processJsonFile = json => {
	let funcs = processFunctions(json.docs);
	let typedefs = processTypeDefs(json.docs);

	associateFunctionsWithTypeDefs(funcs, typedefs);

	return {
		docs: funcs,
		typedefs
	};
};

const associateFunctionsWithTypeDefs = (funcs, typedefs) => {
	let typedefNames = typedefs.map(typedef => typedef.name);

	funcs.forEach(category => {
		if (category.items && category.items.length > 0) {
			category.items.forEach(item => {
				doFunctionTypeDefAssociations("params", item, typedefNames);
				doFunctionTypeDefAssociations("returns", item, typedefNames);
			});
		}
	});
};

const doFunctionTypeDefAssociations = (propName, item, typedefNames) => {
	if (item[propName] && item[propName].length > 0) {
		item[propName].forEach(prop => {
			typedefNames.forEach(typedefName => {
				doFunctionTypeDefAssociation(propName, item, prop, typedefName);
			});
		});
	}
};

const doFunctionTypeDefAssociation = (propName, item, prop, typedefName) => {
	let hasAssociation = prop.type.names.some(name => name.includes(typedefName));

	if (hasAssociation) {
		let typeProp = "typedef" + capitalizeFirstLetter(propName);
		item[typeProp] = item[typeProp] || [];
		item[typeProp].push(typedefName);
	}
};

const writeFile = path => data => writeFileAsPromise(path, JSON.stringify(data));

const exportDocsUsingTemplate = (pathIn, pathOut, docName, template) => () => {
	execAsPromise(`./node_modules/.bin/ejs-cli ${template} > ${pathOut}/${docName}.md -O '${pathIn}'`);
};

const filterFunctions = docs => docs.filter(isFunction);

const formatFunctions = funcs => funcs.map(formatFunction);

const formatFunction = func => {
	func.description = formatDescription(func.description);
	func.href = formatHref(func.name);
	func.paramsTable = formatTable(func.params);
	func.returnsTable = formatTable(func.returns);
	func.signature = formatSignature(func.name, func.params);
	return func;
};

const filterTypeDefs = docs => docs.filter(isTypeDef);

const formatTypeDefs = typedefs => typedefs.map(formatTypeDef);

const formatTypeDef = typedef => {
	typedef.nameCapitalized = capitalizeFirstLetter(typedef.name);
	typedef.propertiesTable = formatTable(typedef.properties);
	return typedef;
};

const formatTable = params => {
	let tableConfig = parseTableConfig(params);

	if (params != null) {
		return [formatTableLineMarkdown(["Name", "Default", "Type", "Description"], tableConfig)]
			.concat([formatTableLineMarkdown(["---", "---", "---", "---"], tableConfig)])
			.concat(params.map(formatTableLine(tableConfig)));
	}
};

const formatTableLine = tableConfig => param => {
	let lines = [
		formatCode(param.name),
		formatCode(param.defaultvalue),
		formatType(param.type.names),
		formatParamDesc(param.description, param.optional)
	];

	return formatTableLineMarkdown(lines, tableConfig);
};

const formatTableLineMarkdown = (arr, tableConfig) => "| " + arr.filter(shouldLineExist(tableConfig)).join(" | ") + " |";

const shouldLineExist = tableConfig => (str, i) => tableConfig[i];

const formatCode = str => (str != null) ? "`" + str + "`" : undefined;

const formatType = typeNames => {
	if (typeNames != null && typeNames.length > 0) {
		return typeNames.map(processType).join("<br>");
	}
};

const formatParamDesc = (description, isOptional) => {
	if (description != null) {
		return removeNewslines(description) + (isOptional ? " **optional**" : "");
	}
};

const formatDescription = description => removeNewslines(description);

const formatHref = name => name.toLowerCase().replace(/ /g, "-");

const formatSignature = (name, params) => {
	params = formatSignatureParams(params);
	return `${name} (${params})`;
};

const formatSignatureParams = params => {
	return (params != null) ? params.map(formatParam).join(", ") : "";
};

const formatParam = param => {
	let preParam = param.optional ? "[" : "";
	let postParam = param.optional ? "]" : "";
	let defaultValue = "";
	return `${preParam}${param.name}${defaultValue}${postParam}`;
};

const parseTableConfig = params => {
	if (params) {
		return [
			params.some(hasParamName),
			params.some(hasParamDefault),
			params.some(hasParamType),
			params.some(hasParamDesc)
		];
	}

	return [false, false, false, false];
};

const hasParamName = param => param.name != null;

const hasParamDesc = param => param.description != null;

const hasParamDefault = param => param.defaultvalue != null;

const hasParamType = param => param.type != null && param.type.names != null && param.type.names.length > 0;

const groupFunctionsByCategory = funcs => groupBy(funcs, byCategory);

const byCategory = func => {
	let errorMsg = `@category missing on ${func.name} documentation`;

	if (!func.tags) {
		throw new Error(errorMsg);
	}

	let categories = func.tags.filter(tag => tag.title === "category");

	if (categories.length === 0) {
		throw new Error(errorMsg);
	}

	return categories[0].value;
};

const formatCategories = funcs => {
	let categories = Object.keys(funcs);

	return categories
		.sort(byAlphabeticalOrder)
		.map(formatCategory(funcs));
};

const formatCategory = funcs => category => {
	let categoryFunctions = funcs[category];

	return {
		name: category,
		href: formatHref(category),
		items: categoryFunctions
	};
};

const isParamOptions = param => param.optional;

const isFunction = docs => docs.kind === "function";

const isTypeDef = docs => docs.kind === "typedef";

const processFunctions = flow([
	filterFunctions,
	formatFunctions,
	groupFunctionsByCategory,
	formatCategories
]);

const processTypeDefs = flow([
	filterTypeDefs,
	formatTypeDefs
]);

const processType = flow([
	capitalizeFirstLetter,
	capitalizeFirstLetterAfter("<"),
	replaceAsteriskForWord,
	escapeHtmlTag
]);

Promise.resolve()
	.then(exportScriptWithRollUp())
	.then(createDir("./docs/temp"))
	.then(exportJsDocsAsJson("./dist", "./docs/temp/raw.json"))
	.then(readJsonFile("./docs/temp/raw.json"))
	.then(processJsonFile)
	.then(writeFile("./docs/temp/processed.json"))
	.then(exportDocsUsingTemplate("./docs/temp/processed.json", "./docs", "index", "./docs/_ejs/content.ejs"))
	.then(exportDocsUsingTemplate("./docs/temp/processed.json", "./docs/_includes", "menu", "./docs/_ejs/menu.ejs"))
	.then(exportSiteUsingJekyll("./docs"))
	.catch();

// If I could stream exportJsDocsAsJson into exportDocsUsingTemplate,
// steps marked with an '*' would be unnecessary.