diff --git a/v3/LICENSE b/v3/LICENSE deleted file mode 100644 index 3178f33..0000000 --- a/v3/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 neuroneural/brainchop - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/v3/README.md b/v3/README.md deleted file mode 100644 index 543f8ee..0000000 --- a/v3/README.md +++ /dev/null @@ -1,43 +0,0 @@ - -# Brainchop [![Version](https://img.shields.io/badge/Version-3.4.0-brightgreen)]() [![JS ](https://img.shields.io/badge/Types-JavaScript-blue)]() [![MIT-License ](https://img.shields.io/badge/license-MIT-green)](https://github.com/neuroneural/brainchop/blob/master/LICENSE) [![tfjs](https://img.shields.io/badge/tfjs-Pre--trained%20Model-blue)](https://github.com/neuroneural/brainchop/tree/master/models/mnm_tfjs_me_test) [![DOI](https://joss.theoj.org/papers/10.21105/joss.05098/status.svg)](https://doi.org/10.21105/joss.05098) - - -
- - - - - -**Frontend For Neuroimaging. Open Source** - -**[Demo v3](https://neuroneural.github.io/brainchop/v3)   [Doc](https://github.com/neuroneural/brainchop/wiki/)** - -
- - -
- - -

- Brainchop v3 brings automatic 3D MRI volumetric segmentation capability to neuroimaging by running a lightweight deep learning model (e.g., MeshNet) in the web-browser for inference on the user side. -

- -

- We make the implementation of Brainchop freely available, releasing its pure javascript code as open-source. The user interface (UI) provides a web-based end-to-end solution for 3D MRI segmentation. Papaya viewer is integrated with the tool for MRI visualization. In version 1.3.0, Three.js is used for MRI 3D rendering. For more information about Brainchop, please refer to this detailed Wiki and this Blog. - - -

- -
- -> **Note:** The remaining content of this README file has been moved to the main [README.md](../README.md) file in the root of the repository. Please refer to the main README for general information, last updates, news, citations, usage guidelines, and contribution details. - - - -
-
- - - -**Mohamed Masoud - Sergey Plis - 2024** -
diff --git a/v3/css/brainchop.css b/v3/css/brainchop.css deleted file mode 100644 index 48b2570..0000000 --- a/v3/css/brainchop.css +++ /dev/null @@ -1,199 +0,0 @@ -.grid-container { - display: grid; - grid-template-columns: auto; - padding: 0vh; -} - -.grid-item { - background-color: rgba(255, 255, 255, 0.8); - border: 0px solid rgba(0, 0, 0, 0.8); - padding: 1vh; - width: 40vw; - height: auto; - font-size: 15px; - text-align: center; -} - - -.toolbarclass { - background-color: white !important; - font-weight: bold; - box-shadow: 5px 5px; -} - -.titleclass { - font-style: italic; - font-weight: bold; - color: black !important; -} - -.headerclass { - border-left: 10px solid black!important; - background: #f3f3f6a3!important; - margin-left: 0!important; - width: 100%!important; - color: black!important; - font-weight: bold; - border-left-color: black!important; -} - -.windowtitleclass { - background-color: #ccc !important; - box-shadow: none, -} - -.webixbg .webix_view { - background-color:black !important; - box-shadow:none; -} - -#feedbackId { - cursor: pointer; - margin-right: 20px; -} - -#resourcesHwSwId { - cursor: pointer; - margin-right: 20px; -} - -#githubId { - cursor: pointer; - margin-right: 20px; -} -.customTheme { - font-weight: bold; - background-color: black; - color: white; -} - -#annotOfContainer_0 { - /*font-weight: bold;*/ - background-color: black; - color: white; - text-align: center; - height: 3vh; -} - -#annotOfContainer_1 { - /*font-weight: bold;*/ - background-color: black; - color: white; - text-align: center; - height: 3vh; -} - -.bt_1 button.webix_button { - background: #396D9E; - background: -moz-linear-gradient(top, #5D91C3 0% , #396D9E 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0,#5D91C3), color-stop(1, black)); - border:1px solid #396D9E; - text-shadow: 0 -1px #134471; - box-shadow: inset 0px 1px 10px #8eb3d5; - -webkit-box-shadow: inset 0px 1px 1px #8eb3d5; - color:#FFFFFF; - font-size: 13px; -} - -.bt_1 button:active { - background: #396D9E !important; -} - -#output_gui_container { - position: absolute; - top: 10%; - right: 1%; - height: auto; -} - -#input_gui_container { - position: absolute; - top: 10%; - right: 1%; - height: auto; -} - -#output-threejs-container { - background-color: black!important; -} - -.panel { - position: fixed; - z-index: 1; - scrollbar-color: rgba(255,255,255, 1); - scrollbar-width: thin; - transition: 0.5s; -} - -.progress-line{ - background: linear-gradient(217deg, rgba(127,127, 127,.8), rgba(255,0,0,0) 70.71%), - linear-gradient(127deg, rgba(0, 0, 0,.8), rgba(0,255,0,0) 70.71%), - linear-gradient(336deg, rgba(255, 255, 255,.8), rgba(0,0,255,0) 70.71%); - top: 88%; - left: 2.5%; - max-width: 96%; - height: 1.4vh; - position: absolute; - transition: 0.5s cubic-bezier(0.075, 0.82, 0.165, 1); -} - - /*****dat.GUI List Extention***/ - -.dropdown-check-list { - display: none; - position: absolute; - top: 28%; - right: 1%; - background-color: #222; -} - -.dropdown-check-list .anchor { - position: relative; - cursor: pointer; - display: inline-block; - padding: 5px 50px 5px 10px; - border: 1px solid #333; - width: 7.5vw; /* header width */ -} - -.dropdown-check-list .anchor:after { - position: absolute; - content: ""; - border-left: 2px solid white; - border-top: 2px solid white; - padding: 5px; - right: 10px; - top: 20%; - -moz-transform: rotate(-135deg); - -ms-transform: rotate(-135deg); - -o-transform: rotate(-135deg); - -webkit-transform: rotate(-135deg); - transform: rotate(-135deg); -} - -.dropdown-check-list .anchor:active:after { - right: 8px; - top: 21%; -} - -.dropdown-check-list ul.items { - padding: 2px; - display: ''; - margin: 0; - border: 1px solid #333; - border-top: none; - max-height: 20vh; - height: auto; - width: 7.5vw; /* list width */ - overflow: scroll; -} - -.dropdown-check-list ul.items li { - list-style: none; -} - -label { - color: white; - font-family: Consolas, Baskerville, 'Segoe UI', sans-serif; - font-size: 10px; -} \ No newline at end of file diff --git a/v3/css/d3-context-menu.css b/v3/css/d3-context-menu.css deleted file mode 100644 index a9833e0..0000000 --- a/v3/css/d3-context-menu.css +++ /dev/null @@ -1,27 +0,0 @@ -.d3-context-menu { - position: absolute; - display: none; - background-color: #f2f2f2; - border-radius: 4px; - font-family: Arial, sans-serif; - font-size: 14px; - min-width: 150px; - border: 1px solid #d4d4d4; - z-index:1200; -} - -.d3-context-menu ul { - list-style-type: none; - margin: 4px 0px; - padding: 0px; - cursor: default; -} - -.d3-context-menu ul li { - padding: 4px 16px; -} - -/*.d3-context-menu ul li:hover { - background-color: #4677f8; - color: #fefefe; -}*/ \ No newline at end of file diff --git a/v3/css/default.css b/v3/css/default.css deleted file mode 100644 index 38031d1..0000000 --- a/v3/css/default.css +++ /dev/null @@ -1,4 +0,0 @@ -body { -margin: 0; - overflow: hidden; -} \ No newline at end of file diff --git a/v3/css/derived.css b/v3/css/derived.css deleted file mode 100644 index 6f38ccf..0000000 --- a/v3/css/derived.css +++ /dev/null @@ -1,31 +0,0 @@ -html { - box-sizing: border-box; -} -*, *:before, *:after { - box-sizing: inherit; -} - -body { - margin: 0; - height: 100vh; - overflow: hidden; -} - -.top-nav { - height: 40px; - border-bottom: 1px solid #eee; - padding: 5px; -} - -.main-area { - position: absolute; - top: 40px; - bottom: 0; - left: 0; - width: 100vw; -} - -.viewer-container { - width: 100%; - height: 100%; -} diff --git a/v3/css/favicon_io/android-chrome-192x192.png b/v3/css/favicon_io/android-chrome-192x192.png deleted file mode 100644 index 27c6dd4..0000000 Binary files a/v3/css/favicon_io/android-chrome-192x192.png and /dev/null differ diff --git a/v3/css/favicon_io/android-chrome-512x512.png b/v3/css/favicon_io/android-chrome-512x512.png deleted file mode 100644 index f930896..0000000 Binary files a/v3/css/favicon_io/android-chrome-512x512.png and /dev/null differ diff --git a/v3/css/favicon_io/apple-touch-icon.png b/v3/css/favicon_io/apple-touch-icon.png deleted file mode 100644 index 2b45326..0000000 Binary files a/v3/css/favicon_io/apple-touch-icon.png and /dev/null differ diff --git a/v3/css/favicon_io/favicon-16x16.png b/v3/css/favicon_io/favicon-16x16.png deleted file mode 100644 index 2f6bfe2..0000000 Binary files a/v3/css/favicon_io/favicon-16x16.png and /dev/null differ diff --git a/v3/css/favicon_io/favicon-32x32.png b/v3/css/favicon_io/favicon-32x32.png deleted file mode 100644 index a2ca6b5..0000000 Binary files a/v3/css/favicon_io/favicon-32x32.png and /dev/null differ diff --git a/v3/css/favicon_io/favicon.ico b/v3/css/favicon_io/favicon.ico deleted file mode 100644 index 21e6155..0000000 Binary files a/v3/css/favicon_io/favicon.ico and /dev/null differ diff --git a/v3/css/favicon_io/site.webmanifest b/v3/css/favicon_io/site.webmanifest deleted file mode 100644 index 45dc8a2..0000000 --- a/v3/css/favicon_io/site.webmanifest +++ /dev/null @@ -1 +0,0 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/v3/css/font-awesome-4.7.0/HELP-US-OUT.txt b/v3/css/font-awesome-4.7.0/HELP-US-OUT.txt deleted file mode 100644 index 83d083d..0000000 --- a/v3/css/font-awesome-4.7.0/HELP-US-OUT.txt +++ /dev/null @@ -1,7 +0,0 @@ -I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, -Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, -comprehensive icon sets or copy and paste your own. - -Please. Check it out. - --Dave Gandy diff --git a/v3/css/font-awesome-4.7.0/css/font-awesome.css b/v3/css/font-awesome-4.7.0/css/font-awesome.css deleted file mode 100644 index ee906a8..0000000 --- a/v3/css/font-awesome-4.7.0/css/font-awesome.css +++ /dev/null @@ -1,2337 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */ -/* FONT PATH - * -------------------------- */ -@font-face { - font-family: 'FontAwesome'; - src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); - src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); - font-weight: normal; - font-style: normal; -} -.fa { - display: inline-block; - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -/* makes the font 33% larger relative to the icon container */ -.fa-lg { - font-size: 1.33333333em; - line-height: 0.75em; - vertical-align: -15%; -} -.fa-2x { - font-size: 2em; -} -.fa-3x { - font-size: 3em; -} -.fa-4x { - font-size: 4em; -} -.fa-5x { - font-size: 5em; -} -.fa-fw { - width: 1.28571429em; - text-align: center; -} -.fa-ul { - padding-left: 0; - margin-left: 2.14285714em; - list-style-type: none; -} -.fa-ul > li { - position: relative; -} -.fa-li { - position: absolute; - left: -2.14285714em; - width: 2.14285714em; - top: 0.14285714em; - text-align: center; -} -.fa-li.fa-lg { - left: -1.85714286em; -} -.fa-border { - padding: .2em .25em .15em; - border: solid 0.08em #eeeeee; - border-radius: .1em; -} -.fa-pull-left { - float: left; -} -.fa-pull-right { - float: right; -} -.fa.fa-pull-left { - margin-right: .3em; -} -.fa.fa-pull-right { - margin-left: .3em; -} -/* Deprecated as of 4.4.0 */ -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.fa.pull-left { - margin-right: .3em; -} -.fa.pull-right { - margin-left: .3em; -} -.fa-spin { - -webkit-animation: fa-spin 2s infinite linear; - animation: fa-spin 2s infinite linear; -} -.fa-pulse { - -webkit-animation: fa-spin 1s infinite steps(8); - animation: fa-spin 1s infinite steps(8); -} -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -.fa-rotate-90 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} -.fa-rotate-180 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.fa-rotate-270 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; - -webkit-transform: rotate(270deg); - -ms-transform: rotate(270deg); - transform: rotate(270deg); -} -.fa-flip-horizontal { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; - -webkit-transform: scale(-1, 1); - -ms-transform: scale(-1, 1); - transform: scale(-1, 1); -} -.fa-flip-vertical { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; - -webkit-transform: scale(1, -1); - -ms-transform: scale(1, -1); - transform: scale(1, -1); -} -:root .fa-rotate-90, -:root .fa-rotate-180, -:root .fa-rotate-270, -:root .fa-flip-horizontal, -:root .fa-flip-vertical { - filter: none; -} -.fa-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} -.fa-stack-1x, -.fa-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} -.fa-stack-1x { - line-height: inherit; -} -.fa-stack-2x { - font-size: 2em; -} -.fa-inverse { - color: #ffffff; -} -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ -.fa-glass:before { - content: "\f000"; -} -.fa-music:before { - content: "\f001"; -} -.fa-search:before { - content: "\f002"; -} -.fa-envelope-o:before { - content: "\f003"; -} -.fa-heart:before { - content: "\f004"; -} -.fa-star:before { - content: "\f005"; -} -.fa-star-o:before { - content: "\f006"; -} -.fa-user:before { - content: "\f007"; -} -.fa-film:before { - content: "\f008"; -} -.fa-th-large:before { - content: "\f009"; -} -.fa-th:before { - content: "\f00a"; -} -.fa-th-list:before { - content: "\f00b"; -} -.fa-check:before { - content: "\f00c"; -} -.fa-remove:before, -.fa-close:before, -.fa-times:before { - content: "\f00d"; -} -.fa-search-plus:before { - content: "\f00e"; -} -.fa-search-minus:before { - content: "\f010"; -} -.fa-power-off:before { - content: "\f011"; -} -.fa-signal:before { - content: "\f012"; -} -.fa-gear:before, -.fa-cog:before { - content: "\f013"; -} -.fa-trash-o:before { - content: "\f014"; -} -.fa-home:before { - content: "\f015"; -} -.fa-file-o:before { - content: "\f016"; -} -.fa-clock-o:before { - content: "\f017"; -} -.fa-road:before { - content: "\f018"; -} -.fa-download:before { - content: "\f019"; -} -.fa-arrow-circle-o-down:before { - content: "\f01a"; -} -.fa-arrow-circle-o-up:before { - content: "\f01b"; -} -.fa-inbox:before { - content: "\f01c"; -} -.fa-play-circle-o:before { - content: "\f01d"; -} -.fa-rotate-right:before, -.fa-repeat:before { - content: "\f01e"; -} -.fa-refresh:before { - content: "\f021"; -} -.fa-list-alt:before { - content: "\f022"; -} -.fa-lock:before { - content: "\f023"; -} -.fa-flag:before { - content: "\f024"; -} -.fa-headphones:before { - content: "\f025"; -} -.fa-volume-off:before { - content: "\f026"; -} -.fa-volume-down:before { - content: "\f027"; -} -.fa-volume-up:before { - content: "\f028"; -} -.fa-qrcode:before { - content: "\f029"; -} -.fa-barcode:before { - content: "\f02a"; -} -.fa-tag:before { - content: "\f02b"; -} -.fa-tags:before { - content: "\f02c"; -} -.fa-book:before { - content: "\f02d"; -} -.fa-bookmark:before { - content: "\f02e"; -} -.fa-print:before { - content: "\f02f"; -} -.fa-camera:before { - content: "\f030"; -} -.fa-font:before { - content: "\f031"; -} -.fa-bold:before { - content: "\f032"; -} -.fa-italic:before { - content: "\f033"; -} -.fa-text-height:before { - content: "\f034"; -} -.fa-text-width:before { - content: "\f035"; -} -.fa-align-left:before { - content: "\f036"; -} -.fa-align-center:before { - content: "\f037"; -} -.fa-align-right:before { - content: "\f038"; -} -.fa-align-justify:before { - content: "\f039"; -} -.fa-list:before { - content: "\f03a"; -} -.fa-dedent:before, -.fa-outdent:before { - content: "\f03b"; -} -.fa-indent:before { - content: "\f03c"; -} -.fa-video-camera:before { - content: "\f03d"; -} -.fa-photo:before, -.fa-image:before, -.fa-picture-o:before { - content: "\f03e"; -} -.fa-pencil:before { - content: "\f040"; -} -.fa-map-marker:before { - content: "\f041"; -} -.fa-adjust:before { - content: "\f042"; -} -.fa-tint:before { - content: "\f043"; -} -.fa-edit:before, -.fa-pencil-square-o:before { - content: "\f044"; -} -.fa-share-square-o:before { - content: "\f045"; -} -.fa-check-square-o:before { - content: "\f046"; -} -.fa-arrows:before { - content: "\f047"; -} -.fa-step-backward:before { - content: "\f048"; -} -.fa-fast-backward:before { - content: "\f049"; -} -.fa-backward:before { - content: "\f04a"; -} -.fa-play:before { - content: "\f04b"; -} -.fa-pause:before { - content: "\f04c"; -} -.fa-stop:before { - content: "\f04d"; -} -.fa-forward:before { - content: "\f04e"; -} -.fa-fast-forward:before { - content: "\f050"; -} -.fa-step-forward:before { - content: "\f051"; -} -.fa-eject:before { - content: "\f052"; -} -.fa-chevron-left:before { - content: "\f053"; -} -.fa-chevron-right:before { - content: "\f054"; -} -.fa-plus-circle:before { - content: "\f055"; -} -.fa-minus-circle:before { - content: "\f056"; -} -.fa-times-circle:before { - content: "\f057"; -} -.fa-check-circle:before { - content: "\f058"; -} -.fa-question-circle:before { - content: "\f059"; -} -.fa-info-circle:before { - content: "\f05a"; -} -.fa-crosshairs:before { - content: "\f05b"; -} -.fa-times-circle-o:before { - content: "\f05c"; -} -.fa-check-circle-o:before { - content: "\f05d"; -} -.fa-ban:before { - content: "\f05e"; -} -.fa-arrow-left:before { - content: "\f060"; -} -.fa-arrow-right:before { - content: "\f061"; -} -.fa-arrow-up:before { - content: "\f062"; -} -.fa-arrow-down:before { - content: "\f063"; -} -.fa-mail-forward:before, -.fa-share:before { - content: "\f064"; -} -.fa-expand:before { - content: "\f065"; -} -.fa-compress:before { - content: "\f066"; -} -.fa-plus:before { - content: "\f067"; -} -.fa-minus:before { - content: "\f068"; -} -.fa-asterisk:before { - content: "\f069"; -} -.fa-exclamation-circle:before { - content: "\f06a"; -} -.fa-gift:before { - content: "\f06b"; -} -.fa-leaf:before { - content: "\f06c"; -} -.fa-fire:before { - content: "\f06d"; -} -.fa-eye:before { - content: "\f06e"; -} -.fa-eye-slash:before { - content: "\f070"; -} -.fa-warning:before, -.fa-exclamation-triangle:before { - content: "\f071"; -} -.fa-plane:before { - content: "\f072"; -} -.fa-calendar:before { - content: "\f073"; -} -.fa-random:before { - content: "\f074"; -} -.fa-comment:before { - content: "\f075"; -} -.fa-magnet:before { - content: "\f076"; -} -.fa-chevron-up:before { - content: "\f077"; -} -.fa-chevron-down:before { - content: "\f078"; -} -.fa-retweet:before { - content: "\f079"; -} -.fa-shopping-cart:before { - content: "\f07a"; -} -.fa-folder:before { - content: "\f07b"; -} -.fa-folder-open:before { - content: "\f07c"; -} -.fa-arrows-v:before { - content: "\f07d"; -} -.fa-arrows-h:before { - content: "\f07e"; -} -.fa-bar-chart-o:before, -.fa-bar-chart:before { - content: "\f080"; -} -.fa-twitter-square:before { - content: "\f081"; -} -.fa-facebook-square:before { - content: "\f082"; -} -.fa-camera-retro:before { - content: "\f083"; -} -.fa-key:before { - content: "\f084"; -} -.fa-gears:before, -.fa-cogs:before { - content: "\f085"; -} -.fa-comments:before { - content: "\f086"; -} -.fa-thumbs-o-up:before { - content: "\f087"; -} -.fa-thumbs-o-down:before { - content: "\f088"; -} -.fa-star-half:before { - content: "\f089"; -} -.fa-heart-o:before { - content: "\f08a"; -} -.fa-sign-out:before { - content: "\f08b"; -} -.fa-linkedin-square:before { - content: "\f08c"; -} -.fa-thumb-tack:before { - content: "\f08d"; -} -.fa-external-link:before { - content: "\f08e"; -} -.fa-sign-in:before { - content: "\f090"; -} -.fa-trophy:before { - content: "\f091"; -} -.fa-github-square:before { - content: "\f092"; -} -.fa-upload:before { - content: "\f093"; -} -.fa-lemon-o:before { - content: "\f094"; -} -.fa-phone:before { - content: "\f095"; -} -.fa-square-o:before { - content: "\f096"; -} -.fa-bookmark-o:before { - content: "\f097"; -} -.fa-phone-square:before { - content: "\f098"; -} -.fa-twitter:before { - content: "\f099"; -} -.fa-facebook-f:before, -.fa-facebook:before { - content: "\f09a"; -} -.fa-github:before { - content: "\f09b"; -} -.fa-unlock:before { - content: "\f09c"; -} -.fa-credit-card:before { - content: "\f09d"; -} -.fa-feed:before, -.fa-rss:before { - content: "\f09e"; -} -.fa-hdd-o:before { - content: "\f0a0"; -} -.fa-bullhorn:before { - content: "\f0a1"; -} -.fa-bell:before { - content: "\f0f3"; -} -.fa-certificate:before { - content: "\f0a3"; -} -.fa-hand-o-right:before { - content: "\f0a4"; -} -.fa-hand-o-left:before { - content: "\f0a5"; -} -.fa-hand-o-up:before { - content: "\f0a6"; -} -.fa-hand-o-down:before { - content: "\f0a7"; -} -.fa-arrow-circle-left:before { - content: "\f0a8"; -} -.fa-arrow-circle-right:before { - content: "\f0a9"; -} -.fa-arrow-circle-up:before { - content: "\f0aa"; -} -.fa-arrow-circle-down:before { - content: "\f0ab"; -} -.fa-globe:before { - content: "\f0ac"; -} -.fa-wrench:before { - content: "\f0ad"; -} -.fa-tasks:before { - content: "\f0ae"; -} -.fa-filter:before { - content: "\f0b0"; -} -.fa-briefcase:before { - content: "\f0b1"; -} -.fa-arrows-alt:before { - content: "\f0b2"; -} -.fa-group:before, -.fa-users:before { - content: "\f0c0"; -} -.fa-chain:before, -.fa-link:before { - content: "\f0c1"; -} -.fa-cloud:before { - content: "\f0c2"; -} -.fa-flask:before { - content: "\f0c3"; -} -.fa-cut:before, -.fa-scissors:before { - content: "\f0c4"; -} -.fa-copy:before, -.fa-files-o:before { - content: "\f0c5"; -} -.fa-paperclip:before { - content: "\f0c6"; -} -.fa-save:before, -.fa-floppy-o:before { - content: "\f0c7"; -} -.fa-square:before { - content: "\f0c8"; -} -.fa-navicon:before, -.fa-reorder:before, -.fa-bars:before { - content: "\f0c9"; -} -.fa-list-ul:before { - content: "\f0ca"; -} -.fa-list-ol:before { - content: "\f0cb"; -} -.fa-strikethrough:before { - content: "\f0cc"; -} -.fa-underline:before { - content: "\f0cd"; -} -.fa-table:before { - content: "\f0ce"; -} -.fa-magic:before { - content: "\f0d0"; -} -.fa-truck:before { - content: "\f0d1"; -} -.fa-pinterest:before { - content: "\f0d2"; -} -.fa-pinterest-square:before { - content: "\f0d3"; -} -.fa-google-plus-square:before { - content: "\f0d4"; -} -.fa-google-plus:before { - content: "\f0d5"; -} -.fa-money:before { - content: "\f0d6"; -} -.fa-caret-down:before { - content: "\f0d7"; -} -.fa-caret-up:before { - content: "\f0d8"; -} -.fa-caret-left:before { - content: "\f0d9"; -} -.fa-caret-right:before { - content: "\f0da"; -} -.fa-columns:before { - content: "\f0db"; -} -.fa-unsorted:before, -.fa-sort:before { - content: "\f0dc"; -} -.fa-sort-down:before, -.fa-sort-desc:before { - content: "\f0dd"; -} -.fa-sort-up:before, -.fa-sort-asc:before { - content: "\f0de"; -} -.fa-envelope:before { - content: "\f0e0"; -} -.fa-linkedin:before { - content: "\f0e1"; -} -.fa-rotate-left:before, -.fa-undo:before { - content: "\f0e2"; -} -.fa-legal:before, -.fa-gavel:before { - content: "\f0e3"; -} -.fa-dashboard:before, -.fa-tachometer:before { - content: "\f0e4"; -} -.fa-comment-o:before { - content: "\f0e5"; -} -.fa-comments-o:before { - content: "\f0e6"; -} -.fa-flash:before, -.fa-bolt:before { - content: "\f0e7"; -} -.fa-sitemap:before { - content: "\f0e8"; -} -.fa-umbrella:before { - content: "\f0e9"; -} -.fa-paste:before, -.fa-clipboard:before { - content: "\f0ea"; -} -.fa-lightbulb-o:before { - content: "\f0eb"; -} -.fa-exchange:before { - content: "\f0ec"; -} -.fa-cloud-download:before { - content: "\f0ed"; -} -.fa-cloud-upload:before { - content: "\f0ee"; -} -.fa-user-md:before { - content: "\f0f0"; -} -.fa-stethoscope:before { - content: "\f0f1"; -} -.fa-suitcase:before { - content: "\f0f2"; -} -.fa-bell-o:before { - content: "\f0a2"; -} -.fa-coffee:before { - content: "\f0f4"; -} -.fa-cutlery:before { - content: "\f0f5"; -} -.fa-file-text-o:before { - content: "\f0f6"; -} -.fa-building-o:before { - content: "\f0f7"; -} -.fa-hospital-o:before { - content: "\f0f8"; -} -.fa-ambulance:before { - content: "\f0f9"; -} -.fa-medkit:before { - content: "\f0fa"; -} -.fa-fighter-jet:before { - content: "\f0fb"; -} -.fa-beer:before { - content: "\f0fc"; -} -.fa-h-square:before { - content: "\f0fd"; -} -.fa-plus-square:before { - content: "\f0fe"; -} -.fa-angle-double-left:before { - content: "\f100"; -} -.fa-angle-double-right:before { - content: "\f101"; -} -.fa-angle-double-up:before { - content: "\f102"; -} -.fa-angle-double-down:before { - content: "\f103"; -} -.fa-angle-left:before { - content: "\f104"; -} -.fa-angle-right:before { - content: "\f105"; -} -.fa-angle-up:before { - content: "\f106"; -} -.fa-angle-down:before { - content: "\f107"; -} -.fa-desktop:before { - content: "\f108"; -} -.fa-laptop:before { - content: "\f109"; -} -.fa-tablet:before { - content: "\f10a"; -} -.fa-mobile-phone:before, -.fa-mobile:before { - content: "\f10b"; -} -.fa-circle-o:before { - content: "\f10c"; -} -.fa-quote-left:before { - content: "\f10d"; -} -.fa-quote-right:before { - content: "\f10e"; -} -.fa-spinner:before { - content: "\f110"; -} -.fa-circle:before { - content: "\f111"; -} -.fa-mail-reply:before, -.fa-reply:before { - content: "\f112"; -} -.fa-github-alt:before { - content: "\f113"; -} -.fa-folder-o:before { - content: "\f114"; -} -.fa-folder-open-o:before { - content: "\f115"; -} -.fa-smile-o:before { - content: "\f118"; -} -.fa-frown-o:before { - content: "\f119"; -} -.fa-meh-o:before { - content: "\f11a"; -} -.fa-gamepad:before { - content: "\f11b"; -} -.fa-keyboard-o:before { - content: "\f11c"; -} -.fa-flag-o:before { - content: "\f11d"; -} -.fa-flag-checkered:before { - content: "\f11e"; -} -.fa-terminal:before { - content: "\f120"; -} -.fa-code:before { - content: "\f121"; -} -.fa-mail-reply-all:before, -.fa-reply-all:before { - content: "\f122"; -} -.fa-star-half-empty:before, -.fa-star-half-full:before, -.fa-star-half-o:before { - content: "\f123"; -} -.fa-location-arrow:before { - content: "\f124"; -} -.fa-crop:before { - content: "\f125"; -} -.fa-code-fork:before { - content: "\f126"; -} -.fa-unlink:before, -.fa-chain-broken:before { - content: "\f127"; -} -.fa-question:before { - content: "\f128"; -} -.fa-info:before { - content: "\f129"; -} -.fa-exclamation:before { - content: "\f12a"; -} -.fa-superscript:before { - content: "\f12b"; -} -.fa-subscript:before { - content: "\f12c"; -} -.fa-eraser:before { - content: "\f12d"; -} -.fa-puzzle-piece:before { - content: "\f12e"; -} -.fa-microphone:before { - content: "\f130"; -} -.fa-microphone-slash:before { - content: "\f131"; -} -.fa-shield:before { - content: "\f132"; -} -.fa-calendar-o:before { - content: "\f133"; -} -.fa-fire-extinguisher:before { - content: "\f134"; -} -.fa-rocket:before { - content: "\f135"; -} -.fa-maxcdn:before { - content: "\f136"; -} -.fa-chevron-circle-left:before { - content: "\f137"; -} -.fa-chevron-circle-right:before { - content: "\f138"; -} -.fa-chevron-circle-up:before { - content: "\f139"; -} -.fa-chevron-circle-down:before { - content: "\f13a"; -} -.fa-html5:before { - content: "\f13b"; -} -.fa-css3:before { - content: "\f13c"; -} -.fa-anchor:before { - content: "\f13d"; -} -.fa-unlock-alt:before { - content: "\f13e"; -} -.fa-bullseye:before { - content: "\f140"; -} -.fa-ellipsis-h:before { - content: "\f141"; -} -.fa-ellipsis-v:before { - content: "\f142"; -} -.fa-rss-square:before { - content: "\f143"; -} -.fa-play-circle:before { - content: "\f144"; -} -.fa-ticket:before { - content: "\f145"; -} -.fa-minus-square:before { - content: "\f146"; -} -.fa-minus-square-o:before { - content: "\f147"; -} -.fa-level-up:before { - content: "\f148"; -} -.fa-level-down:before { - content: "\f149"; -} -.fa-check-square:before { - content: "\f14a"; -} -.fa-pencil-square:before { - content: "\f14b"; -} -.fa-external-link-square:before { - content: "\f14c"; -} -.fa-share-square:before { - content: "\f14d"; -} -.fa-compass:before { - content: "\f14e"; -} -.fa-toggle-down:before, -.fa-caret-square-o-down:before { - content: "\f150"; -} -.fa-toggle-up:before, -.fa-caret-square-o-up:before { - content: "\f151"; -} -.fa-toggle-right:before, -.fa-caret-square-o-right:before { - content: "\f152"; -} -.fa-euro:before, -.fa-eur:before { - content: "\f153"; -} -.fa-gbp:before { - content: "\f154"; -} -.fa-dollar:before, -.fa-usd:before { - content: "\f155"; -} -.fa-rupee:before, -.fa-inr:before { - content: "\f156"; -} -.fa-cny:before, -.fa-rmb:before, -.fa-yen:before, -.fa-jpy:before { - content: "\f157"; -} -.fa-ruble:before, -.fa-rouble:before, -.fa-rub:before { - content: "\f158"; -} -.fa-won:before, -.fa-krw:before { - content: "\f159"; -} -.fa-bitcoin:before, -.fa-btc:before { - content: "\f15a"; -} -.fa-file:before { - content: "\f15b"; -} -.fa-file-text:before { - content: "\f15c"; -} -.fa-sort-alpha-asc:before { - content: "\f15d"; -} -.fa-sort-alpha-desc:before { - content: "\f15e"; -} -.fa-sort-amount-asc:before { - content: "\f160"; -} -.fa-sort-amount-desc:before { - content: "\f161"; -} -.fa-sort-numeric-asc:before { - content: "\f162"; -} -.fa-sort-numeric-desc:before { - content: "\f163"; -} -.fa-thumbs-up:before { - content: "\f164"; -} -.fa-thumbs-down:before { - content: "\f165"; -} -.fa-youtube-square:before { - content: "\f166"; -} -.fa-youtube:before { - content: "\f167"; -} -.fa-xing:before { - content: "\f168"; -} -.fa-xing-square:before { - content: "\f169"; -} -.fa-youtube-play:before { - content: "\f16a"; -} -.fa-dropbox:before { - content: "\f16b"; -} -.fa-stack-overflow:before { - content: "\f16c"; -} -.fa-instagram:before { - content: "\f16d"; -} -.fa-flickr:before { - content: "\f16e"; -} -.fa-adn:before { - content: "\f170"; -} -.fa-bitbucket:before { - content: "\f171"; -} -.fa-bitbucket-square:before { - content: "\f172"; -} -.fa-tumblr:before { - content: "\f173"; -} -.fa-tumblr-square:before { - content: "\f174"; -} -.fa-long-arrow-down:before { - content: "\f175"; -} -.fa-long-arrow-up:before { - content: "\f176"; -} -.fa-long-arrow-left:before { - content: "\f177"; -} -.fa-long-arrow-right:before { - content: "\f178"; -} -.fa-apple:before { - content: "\f179"; -} -.fa-windows:before { - content: "\f17a"; -} -.fa-android:before { - content: "\f17b"; -} -.fa-linux:before { - content: "\f17c"; -} -.fa-dribbble:before { - content: "\f17d"; -} -.fa-skype:before { - content: "\f17e"; -} -.fa-foursquare:before { - content: "\f180"; -} -.fa-trello:before { - content: "\f181"; -} -.fa-female:before { - content: "\f182"; -} -.fa-male:before { - content: "\f183"; -} -.fa-gittip:before, -.fa-gratipay:before { - content: "\f184"; -} -.fa-sun-o:before { - content: "\f185"; -} -.fa-moon-o:before { - content: "\f186"; -} -.fa-archive:before { - content: "\f187"; -} -.fa-bug:before { - content: "\f188"; -} -.fa-vk:before { - content: "\f189"; -} -.fa-weibo:before { - content: "\f18a"; -} -.fa-renren:before { - content: "\f18b"; -} -.fa-pagelines:before { - content: "\f18c"; -} -.fa-stack-exchange:before { - content: "\f18d"; -} -.fa-arrow-circle-o-right:before { - content: "\f18e"; -} -.fa-arrow-circle-o-left:before { - content: "\f190"; -} -.fa-toggle-left:before, -.fa-caret-square-o-left:before { - content: "\f191"; -} -.fa-dot-circle-o:before { - content: "\f192"; -} -.fa-wheelchair:before { - content: "\f193"; -} -.fa-vimeo-square:before { - content: "\f194"; -} -.fa-turkish-lira:before, -.fa-try:before { - content: "\f195"; -} -.fa-plus-square-o:before { - content: "\f196"; -} -.fa-space-shuttle:before { - content: "\f197"; -} -.fa-slack:before { - content: "\f198"; -} -.fa-envelope-square:before { - content: "\f199"; -} -.fa-wordpress:before { - content: "\f19a"; -} -.fa-openid:before { - content: "\f19b"; -} -.fa-institution:before, -.fa-bank:before, -.fa-university:before { - content: "\f19c"; -} -.fa-mortar-board:before, -.fa-graduation-cap:before { - content: "\f19d"; -} -.fa-yahoo:before { - content: "\f19e"; -} -.fa-google:before { - content: "\f1a0"; -} -.fa-reddit:before { - content: "\f1a1"; -} -.fa-reddit-square:before { - content: "\f1a2"; -} -.fa-stumbleupon-circle:before { - content: "\f1a3"; -} -.fa-stumbleupon:before { - content: "\f1a4"; -} -.fa-delicious:before { - content: "\f1a5"; -} -.fa-digg:before { - content: "\f1a6"; -} -.fa-pied-piper-pp:before { - content: "\f1a7"; -} -.fa-pied-piper-alt:before { - content: "\f1a8"; -} -.fa-drupal:before { - content: "\f1a9"; -} -.fa-joomla:before { - content: "\f1aa"; -} -.fa-language:before { - content: "\f1ab"; -} -.fa-fax:before { - content: "\f1ac"; -} -.fa-building:before { - content: "\f1ad"; -} -.fa-child:before { - content: "\f1ae"; -} -.fa-paw:before { - content: "\f1b0"; -} -.fa-spoon:before { - content: "\f1b1"; -} -.fa-cube:before { - content: "\f1b2"; -} -.fa-cubes:before { - content: "\f1b3"; -} -.fa-behance:before { - content: "\f1b4"; -} -.fa-behance-square:before { - content: "\f1b5"; -} -.fa-steam:before { - content: "\f1b6"; -} -.fa-steam-square:before { - content: "\f1b7"; -} -.fa-recycle:before { - content: "\f1b8"; -} -.fa-automobile:before, -.fa-car:before { - content: "\f1b9"; -} -.fa-cab:before, -.fa-taxi:before { - content: "\f1ba"; -} -.fa-tree:before { - content: "\f1bb"; -} -.fa-spotify:before { - content: "\f1bc"; -} -.fa-deviantart:before { - content: "\f1bd"; -} -.fa-soundcloud:before { - content: "\f1be"; -} -.fa-database:before { - content: "\f1c0"; -} -.fa-file-pdf-o:before { - content: "\f1c1"; -} -.fa-file-word-o:before { - content: "\f1c2"; -} -.fa-file-excel-o:before { - content: "\f1c3"; -} -.fa-file-powerpoint-o:before { - content: "\f1c4"; -} -.fa-file-photo-o:before, -.fa-file-picture-o:before, -.fa-file-image-o:before { - content: "\f1c5"; -} -.fa-file-zip-o:before, -.fa-file-archive-o:before { - content: "\f1c6"; -} -.fa-file-sound-o:before, -.fa-file-audio-o:before { - content: "\f1c7"; -} -.fa-file-movie-o:before, -.fa-file-video-o:before { - content: "\f1c8"; -} -.fa-file-code-o:before { - content: "\f1c9"; -} -.fa-vine:before { - content: "\f1ca"; -} -.fa-codepen:before { - content: "\f1cb"; -} -.fa-jsfiddle:before { - content: "\f1cc"; -} -.fa-life-bouy:before, -.fa-life-buoy:before, -.fa-life-saver:before, -.fa-support:before, -.fa-life-ring:before { - content: "\f1cd"; -} -.fa-circle-o-notch:before { - content: "\f1ce"; -} -.fa-ra:before, -.fa-resistance:before, -.fa-rebel:before { - content: "\f1d0"; -} -.fa-ge:before, -.fa-empire:before { - content: "\f1d1"; -} -.fa-git-square:before { - content: "\f1d2"; -} -.fa-git:before { - content: "\f1d3"; -} -.fa-y-combinator-square:before, -.fa-yc-square:before, -.fa-hacker-news:before { - content: "\f1d4"; -} -.fa-tencent-weibo:before { - content: "\f1d5"; -} -.fa-qq:before { - content: "\f1d6"; -} -.fa-wechat:before, -.fa-weixin:before { - content: "\f1d7"; -} -.fa-send:before, -.fa-paper-plane:before { - content: "\f1d8"; -} -.fa-send-o:before, -.fa-paper-plane-o:before { - content: "\f1d9"; -} -.fa-history:before { - content: "\f1da"; -} -.fa-circle-thin:before { - content: "\f1db"; -} -.fa-header:before { - content: "\f1dc"; -} -.fa-paragraph:before { - content: "\f1dd"; -} -.fa-sliders:before { - content: "\f1de"; -} -.fa-share-alt:before { - content: "\f1e0"; -} -.fa-share-alt-square:before { - content: "\f1e1"; -} -.fa-bomb:before { - content: "\f1e2"; -} -.fa-soccer-ball-o:before, -.fa-futbol-o:before { - content: "\f1e3"; -} -.fa-tty:before { - content: "\f1e4"; -} -.fa-binoculars:before { - content: "\f1e5"; -} -.fa-plug:before { - content: "\f1e6"; -} -.fa-slideshare:before { - content: "\f1e7"; -} -.fa-twitch:before { - content: "\f1e8"; -} -.fa-yelp:before { - content: "\f1e9"; -} -.fa-newspaper-o:before { - content: "\f1ea"; -} -.fa-wifi:before { - content: "\f1eb"; -} -.fa-calculator:before { - content: "\f1ec"; -} -.fa-paypal:before { - content: "\f1ed"; -} -.fa-google-wallet:before { - content: "\f1ee"; -} -.fa-cc-visa:before { - content: "\f1f0"; -} -.fa-cc-mastercard:before { - content: "\f1f1"; -} -.fa-cc-discover:before { - content: "\f1f2"; -} -.fa-cc-amex:before { - content: "\f1f3"; -} -.fa-cc-paypal:before { - content: "\f1f4"; -} -.fa-cc-stripe:before { - content: "\f1f5"; -} -.fa-bell-slash:before { - content: "\f1f6"; -} -.fa-bell-slash-o:before { - content: "\f1f7"; -} -.fa-trash:before { - content: "\f1f8"; -} -.fa-copyright:before { - content: "\f1f9"; -} -.fa-at:before { - content: "\f1fa"; -} -.fa-eyedropper:before { - content: "\f1fb"; -} -.fa-paint-brush:before { - content: "\f1fc"; -} -.fa-birthday-cake:before { - content: "\f1fd"; -} -.fa-area-chart:before { - content: "\f1fe"; -} -.fa-pie-chart:before { - content: "\f200"; -} -.fa-line-chart:before { - content: "\f201"; -} -.fa-lastfm:before { - content: "\f202"; -} -.fa-lastfm-square:before { - content: "\f203"; -} -.fa-toggle-off:before { - content: "\f204"; -} -.fa-toggle-on:before { - content: "\f205"; -} -.fa-bicycle:before { - content: "\f206"; -} -.fa-bus:before { - content: "\f207"; -} -.fa-ioxhost:before { - content: "\f208"; -} -.fa-angellist:before { - content: "\f209"; -} -.fa-cc:before { - content: "\f20a"; -} -.fa-shekel:before, -.fa-sheqel:before, -.fa-ils:before { - content: "\f20b"; -} -.fa-meanpath:before { - content: "\f20c"; -} -.fa-buysellads:before { - content: "\f20d"; -} -.fa-connectdevelop:before { - content: "\f20e"; -} -.fa-dashcube:before { - content: "\f210"; -} -.fa-forumbee:before { - content: "\f211"; -} -.fa-leanpub:before { - content: "\f212"; -} -.fa-sellsy:before { - content: "\f213"; -} -.fa-shirtsinbulk:before { - content: "\f214"; -} -.fa-simplybuilt:before { - content: "\f215"; -} -.fa-skyatlas:before { - content: "\f216"; -} -.fa-cart-plus:before { - content: "\f217"; -} -.fa-cart-arrow-down:before { - content: "\f218"; -} -.fa-diamond:before { - content: "\f219"; -} -.fa-ship:before { - content: "\f21a"; -} -.fa-user-secret:before { - content: "\f21b"; -} -.fa-motorcycle:before { - content: "\f21c"; -} -.fa-street-view:before { - content: "\f21d"; -} -.fa-heartbeat:before { - content: "\f21e"; -} -.fa-venus:before { - content: "\f221"; -} -.fa-mars:before { - content: "\f222"; -} -.fa-mercury:before { - content: "\f223"; -} -.fa-intersex:before, -.fa-transgender:before { - content: "\f224"; -} -.fa-transgender-alt:before { - content: "\f225"; -} -.fa-venus-double:before { - content: "\f226"; -} -.fa-mars-double:before { - content: "\f227"; -} -.fa-venus-mars:before { - content: "\f228"; -} -.fa-mars-stroke:before { - content: "\f229"; -} -.fa-mars-stroke-v:before { - content: "\f22a"; -} -.fa-mars-stroke-h:before { - content: "\f22b"; -} -.fa-neuter:before { - content: "\f22c"; -} -.fa-genderless:before { - content: "\f22d"; -} -.fa-facebook-official:before { - content: "\f230"; -} -.fa-pinterest-p:before { - content: "\f231"; -} -.fa-whatsapp:before { - content: "\f232"; -} -.fa-server:before { - content: "\f233"; -} -.fa-user-plus:before { - content: "\f234"; -} -.fa-user-times:before { - content: "\f235"; -} -.fa-hotel:before, -.fa-bed:before { - content: "\f236"; -} -.fa-viacoin:before { - content: "\f237"; -} -.fa-train:before { - content: "\f238"; -} -.fa-subway:before { - content: "\f239"; -} -.fa-medium:before { - content: "\f23a"; -} -.fa-yc:before, -.fa-y-combinator:before { - content: "\f23b"; -} -.fa-optin-monster:before { - content: "\f23c"; -} -.fa-opencart:before { - content: "\f23d"; -} -.fa-expeditedssl:before { - content: "\f23e"; -} -.fa-battery-4:before, -.fa-battery:before, -.fa-battery-full:before { - content: "\f240"; -} -.fa-battery-3:before, -.fa-battery-three-quarters:before { - content: "\f241"; -} -.fa-battery-2:before, -.fa-battery-half:before { - content: "\f242"; -} -.fa-battery-1:before, -.fa-battery-quarter:before { - content: "\f243"; -} -.fa-battery-0:before, -.fa-battery-empty:before { - content: "\f244"; -} -.fa-mouse-pointer:before { - content: "\f245"; -} -.fa-i-cursor:before { - content: "\f246"; -} -.fa-object-group:before { - content: "\f247"; -} -.fa-object-ungroup:before { - content: "\f248"; -} -.fa-sticky-note:before { - content: "\f249"; -} -.fa-sticky-note-o:before { - content: "\f24a"; -} -.fa-cc-jcb:before { - content: "\f24b"; -} -.fa-cc-diners-club:before { - content: "\f24c"; -} -.fa-clone:before { - content: "\f24d"; -} -.fa-balance-scale:before { - content: "\f24e"; -} -.fa-hourglass-o:before { - content: "\f250"; -} -.fa-hourglass-1:before, -.fa-hourglass-start:before { - content: "\f251"; -} -.fa-hourglass-2:before, -.fa-hourglass-half:before { - content: "\f252"; -} -.fa-hourglass-3:before, -.fa-hourglass-end:before { - content: "\f253"; -} -.fa-hourglass:before { - content: "\f254"; -} -.fa-hand-grab-o:before, -.fa-hand-rock-o:before { - content: "\f255"; -} -.fa-hand-stop-o:before, -.fa-hand-paper-o:before { - content: "\f256"; -} -.fa-hand-scissors-o:before { - content: "\f257"; -} -.fa-hand-lizard-o:before { - content: "\f258"; -} -.fa-hand-spock-o:before { - content: "\f259"; -} -.fa-hand-pointer-o:before { - content: "\f25a"; -} -.fa-hand-peace-o:before { - content: "\f25b"; -} -.fa-trademark:before { - content: "\f25c"; -} -.fa-registered:before { - content: "\f25d"; -} -.fa-creative-commons:before { - content: "\f25e"; -} -.fa-gg:before { - content: "\f260"; -} -.fa-gg-circle:before { - content: "\f261"; -} -.fa-tripadvisor:before { - content: "\f262"; -} -.fa-odnoklassniki:before { - content: "\f263"; -} -.fa-odnoklassniki-square:before { - content: "\f264"; -} -.fa-get-pocket:before { - content: "\f265"; -} -.fa-wikipedia-w:before { - content: "\f266"; -} -.fa-safari:before { - content: "\f267"; -} -.fa-chrome:before { - content: "\f268"; -} -.fa-firefox:before { - content: "\f269"; -} -.fa-opera:before { - content: "\f26a"; -} -.fa-internet-explorer:before { - content: "\f26b"; -} -.fa-tv:before, -.fa-television:before { - content: "\f26c"; -} -.fa-contao:before { - content: "\f26d"; -} -.fa-500px:before { - content: "\f26e"; -} -.fa-amazon:before { - content: "\f270"; -} -.fa-calendar-plus-o:before { - content: "\f271"; -} -.fa-calendar-minus-o:before { - content: "\f272"; -} -.fa-calendar-times-o:before { - content: "\f273"; -} -.fa-calendar-check-o:before { - content: "\f274"; -} -.fa-industry:before { - content: "\f275"; -} -.fa-map-pin:before { - content: "\f276"; -} -.fa-map-signs:before { - content: "\f277"; -} -.fa-map-o:before { - content: "\f278"; -} -.fa-map:before { - content: "\f279"; -} -.fa-commenting:before { - content: "\f27a"; -} -.fa-commenting-o:before { - content: "\f27b"; -} -.fa-houzz:before { - content: "\f27c"; -} -.fa-vimeo:before { - content: "\f27d"; -} -.fa-black-tie:before { - content: "\f27e"; -} -.fa-fonticons:before { - content: "\f280"; -} -.fa-reddit-alien:before { - content: "\f281"; -} -.fa-edge:before { - content: "\f282"; -} -.fa-credit-card-alt:before { - content: "\f283"; -} -.fa-codiepie:before { - content: "\f284"; -} -.fa-modx:before { - content: "\f285"; -} -.fa-fort-awesome:before { - content: "\f286"; -} -.fa-usb:before { - content: "\f287"; -} -.fa-product-hunt:before { - content: "\f288"; -} -.fa-mixcloud:before { - content: "\f289"; -} -.fa-scribd:before { - content: "\f28a"; -} -.fa-pause-circle:before { - content: "\f28b"; -} -.fa-pause-circle-o:before { - content: "\f28c"; -} -.fa-stop-circle:before { - content: "\f28d"; -} -.fa-stop-circle-o:before { - content: "\f28e"; -} -.fa-shopping-bag:before { - content: "\f290"; -} -.fa-shopping-basket:before { - content: "\f291"; -} -.fa-hashtag:before { - content: "\f292"; -} -.fa-bluetooth:before { - content: "\f293"; -} -.fa-bluetooth-b:before { - content: "\f294"; -} -.fa-percent:before { - content: "\f295"; -} -.fa-gitlab:before { - content: "\f296"; -} -.fa-wpbeginner:before { - content: "\f297"; -} -.fa-wpforms:before { - content: "\f298"; -} -.fa-envira:before { - content: "\f299"; -} -.fa-universal-access:before { - content: "\f29a"; -} -.fa-wheelchair-alt:before { - content: "\f29b"; -} -.fa-question-circle-o:before { - content: "\f29c"; -} -.fa-blind:before { - content: "\f29d"; -} -.fa-audio-description:before { - content: "\f29e"; -} -.fa-volume-control-phone:before { - content: "\f2a0"; -} -.fa-braille:before { - content: "\f2a1"; -} -.fa-assistive-listening-systems:before { - content: "\f2a2"; -} -.fa-asl-interpreting:before, -.fa-american-sign-language-interpreting:before { - content: "\f2a3"; -} -.fa-deafness:before, -.fa-hard-of-hearing:before, -.fa-deaf:before { - content: "\f2a4"; -} -.fa-glide:before { - content: "\f2a5"; -} -.fa-glide-g:before { - content: "\f2a6"; -} -.fa-signing:before, -.fa-sign-language:before { - content: "\f2a7"; -} -.fa-low-vision:before { - content: "\f2a8"; -} -.fa-viadeo:before { - content: "\f2a9"; -} -.fa-viadeo-square:before { - content: "\f2aa"; -} -.fa-snapchat:before { - content: "\f2ab"; -} -.fa-snapchat-ghost:before { - content: "\f2ac"; -} -.fa-snapchat-square:before { - content: "\f2ad"; -} -.fa-pied-piper:before { - content: "\f2ae"; -} -.fa-first-order:before { - content: "\f2b0"; -} -.fa-yoast:before { - content: "\f2b1"; -} -.fa-themeisle:before { - content: "\f2b2"; -} -.fa-google-plus-circle:before, -.fa-google-plus-official:before { - content: "\f2b3"; -} -.fa-fa:before, -.fa-font-awesome:before { - content: "\f2b4"; -} -.fa-handshake-o:before { - content: "\f2b5"; -} -.fa-envelope-open:before { - content: "\f2b6"; -} -.fa-envelope-open-o:before { - content: "\f2b7"; -} -.fa-linode:before { - content: "\f2b8"; -} -.fa-address-book:before { - content: "\f2b9"; -} -.fa-address-book-o:before { - content: "\f2ba"; -} -.fa-vcard:before, -.fa-address-card:before { - content: "\f2bb"; -} -.fa-vcard-o:before, -.fa-address-card-o:before { - content: "\f2bc"; -} -.fa-user-circle:before { - content: "\f2bd"; -} -.fa-user-circle-o:before { - content: "\f2be"; -} -.fa-user-o:before { - content: "\f2c0"; -} -.fa-id-badge:before { - content: "\f2c1"; -} -.fa-drivers-license:before, -.fa-id-card:before { - content: "\f2c2"; -} -.fa-drivers-license-o:before, -.fa-id-card-o:before { - content: "\f2c3"; -} -.fa-quora:before { - content: "\f2c4"; -} -.fa-free-code-camp:before { - content: "\f2c5"; -} -.fa-telegram:before { - content: "\f2c6"; -} -.fa-thermometer-4:before, -.fa-thermometer:before, -.fa-thermometer-full:before { - content: "\f2c7"; -} -.fa-thermometer-3:before, -.fa-thermometer-three-quarters:before { - content: "\f2c8"; -} -.fa-thermometer-2:before, -.fa-thermometer-half:before { - content: "\f2c9"; -} -.fa-thermometer-1:before, -.fa-thermometer-quarter:before { - content: "\f2ca"; -} -.fa-thermometer-0:before, -.fa-thermometer-empty:before { - content: "\f2cb"; -} -.fa-shower:before { - content: "\f2cc"; -} -.fa-bathtub:before, -.fa-s15:before, -.fa-bath:before { - content: "\f2cd"; -} -.fa-podcast:before { - content: "\f2ce"; -} -.fa-window-maximize:before { - content: "\f2d0"; -} -.fa-window-minimize:before { - content: "\f2d1"; -} -.fa-window-restore:before { - content: "\f2d2"; -} -.fa-times-rectangle:before, -.fa-window-close:before { - content: "\f2d3"; -} -.fa-times-rectangle-o:before, -.fa-window-close-o:before { - content: "\f2d4"; -} -.fa-bandcamp:before { - content: "\f2d5"; -} -.fa-grav:before { - content: "\f2d6"; -} -.fa-etsy:before { - content: "\f2d7"; -} -.fa-imdb:before { - content: "\f2d8"; -} -.fa-ravelry:before { - content: "\f2d9"; -} -.fa-eercast:before { - content: "\f2da"; -} -.fa-microchip:before { - content: "\f2db"; -} -.fa-snowflake-o:before { - content: "\f2dc"; -} -.fa-superpowers:before { - content: "\f2dd"; -} -.fa-wpexplorer:before { - content: "\f2de"; -} -.fa-meetup:before { - content: "\f2e0"; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} diff --git a/v3/css/font-awesome-4.7.0/css/font-awesome.min.css b/v3/css/font-awesome-4.7.0/css/font-awesome.min.css deleted file mode 100644 index 540440c..0000000 --- a/v3/css/font-awesome-4.7.0/css/font-awesome.min.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/v3/css/font-awesome-4.7.0/fonts/FontAwesome.otf b/v3/css/font-awesome-4.7.0/fonts/FontAwesome.otf deleted file mode 100644 index 401ec0f..0000000 Binary files a/v3/css/font-awesome-4.7.0/fonts/FontAwesome.otf and /dev/null differ diff --git a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.eot b/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.svg b/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf b/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff b/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 b/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/v3/css/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/v3/css/font-awesome-4.7.0/less/animated.less b/v3/css/font-awesome-4.7.0/less/animated.less deleted file mode 100644 index 66ad52a..0000000 --- a/v3/css/font-awesome-4.7.0/less/animated.less +++ /dev/null @@ -1,34 +0,0 @@ -// Animated Icons -// -------------------------- - -.@{fa-css-prefix}-spin { - -webkit-animation: fa-spin 2s infinite linear; - animation: fa-spin 2s infinite linear; -} - -.@{fa-css-prefix}-pulse { - -webkit-animation: fa-spin 1s infinite steps(8); - animation: fa-spin 1s infinite steps(8); -} - -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} - -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} diff --git a/v3/css/font-awesome-4.7.0/less/bordered-pulled.less b/v3/css/font-awesome-4.7.0/less/bordered-pulled.less deleted file mode 100644 index f1c8ad7..0000000 --- a/v3/css/font-awesome-4.7.0/less/bordered-pulled.less +++ /dev/null @@ -1,25 +0,0 @@ -// Bordered & Pulled -// ------------------------- - -.@{fa-css-prefix}-border { - padding: .2em .25em .15em; - border: solid .08em @fa-border-color; - border-radius: .1em; -} - -.@{fa-css-prefix}-pull-left { float: left; } -.@{fa-css-prefix}-pull-right { float: right; } - -.@{fa-css-prefix} { - &.@{fa-css-prefix}-pull-left { margin-right: .3em; } - &.@{fa-css-prefix}-pull-right { margin-left: .3em; } -} - -/* Deprecated as of 4.4.0 */ -.pull-right { float: right; } -.pull-left { float: left; } - -.@{fa-css-prefix} { - &.pull-left { margin-right: .3em; } - &.pull-right { margin-left: .3em; } -} diff --git a/v3/css/font-awesome-4.7.0/less/core.less b/v3/css/font-awesome-4.7.0/less/core.less deleted file mode 100644 index c577ac8..0000000 --- a/v3/css/font-awesome-4.7.0/less/core.less +++ /dev/null @@ -1,12 +0,0 @@ -// Base Class Definition -// ------------------------- - -.@{fa-css-prefix} { - display: inline-block; - font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration - font-size: inherit; // can't have font-size inherit on line above, so need to override - text-rendering: auto; // optimizelegibility throws things off #1094 - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -} diff --git a/v3/css/font-awesome-4.7.0/less/fixed-width.less b/v3/css/font-awesome-4.7.0/less/fixed-width.less deleted file mode 100644 index 110289f..0000000 --- a/v3/css/font-awesome-4.7.0/less/fixed-width.less +++ /dev/null @@ -1,6 +0,0 @@ -// Fixed Width Icons -// ------------------------- -.@{fa-css-prefix}-fw { - width: (18em / 14); - text-align: center; -} diff --git a/v3/css/font-awesome-4.7.0/less/font-awesome.less b/v3/css/font-awesome-4.7.0/less/font-awesome.less deleted file mode 100644 index c3677de..0000000 --- a/v3/css/font-awesome-4.7.0/less/font-awesome.less +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */ - -@import "variables.less"; -@import "mixins.less"; -@import "path.less"; -@import "core.less"; -@import "larger.less"; -@import "fixed-width.less"; -@import "list.less"; -@import "bordered-pulled.less"; -@import "animated.less"; -@import "rotated-flipped.less"; -@import "stacked.less"; -@import "icons.less"; -@import "screen-reader.less"; diff --git a/v3/css/font-awesome-4.7.0/less/icons.less b/v3/css/font-awesome-4.7.0/less/icons.less deleted file mode 100644 index 159d600..0000000 --- a/v3/css/font-awesome-4.7.0/less/icons.less +++ /dev/null @@ -1,789 +0,0 @@ -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ - -.@{fa-css-prefix}-glass:before { content: @fa-var-glass; } -.@{fa-css-prefix}-music:before { content: @fa-var-music; } -.@{fa-css-prefix}-search:before { content: @fa-var-search; } -.@{fa-css-prefix}-envelope-o:before { content: @fa-var-envelope-o; } -.@{fa-css-prefix}-heart:before { content: @fa-var-heart; } -.@{fa-css-prefix}-star:before { content: @fa-var-star; } -.@{fa-css-prefix}-star-o:before { content: @fa-var-star-o; } -.@{fa-css-prefix}-user:before { content: @fa-var-user; } -.@{fa-css-prefix}-film:before { content: @fa-var-film; } -.@{fa-css-prefix}-th-large:before { content: @fa-var-th-large; } -.@{fa-css-prefix}-th:before { content: @fa-var-th; } -.@{fa-css-prefix}-th-list:before { content: @fa-var-th-list; } -.@{fa-css-prefix}-check:before { content: @fa-var-check; } -.@{fa-css-prefix}-remove:before, -.@{fa-css-prefix}-close:before, -.@{fa-css-prefix}-times:before { content: @fa-var-times; } -.@{fa-css-prefix}-search-plus:before { content: @fa-var-search-plus; } -.@{fa-css-prefix}-search-minus:before { content: @fa-var-search-minus; } -.@{fa-css-prefix}-power-off:before { content: @fa-var-power-off; } -.@{fa-css-prefix}-signal:before { content: @fa-var-signal; } -.@{fa-css-prefix}-gear:before, -.@{fa-css-prefix}-cog:before { content: @fa-var-cog; } -.@{fa-css-prefix}-trash-o:before { content: @fa-var-trash-o; } -.@{fa-css-prefix}-home:before { content: @fa-var-home; } -.@{fa-css-prefix}-file-o:before { content: @fa-var-file-o; } -.@{fa-css-prefix}-clock-o:before { content: @fa-var-clock-o; } -.@{fa-css-prefix}-road:before { content: @fa-var-road; } -.@{fa-css-prefix}-download:before { content: @fa-var-download; } -.@{fa-css-prefix}-arrow-circle-o-down:before { content: @fa-var-arrow-circle-o-down; } -.@{fa-css-prefix}-arrow-circle-o-up:before { content: @fa-var-arrow-circle-o-up; } -.@{fa-css-prefix}-inbox:before { content: @fa-var-inbox; } -.@{fa-css-prefix}-play-circle-o:before { content: @fa-var-play-circle-o; } -.@{fa-css-prefix}-rotate-right:before, -.@{fa-css-prefix}-repeat:before { content: @fa-var-repeat; } -.@{fa-css-prefix}-refresh:before { content: @fa-var-refresh; } -.@{fa-css-prefix}-list-alt:before { content: @fa-var-list-alt; } -.@{fa-css-prefix}-lock:before { content: @fa-var-lock; } -.@{fa-css-prefix}-flag:before { content: @fa-var-flag; } -.@{fa-css-prefix}-headphones:before { content: @fa-var-headphones; } -.@{fa-css-prefix}-volume-off:before { content: @fa-var-volume-off; } -.@{fa-css-prefix}-volume-down:before { content: @fa-var-volume-down; } -.@{fa-css-prefix}-volume-up:before { content: @fa-var-volume-up; } -.@{fa-css-prefix}-qrcode:before { content: @fa-var-qrcode; } -.@{fa-css-prefix}-barcode:before { content: @fa-var-barcode; } -.@{fa-css-prefix}-tag:before { content: @fa-var-tag; } -.@{fa-css-prefix}-tags:before { content: @fa-var-tags; } -.@{fa-css-prefix}-book:before { content: @fa-var-book; } -.@{fa-css-prefix}-bookmark:before { content: @fa-var-bookmark; } -.@{fa-css-prefix}-print:before { content: @fa-var-print; } -.@{fa-css-prefix}-camera:before { content: @fa-var-camera; } -.@{fa-css-prefix}-font:before { content: @fa-var-font; } -.@{fa-css-prefix}-bold:before { content: @fa-var-bold; } -.@{fa-css-prefix}-italic:before { content: @fa-var-italic; } -.@{fa-css-prefix}-text-height:before { content: @fa-var-text-height; } -.@{fa-css-prefix}-text-width:before { content: @fa-var-text-width; } -.@{fa-css-prefix}-align-left:before { content: @fa-var-align-left; } -.@{fa-css-prefix}-align-center:before { content: @fa-var-align-center; } -.@{fa-css-prefix}-align-right:before { content: @fa-var-align-right; } -.@{fa-css-prefix}-align-justify:before { content: @fa-var-align-justify; } -.@{fa-css-prefix}-list:before { content: @fa-var-list; } -.@{fa-css-prefix}-dedent:before, -.@{fa-css-prefix}-outdent:before { content: @fa-var-outdent; } -.@{fa-css-prefix}-indent:before { content: @fa-var-indent; } -.@{fa-css-prefix}-video-camera:before { content: @fa-var-video-camera; } -.@{fa-css-prefix}-photo:before, -.@{fa-css-prefix}-image:before, -.@{fa-css-prefix}-picture-o:before { content: @fa-var-picture-o; } -.@{fa-css-prefix}-pencil:before { content: @fa-var-pencil; } -.@{fa-css-prefix}-map-marker:before { content: @fa-var-map-marker; } -.@{fa-css-prefix}-adjust:before { content: @fa-var-adjust; } -.@{fa-css-prefix}-tint:before { content: @fa-var-tint; } -.@{fa-css-prefix}-edit:before, -.@{fa-css-prefix}-pencil-square-o:before { content: @fa-var-pencil-square-o; } -.@{fa-css-prefix}-share-square-o:before { content: @fa-var-share-square-o; } -.@{fa-css-prefix}-check-square-o:before { content: @fa-var-check-square-o; } -.@{fa-css-prefix}-arrows:before { content: @fa-var-arrows; } -.@{fa-css-prefix}-step-backward:before { content: @fa-var-step-backward; } -.@{fa-css-prefix}-fast-backward:before { content: @fa-var-fast-backward; } -.@{fa-css-prefix}-backward:before { content: @fa-var-backward; } -.@{fa-css-prefix}-play:before { content: @fa-var-play; } -.@{fa-css-prefix}-pause:before { content: @fa-var-pause; } -.@{fa-css-prefix}-stop:before { content: @fa-var-stop; } -.@{fa-css-prefix}-forward:before { content: @fa-var-forward; } -.@{fa-css-prefix}-fast-forward:before { content: @fa-var-fast-forward; } -.@{fa-css-prefix}-step-forward:before { content: @fa-var-step-forward; } -.@{fa-css-prefix}-eject:before { content: @fa-var-eject; } -.@{fa-css-prefix}-chevron-left:before { content: @fa-var-chevron-left; } -.@{fa-css-prefix}-chevron-right:before { content: @fa-var-chevron-right; } -.@{fa-css-prefix}-plus-circle:before { content: @fa-var-plus-circle; } -.@{fa-css-prefix}-minus-circle:before { content: @fa-var-minus-circle; } -.@{fa-css-prefix}-times-circle:before { content: @fa-var-times-circle; } -.@{fa-css-prefix}-check-circle:before { content: @fa-var-check-circle; } -.@{fa-css-prefix}-question-circle:before { content: @fa-var-question-circle; } -.@{fa-css-prefix}-info-circle:before { content: @fa-var-info-circle; } -.@{fa-css-prefix}-crosshairs:before { content: @fa-var-crosshairs; } -.@{fa-css-prefix}-times-circle-o:before { content: @fa-var-times-circle-o; } -.@{fa-css-prefix}-check-circle-o:before { content: @fa-var-check-circle-o; } -.@{fa-css-prefix}-ban:before { content: @fa-var-ban; } -.@{fa-css-prefix}-arrow-left:before { content: @fa-var-arrow-left; } -.@{fa-css-prefix}-arrow-right:before { content: @fa-var-arrow-right; } -.@{fa-css-prefix}-arrow-up:before { content: @fa-var-arrow-up; } -.@{fa-css-prefix}-arrow-down:before { content: @fa-var-arrow-down; } -.@{fa-css-prefix}-mail-forward:before, -.@{fa-css-prefix}-share:before { content: @fa-var-share; } -.@{fa-css-prefix}-expand:before { content: @fa-var-expand; } -.@{fa-css-prefix}-compress:before { content: @fa-var-compress; } -.@{fa-css-prefix}-plus:before { content: @fa-var-plus; } -.@{fa-css-prefix}-minus:before { content: @fa-var-minus; } -.@{fa-css-prefix}-asterisk:before { content: @fa-var-asterisk; } -.@{fa-css-prefix}-exclamation-circle:before { content: @fa-var-exclamation-circle; } -.@{fa-css-prefix}-gift:before { content: @fa-var-gift; } -.@{fa-css-prefix}-leaf:before { content: @fa-var-leaf; } -.@{fa-css-prefix}-fire:before { content: @fa-var-fire; } -.@{fa-css-prefix}-eye:before { content: @fa-var-eye; } -.@{fa-css-prefix}-eye-slash:before { content: @fa-var-eye-slash; } -.@{fa-css-prefix}-warning:before, -.@{fa-css-prefix}-exclamation-triangle:before { content: @fa-var-exclamation-triangle; } -.@{fa-css-prefix}-plane:before { content: @fa-var-plane; } -.@{fa-css-prefix}-calendar:before { content: @fa-var-calendar; } -.@{fa-css-prefix}-random:before { content: @fa-var-random; } -.@{fa-css-prefix}-comment:before { content: @fa-var-comment; } -.@{fa-css-prefix}-magnet:before { content: @fa-var-magnet; } -.@{fa-css-prefix}-chevron-up:before { content: @fa-var-chevron-up; } -.@{fa-css-prefix}-chevron-down:before { content: @fa-var-chevron-down; } -.@{fa-css-prefix}-retweet:before { content: @fa-var-retweet; } -.@{fa-css-prefix}-shopping-cart:before { content: @fa-var-shopping-cart; } -.@{fa-css-prefix}-folder:before { content: @fa-var-folder; } -.@{fa-css-prefix}-folder-open:before { content: @fa-var-folder-open; } -.@{fa-css-prefix}-arrows-v:before { content: @fa-var-arrows-v; } -.@{fa-css-prefix}-arrows-h:before { content: @fa-var-arrows-h; } -.@{fa-css-prefix}-bar-chart-o:before, -.@{fa-css-prefix}-bar-chart:before { content: @fa-var-bar-chart; } -.@{fa-css-prefix}-twitter-square:before { content: @fa-var-twitter-square; } -.@{fa-css-prefix}-facebook-square:before { content: @fa-var-facebook-square; } -.@{fa-css-prefix}-camera-retro:before { content: @fa-var-camera-retro; } -.@{fa-css-prefix}-key:before { content: @fa-var-key; } -.@{fa-css-prefix}-gears:before, -.@{fa-css-prefix}-cogs:before { content: @fa-var-cogs; } -.@{fa-css-prefix}-comments:before { content: @fa-var-comments; } -.@{fa-css-prefix}-thumbs-o-up:before { content: @fa-var-thumbs-o-up; } -.@{fa-css-prefix}-thumbs-o-down:before { content: @fa-var-thumbs-o-down; } -.@{fa-css-prefix}-star-half:before { content: @fa-var-star-half; } -.@{fa-css-prefix}-heart-o:before { content: @fa-var-heart-o; } -.@{fa-css-prefix}-sign-out:before { content: @fa-var-sign-out; } -.@{fa-css-prefix}-linkedin-square:before { content: @fa-var-linkedin-square; } -.@{fa-css-prefix}-thumb-tack:before { content: @fa-var-thumb-tack; } -.@{fa-css-prefix}-external-link:before { content: @fa-var-external-link; } -.@{fa-css-prefix}-sign-in:before { content: @fa-var-sign-in; } -.@{fa-css-prefix}-trophy:before { content: @fa-var-trophy; } -.@{fa-css-prefix}-github-square:before { content: @fa-var-github-square; } -.@{fa-css-prefix}-upload:before { content: @fa-var-upload; } -.@{fa-css-prefix}-lemon-o:before { content: @fa-var-lemon-o; } -.@{fa-css-prefix}-phone:before { content: @fa-var-phone; } -.@{fa-css-prefix}-square-o:before { content: @fa-var-square-o; } -.@{fa-css-prefix}-bookmark-o:before { content: @fa-var-bookmark-o; } -.@{fa-css-prefix}-phone-square:before { content: @fa-var-phone-square; } -.@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; } -.@{fa-css-prefix}-facebook-f:before, -.@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; } -.@{fa-css-prefix}-github:before { content: @fa-var-github; } -.@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; } -.@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; } -.@{fa-css-prefix}-feed:before, -.@{fa-css-prefix}-rss:before { content: @fa-var-rss; } -.@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; } -.@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; } -.@{fa-css-prefix}-bell:before { content: @fa-var-bell; } -.@{fa-css-prefix}-certificate:before { content: @fa-var-certificate; } -.@{fa-css-prefix}-hand-o-right:before { content: @fa-var-hand-o-right; } -.@{fa-css-prefix}-hand-o-left:before { content: @fa-var-hand-o-left; } -.@{fa-css-prefix}-hand-o-up:before { content: @fa-var-hand-o-up; } -.@{fa-css-prefix}-hand-o-down:before { content: @fa-var-hand-o-down; } -.@{fa-css-prefix}-arrow-circle-left:before { content: @fa-var-arrow-circle-left; } -.@{fa-css-prefix}-arrow-circle-right:before { content: @fa-var-arrow-circle-right; } -.@{fa-css-prefix}-arrow-circle-up:before { content: @fa-var-arrow-circle-up; } -.@{fa-css-prefix}-arrow-circle-down:before { content: @fa-var-arrow-circle-down; } -.@{fa-css-prefix}-globe:before { content: @fa-var-globe; } -.@{fa-css-prefix}-wrench:before { content: @fa-var-wrench; } -.@{fa-css-prefix}-tasks:before { content: @fa-var-tasks; } -.@{fa-css-prefix}-filter:before { content: @fa-var-filter; } -.@{fa-css-prefix}-briefcase:before { content: @fa-var-briefcase; } -.@{fa-css-prefix}-arrows-alt:before { content: @fa-var-arrows-alt; } -.@{fa-css-prefix}-group:before, -.@{fa-css-prefix}-users:before { content: @fa-var-users; } -.@{fa-css-prefix}-chain:before, -.@{fa-css-prefix}-link:before { content: @fa-var-link; } -.@{fa-css-prefix}-cloud:before { content: @fa-var-cloud; } -.@{fa-css-prefix}-flask:before { content: @fa-var-flask; } -.@{fa-css-prefix}-cut:before, -.@{fa-css-prefix}-scissors:before { content: @fa-var-scissors; } -.@{fa-css-prefix}-copy:before, -.@{fa-css-prefix}-files-o:before { content: @fa-var-files-o; } -.@{fa-css-prefix}-paperclip:before { content: @fa-var-paperclip; } -.@{fa-css-prefix}-save:before, -.@{fa-css-prefix}-floppy-o:before { content: @fa-var-floppy-o; } -.@{fa-css-prefix}-square:before { content: @fa-var-square; } -.@{fa-css-prefix}-navicon:before, -.@{fa-css-prefix}-reorder:before, -.@{fa-css-prefix}-bars:before { content: @fa-var-bars; } -.@{fa-css-prefix}-list-ul:before { content: @fa-var-list-ul; } -.@{fa-css-prefix}-list-ol:before { content: @fa-var-list-ol; } -.@{fa-css-prefix}-strikethrough:before { content: @fa-var-strikethrough; } -.@{fa-css-prefix}-underline:before { content: @fa-var-underline; } -.@{fa-css-prefix}-table:before { content: @fa-var-table; } -.@{fa-css-prefix}-magic:before { content: @fa-var-magic; } -.@{fa-css-prefix}-truck:before { content: @fa-var-truck; } -.@{fa-css-prefix}-pinterest:before { content: @fa-var-pinterest; } -.@{fa-css-prefix}-pinterest-square:before { content: @fa-var-pinterest-square; } -.@{fa-css-prefix}-google-plus-square:before { content: @fa-var-google-plus-square; } -.@{fa-css-prefix}-google-plus:before { content: @fa-var-google-plus; } -.@{fa-css-prefix}-money:before { content: @fa-var-money; } -.@{fa-css-prefix}-caret-down:before { content: @fa-var-caret-down; } -.@{fa-css-prefix}-caret-up:before { content: @fa-var-caret-up; } -.@{fa-css-prefix}-caret-left:before { content: @fa-var-caret-left; } -.@{fa-css-prefix}-caret-right:before { content: @fa-var-caret-right; } -.@{fa-css-prefix}-columns:before { content: @fa-var-columns; } -.@{fa-css-prefix}-unsorted:before, -.@{fa-css-prefix}-sort:before { content: @fa-var-sort; } -.@{fa-css-prefix}-sort-down:before, -.@{fa-css-prefix}-sort-desc:before { content: @fa-var-sort-desc; } -.@{fa-css-prefix}-sort-up:before, -.@{fa-css-prefix}-sort-asc:before { content: @fa-var-sort-asc; } -.@{fa-css-prefix}-envelope:before { content: @fa-var-envelope; } -.@{fa-css-prefix}-linkedin:before { content: @fa-var-linkedin; } -.@{fa-css-prefix}-rotate-left:before, -.@{fa-css-prefix}-undo:before { content: @fa-var-undo; } -.@{fa-css-prefix}-legal:before, -.@{fa-css-prefix}-gavel:before { content: @fa-var-gavel; } -.@{fa-css-prefix}-dashboard:before, -.@{fa-css-prefix}-tachometer:before { content: @fa-var-tachometer; } -.@{fa-css-prefix}-comment-o:before { content: @fa-var-comment-o; } -.@{fa-css-prefix}-comments-o:before { content: @fa-var-comments-o; } -.@{fa-css-prefix}-flash:before, -.@{fa-css-prefix}-bolt:before { content: @fa-var-bolt; } -.@{fa-css-prefix}-sitemap:before { content: @fa-var-sitemap; } -.@{fa-css-prefix}-umbrella:before { content: @fa-var-umbrella; } -.@{fa-css-prefix}-paste:before, -.@{fa-css-prefix}-clipboard:before { content: @fa-var-clipboard; } -.@{fa-css-prefix}-lightbulb-o:before { content: @fa-var-lightbulb-o; } -.@{fa-css-prefix}-exchange:before { content: @fa-var-exchange; } -.@{fa-css-prefix}-cloud-download:before { content: @fa-var-cloud-download; } -.@{fa-css-prefix}-cloud-upload:before { content: @fa-var-cloud-upload; } -.@{fa-css-prefix}-user-md:before { content: @fa-var-user-md; } -.@{fa-css-prefix}-stethoscope:before { content: @fa-var-stethoscope; } -.@{fa-css-prefix}-suitcase:before { content: @fa-var-suitcase; } -.@{fa-css-prefix}-bell-o:before { content: @fa-var-bell-o; } -.@{fa-css-prefix}-coffee:before { content: @fa-var-coffee; } -.@{fa-css-prefix}-cutlery:before { content: @fa-var-cutlery; } -.@{fa-css-prefix}-file-text-o:before { content: @fa-var-file-text-o; } -.@{fa-css-prefix}-building-o:before { content: @fa-var-building-o; } -.@{fa-css-prefix}-hospital-o:before { content: @fa-var-hospital-o; } -.@{fa-css-prefix}-ambulance:before { content: @fa-var-ambulance; } -.@{fa-css-prefix}-medkit:before { content: @fa-var-medkit; } -.@{fa-css-prefix}-fighter-jet:before { content: @fa-var-fighter-jet; } -.@{fa-css-prefix}-beer:before { content: @fa-var-beer; } -.@{fa-css-prefix}-h-square:before { content: @fa-var-h-square; } -.@{fa-css-prefix}-plus-square:before { content: @fa-var-plus-square; } -.@{fa-css-prefix}-angle-double-left:before { content: @fa-var-angle-double-left; } -.@{fa-css-prefix}-angle-double-right:before { content: @fa-var-angle-double-right; } -.@{fa-css-prefix}-angle-double-up:before { content: @fa-var-angle-double-up; } -.@{fa-css-prefix}-angle-double-down:before { content: @fa-var-angle-double-down; } -.@{fa-css-prefix}-angle-left:before { content: @fa-var-angle-left; } -.@{fa-css-prefix}-angle-right:before { content: @fa-var-angle-right; } -.@{fa-css-prefix}-angle-up:before { content: @fa-var-angle-up; } -.@{fa-css-prefix}-angle-down:before { content: @fa-var-angle-down; } -.@{fa-css-prefix}-desktop:before { content: @fa-var-desktop; } -.@{fa-css-prefix}-laptop:before { content: @fa-var-laptop; } -.@{fa-css-prefix}-tablet:before { content: @fa-var-tablet; } -.@{fa-css-prefix}-mobile-phone:before, -.@{fa-css-prefix}-mobile:before { content: @fa-var-mobile; } -.@{fa-css-prefix}-circle-o:before { content: @fa-var-circle-o; } -.@{fa-css-prefix}-quote-left:before { content: @fa-var-quote-left; } -.@{fa-css-prefix}-quote-right:before { content: @fa-var-quote-right; } -.@{fa-css-prefix}-spinner:before { content: @fa-var-spinner; } -.@{fa-css-prefix}-circle:before { content: @fa-var-circle; } -.@{fa-css-prefix}-mail-reply:before, -.@{fa-css-prefix}-reply:before { content: @fa-var-reply; } -.@{fa-css-prefix}-github-alt:before { content: @fa-var-github-alt; } -.@{fa-css-prefix}-folder-o:before { content: @fa-var-folder-o; } -.@{fa-css-prefix}-folder-open-o:before { content: @fa-var-folder-open-o; } -.@{fa-css-prefix}-smile-o:before { content: @fa-var-smile-o; } -.@{fa-css-prefix}-frown-o:before { content: @fa-var-frown-o; } -.@{fa-css-prefix}-meh-o:before { content: @fa-var-meh-o; } -.@{fa-css-prefix}-gamepad:before { content: @fa-var-gamepad; } -.@{fa-css-prefix}-keyboard-o:before { content: @fa-var-keyboard-o; } -.@{fa-css-prefix}-flag-o:before { content: @fa-var-flag-o; } -.@{fa-css-prefix}-flag-checkered:before { content: @fa-var-flag-checkered; } -.@{fa-css-prefix}-terminal:before { content: @fa-var-terminal; } -.@{fa-css-prefix}-code:before { content: @fa-var-code; } -.@{fa-css-prefix}-mail-reply-all:before, -.@{fa-css-prefix}-reply-all:before { content: @fa-var-reply-all; } -.@{fa-css-prefix}-star-half-empty:before, -.@{fa-css-prefix}-star-half-full:before, -.@{fa-css-prefix}-star-half-o:before { content: @fa-var-star-half-o; } -.@{fa-css-prefix}-location-arrow:before { content: @fa-var-location-arrow; } -.@{fa-css-prefix}-crop:before { content: @fa-var-crop; } -.@{fa-css-prefix}-code-fork:before { content: @fa-var-code-fork; } -.@{fa-css-prefix}-unlink:before, -.@{fa-css-prefix}-chain-broken:before { content: @fa-var-chain-broken; } -.@{fa-css-prefix}-question:before { content: @fa-var-question; } -.@{fa-css-prefix}-info:before { content: @fa-var-info; } -.@{fa-css-prefix}-exclamation:before { content: @fa-var-exclamation; } -.@{fa-css-prefix}-superscript:before { content: @fa-var-superscript; } -.@{fa-css-prefix}-subscript:before { content: @fa-var-subscript; } -.@{fa-css-prefix}-eraser:before { content: @fa-var-eraser; } -.@{fa-css-prefix}-puzzle-piece:before { content: @fa-var-puzzle-piece; } -.@{fa-css-prefix}-microphone:before { content: @fa-var-microphone; } -.@{fa-css-prefix}-microphone-slash:before { content: @fa-var-microphone-slash; } -.@{fa-css-prefix}-shield:before { content: @fa-var-shield; } -.@{fa-css-prefix}-calendar-o:before { content: @fa-var-calendar-o; } -.@{fa-css-prefix}-fire-extinguisher:before { content: @fa-var-fire-extinguisher; } -.@{fa-css-prefix}-rocket:before { content: @fa-var-rocket; } -.@{fa-css-prefix}-maxcdn:before { content: @fa-var-maxcdn; } -.@{fa-css-prefix}-chevron-circle-left:before { content: @fa-var-chevron-circle-left; } -.@{fa-css-prefix}-chevron-circle-right:before { content: @fa-var-chevron-circle-right; } -.@{fa-css-prefix}-chevron-circle-up:before { content: @fa-var-chevron-circle-up; } -.@{fa-css-prefix}-chevron-circle-down:before { content: @fa-var-chevron-circle-down; } -.@{fa-css-prefix}-html5:before { content: @fa-var-html5; } -.@{fa-css-prefix}-css3:before { content: @fa-var-css3; } -.@{fa-css-prefix}-anchor:before { content: @fa-var-anchor; } -.@{fa-css-prefix}-unlock-alt:before { content: @fa-var-unlock-alt; } -.@{fa-css-prefix}-bullseye:before { content: @fa-var-bullseye; } -.@{fa-css-prefix}-ellipsis-h:before { content: @fa-var-ellipsis-h; } -.@{fa-css-prefix}-ellipsis-v:before { content: @fa-var-ellipsis-v; } -.@{fa-css-prefix}-rss-square:before { content: @fa-var-rss-square; } -.@{fa-css-prefix}-play-circle:before { content: @fa-var-play-circle; } -.@{fa-css-prefix}-ticket:before { content: @fa-var-ticket; } -.@{fa-css-prefix}-minus-square:before { content: @fa-var-minus-square; } -.@{fa-css-prefix}-minus-square-o:before { content: @fa-var-minus-square-o; } -.@{fa-css-prefix}-level-up:before { content: @fa-var-level-up; } -.@{fa-css-prefix}-level-down:before { content: @fa-var-level-down; } -.@{fa-css-prefix}-check-square:before { content: @fa-var-check-square; } -.@{fa-css-prefix}-pencil-square:before { content: @fa-var-pencil-square; } -.@{fa-css-prefix}-external-link-square:before { content: @fa-var-external-link-square; } -.@{fa-css-prefix}-share-square:before { content: @fa-var-share-square; } -.@{fa-css-prefix}-compass:before { content: @fa-var-compass; } -.@{fa-css-prefix}-toggle-down:before, -.@{fa-css-prefix}-caret-square-o-down:before { content: @fa-var-caret-square-o-down; } -.@{fa-css-prefix}-toggle-up:before, -.@{fa-css-prefix}-caret-square-o-up:before { content: @fa-var-caret-square-o-up; } -.@{fa-css-prefix}-toggle-right:before, -.@{fa-css-prefix}-caret-square-o-right:before { content: @fa-var-caret-square-o-right; } -.@{fa-css-prefix}-euro:before, -.@{fa-css-prefix}-eur:before { content: @fa-var-eur; } -.@{fa-css-prefix}-gbp:before { content: @fa-var-gbp; } -.@{fa-css-prefix}-dollar:before, -.@{fa-css-prefix}-usd:before { content: @fa-var-usd; } -.@{fa-css-prefix}-rupee:before, -.@{fa-css-prefix}-inr:before { content: @fa-var-inr; } -.@{fa-css-prefix}-cny:before, -.@{fa-css-prefix}-rmb:before, -.@{fa-css-prefix}-yen:before, -.@{fa-css-prefix}-jpy:before { content: @fa-var-jpy; } -.@{fa-css-prefix}-ruble:before, -.@{fa-css-prefix}-rouble:before, -.@{fa-css-prefix}-rub:before { content: @fa-var-rub; } -.@{fa-css-prefix}-won:before, -.@{fa-css-prefix}-krw:before { content: @fa-var-krw; } -.@{fa-css-prefix}-bitcoin:before, -.@{fa-css-prefix}-btc:before { content: @fa-var-btc; } -.@{fa-css-prefix}-file:before { content: @fa-var-file; } -.@{fa-css-prefix}-file-text:before { content: @fa-var-file-text; } -.@{fa-css-prefix}-sort-alpha-asc:before { content: @fa-var-sort-alpha-asc; } -.@{fa-css-prefix}-sort-alpha-desc:before { content: @fa-var-sort-alpha-desc; } -.@{fa-css-prefix}-sort-amount-asc:before { content: @fa-var-sort-amount-asc; } -.@{fa-css-prefix}-sort-amount-desc:before { content: @fa-var-sort-amount-desc; } -.@{fa-css-prefix}-sort-numeric-asc:before { content: @fa-var-sort-numeric-asc; } -.@{fa-css-prefix}-sort-numeric-desc:before { content: @fa-var-sort-numeric-desc; } -.@{fa-css-prefix}-thumbs-up:before { content: @fa-var-thumbs-up; } -.@{fa-css-prefix}-thumbs-down:before { content: @fa-var-thumbs-down; } -.@{fa-css-prefix}-youtube-square:before { content: @fa-var-youtube-square; } -.@{fa-css-prefix}-youtube:before { content: @fa-var-youtube; } -.@{fa-css-prefix}-xing:before { content: @fa-var-xing; } -.@{fa-css-prefix}-xing-square:before { content: @fa-var-xing-square; } -.@{fa-css-prefix}-youtube-play:before { content: @fa-var-youtube-play; } -.@{fa-css-prefix}-dropbox:before { content: @fa-var-dropbox; } -.@{fa-css-prefix}-stack-overflow:before { content: @fa-var-stack-overflow; } -.@{fa-css-prefix}-instagram:before { content: @fa-var-instagram; } -.@{fa-css-prefix}-flickr:before { content: @fa-var-flickr; } -.@{fa-css-prefix}-adn:before { content: @fa-var-adn; } -.@{fa-css-prefix}-bitbucket:before { content: @fa-var-bitbucket; } -.@{fa-css-prefix}-bitbucket-square:before { content: @fa-var-bitbucket-square; } -.@{fa-css-prefix}-tumblr:before { content: @fa-var-tumblr; } -.@{fa-css-prefix}-tumblr-square:before { content: @fa-var-tumblr-square; } -.@{fa-css-prefix}-long-arrow-down:before { content: @fa-var-long-arrow-down; } -.@{fa-css-prefix}-long-arrow-up:before { content: @fa-var-long-arrow-up; } -.@{fa-css-prefix}-long-arrow-left:before { content: @fa-var-long-arrow-left; } -.@{fa-css-prefix}-long-arrow-right:before { content: @fa-var-long-arrow-right; } -.@{fa-css-prefix}-apple:before { content: @fa-var-apple; } -.@{fa-css-prefix}-windows:before { content: @fa-var-windows; } -.@{fa-css-prefix}-android:before { content: @fa-var-android; } -.@{fa-css-prefix}-linux:before { content: @fa-var-linux; } -.@{fa-css-prefix}-dribbble:before { content: @fa-var-dribbble; } -.@{fa-css-prefix}-skype:before { content: @fa-var-skype; } -.@{fa-css-prefix}-foursquare:before { content: @fa-var-foursquare; } -.@{fa-css-prefix}-trello:before { content: @fa-var-trello; } -.@{fa-css-prefix}-female:before { content: @fa-var-female; } -.@{fa-css-prefix}-male:before { content: @fa-var-male; } -.@{fa-css-prefix}-gittip:before, -.@{fa-css-prefix}-gratipay:before { content: @fa-var-gratipay; } -.@{fa-css-prefix}-sun-o:before { content: @fa-var-sun-o; } -.@{fa-css-prefix}-moon-o:before { content: @fa-var-moon-o; } -.@{fa-css-prefix}-archive:before { content: @fa-var-archive; } -.@{fa-css-prefix}-bug:before { content: @fa-var-bug; } -.@{fa-css-prefix}-vk:before { content: @fa-var-vk; } -.@{fa-css-prefix}-weibo:before { content: @fa-var-weibo; } -.@{fa-css-prefix}-renren:before { content: @fa-var-renren; } -.@{fa-css-prefix}-pagelines:before { content: @fa-var-pagelines; } -.@{fa-css-prefix}-stack-exchange:before { content: @fa-var-stack-exchange; } -.@{fa-css-prefix}-arrow-circle-o-right:before { content: @fa-var-arrow-circle-o-right; } -.@{fa-css-prefix}-arrow-circle-o-left:before { content: @fa-var-arrow-circle-o-left; } -.@{fa-css-prefix}-toggle-left:before, -.@{fa-css-prefix}-caret-square-o-left:before { content: @fa-var-caret-square-o-left; } -.@{fa-css-prefix}-dot-circle-o:before { content: @fa-var-dot-circle-o; } -.@{fa-css-prefix}-wheelchair:before { content: @fa-var-wheelchair; } -.@{fa-css-prefix}-vimeo-square:before { content: @fa-var-vimeo-square; } -.@{fa-css-prefix}-turkish-lira:before, -.@{fa-css-prefix}-try:before { content: @fa-var-try; } -.@{fa-css-prefix}-plus-square-o:before { content: @fa-var-plus-square-o; } -.@{fa-css-prefix}-space-shuttle:before { content: @fa-var-space-shuttle; } -.@{fa-css-prefix}-slack:before { content: @fa-var-slack; } -.@{fa-css-prefix}-envelope-square:before { content: @fa-var-envelope-square; } -.@{fa-css-prefix}-wordpress:before { content: @fa-var-wordpress; } -.@{fa-css-prefix}-openid:before { content: @fa-var-openid; } -.@{fa-css-prefix}-institution:before, -.@{fa-css-prefix}-bank:before, -.@{fa-css-prefix}-university:before { content: @fa-var-university; } -.@{fa-css-prefix}-mortar-board:before, -.@{fa-css-prefix}-graduation-cap:before { content: @fa-var-graduation-cap; } -.@{fa-css-prefix}-yahoo:before { content: @fa-var-yahoo; } -.@{fa-css-prefix}-google:before { content: @fa-var-google; } -.@{fa-css-prefix}-reddit:before { content: @fa-var-reddit; } -.@{fa-css-prefix}-reddit-square:before { content: @fa-var-reddit-square; } -.@{fa-css-prefix}-stumbleupon-circle:before { content: @fa-var-stumbleupon-circle; } -.@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; } -.@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; } -.@{fa-css-prefix}-digg:before { content: @fa-var-digg; } -.@{fa-css-prefix}-pied-piper-pp:before { content: @fa-var-pied-piper-pp; } -.@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; } -.@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; } -.@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; } -.@{fa-css-prefix}-language:before { content: @fa-var-language; } -.@{fa-css-prefix}-fax:before { content: @fa-var-fax; } -.@{fa-css-prefix}-building:before { content: @fa-var-building; } -.@{fa-css-prefix}-child:before { content: @fa-var-child; } -.@{fa-css-prefix}-paw:before { content: @fa-var-paw; } -.@{fa-css-prefix}-spoon:before { content: @fa-var-spoon; } -.@{fa-css-prefix}-cube:before { content: @fa-var-cube; } -.@{fa-css-prefix}-cubes:before { content: @fa-var-cubes; } -.@{fa-css-prefix}-behance:before { content: @fa-var-behance; } -.@{fa-css-prefix}-behance-square:before { content: @fa-var-behance-square; } -.@{fa-css-prefix}-steam:before { content: @fa-var-steam; } -.@{fa-css-prefix}-steam-square:before { content: @fa-var-steam-square; } -.@{fa-css-prefix}-recycle:before { content: @fa-var-recycle; } -.@{fa-css-prefix}-automobile:before, -.@{fa-css-prefix}-car:before { content: @fa-var-car; } -.@{fa-css-prefix}-cab:before, -.@{fa-css-prefix}-taxi:before { content: @fa-var-taxi; } -.@{fa-css-prefix}-tree:before { content: @fa-var-tree; } -.@{fa-css-prefix}-spotify:before { content: @fa-var-spotify; } -.@{fa-css-prefix}-deviantart:before { content: @fa-var-deviantart; } -.@{fa-css-prefix}-soundcloud:before { content: @fa-var-soundcloud; } -.@{fa-css-prefix}-database:before { content: @fa-var-database; } -.@{fa-css-prefix}-file-pdf-o:before { content: @fa-var-file-pdf-o; } -.@{fa-css-prefix}-file-word-o:before { content: @fa-var-file-word-o; } -.@{fa-css-prefix}-file-excel-o:before { content: @fa-var-file-excel-o; } -.@{fa-css-prefix}-file-powerpoint-o:before { content: @fa-var-file-powerpoint-o; } -.@{fa-css-prefix}-file-photo-o:before, -.@{fa-css-prefix}-file-picture-o:before, -.@{fa-css-prefix}-file-image-o:before { content: @fa-var-file-image-o; } -.@{fa-css-prefix}-file-zip-o:before, -.@{fa-css-prefix}-file-archive-o:before { content: @fa-var-file-archive-o; } -.@{fa-css-prefix}-file-sound-o:before, -.@{fa-css-prefix}-file-audio-o:before { content: @fa-var-file-audio-o; } -.@{fa-css-prefix}-file-movie-o:before, -.@{fa-css-prefix}-file-video-o:before { content: @fa-var-file-video-o; } -.@{fa-css-prefix}-file-code-o:before { content: @fa-var-file-code-o; } -.@{fa-css-prefix}-vine:before { content: @fa-var-vine; } -.@{fa-css-prefix}-codepen:before { content: @fa-var-codepen; } -.@{fa-css-prefix}-jsfiddle:before { content: @fa-var-jsfiddle; } -.@{fa-css-prefix}-life-bouy:before, -.@{fa-css-prefix}-life-buoy:before, -.@{fa-css-prefix}-life-saver:before, -.@{fa-css-prefix}-support:before, -.@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; } -.@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; } -.@{fa-css-prefix}-ra:before, -.@{fa-css-prefix}-resistance:before, -.@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; } -.@{fa-css-prefix}-ge:before, -.@{fa-css-prefix}-empire:before { content: @fa-var-empire; } -.@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; } -.@{fa-css-prefix}-git:before { content: @fa-var-git; } -.@{fa-css-prefix}-y-combinator-square:before, -.@{fa-css-prefix}-yc-square:before, -.@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; } -.@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; } -.@{fa-css-prefix}-qq:before { content: @fa-var-qq; } -.@{fa-css-prefix}-wechat:before, -.@{fa-css-prefix}-weixin:before { content: @fa-var-weixin; } -.@{fa-css-prefix}-send:before, -.@{fa-css-prefix}-paper-plane:before { content: @fa-var-paper-plane; } -.@{fa-css-prefix}-send-o:before, -.@{fa-css-prefix}-paper-plane-o:before { content: @fa-var-paper-plane-o; } -.@{fa-css-prefix}-history:before { content: @fa-var-history; } -.@{fa-css-prefix}-circle-thin:before { content: @fa-var-circle-thin; } -.@{fa-css-prefix}-header:before { content: @fa-var-header; } -.@{fa-css-prefix}-paragraph:before { content: @fa-var-paragraph; } -.@{fa-css-prefix}-sliders:before { content: @fa-var-sliders; } -.@{fa-css-prefix}-share-alt:before { content: @fa-var-share-alt; } -.@{fa-css-prefix}-share-alt-square:before { content: @fa-var-share-alt-square; } -.@{fa-css-prefix}-bomb:before { content: @fa-var-bomb; } -.@{fa-css-prefix}-soccer-ball-o:before, -.@{fa-css-prefix}-futbol-o:before { content: @fa-var-futbol-o; } -.@{fa-css-prefix}-tty:before { content: @fa-var-tty; } -.@{fa-css-prefix}-binoculars:before { content: @fa-var-binoculars; } -.@{fa-css-prefix}-plug:before { content: @fa-var-plug; } -.@{fa-css-prefix}-slideshare:before { content: @fa-var-slideshare; } -.@{fa-css-prefix}-twitch:before { content: @fa-var-twitch; } -.@{fa-css-prefix}-yelp:before { content: @fa-var-yelp; } -.@{fa-css-prefix}-newspaper-o:before { content: @fa-var-newspaper-o; } -.@{fa-css-prefix}-wifi:before { content: @fa-var-wifi; } -.@{fa-css-prefix}-calculator:before { content: @fa-var-calculator; } -.@{fa-css-prefix}-paypal:before { content: @fa-var-paypal; } -.@{fa-css-prefix}-google-wallet:before { content: @fa-var-google-wallet; } -.@{fa-css-prefix}-cc-visa:before { content: @fa-var-cc-visa; } -.@{fa-css-prefix}-cc-mastercard:before { content: @fa-var-cc-mastercard; } -.@{fa-css-prefix}-cc-discover:before { content: @fa-var-cc-discover; } -.@{fa-css-prefix}-cc-amex:before { content: @fa-var-cc-amex; } -.@{fa-css-prefix}-cc-paypal:before { content: @fa-var-cc-paypal; } -.@{fa-css-prefix}-cc-stripe:before { content: @fa-var-cc-stripe; } -.@{fa-css-prefix}-bell-slash:before { content: @fa-var-bell-slash; } -.@{fa-css-prefix}-bell-slash-o:before { content: @fa-var-bell-slash-o; } -.@{fa-css-prefix}-trash:before { content: @fa-var-trash; } -.@{fa-css-prefix}-copyright:before { content: @fa-var-copyright; } -.@{fa-css-prefix}-at:before { content: @fa-var-at; } -.@{fa-css-prefix}-eyedropper:before { content: @fa-var-eyedropper; } -.@{fa-css-prefix}-paint-brush:before { content: @fa-var-paint-brush; } -.@{fa-css-prefix}-birthday-cake:before { content: @fa-var-birthday-cake; } -.@{fa-css-prefix}-area-chart:before { content: @fa-var-area-chart; } -.@{fa-css-prefix}-pie-chart:before { content: @fa-var-pie-chart; } -.@{fa-css-prefix}-line-chart:before { content: @fa-var-line-chart; } -.@{fa-css-prefix}-lastfm:before { content: @fa-var-lastfm; } -.@{fa-css-prefix}-lastfm-square:before { content: @fa-var-lastfm-square; } -.@{fa-css-prefix}-toggle-off:before { content: @fa-var-toggle-off; } -.@{fa-css-prefix}-toggle-on:before { content: @fa-var-toggle-on; } -.@{fa-css-prefix}-bicycle:before { content: @fa-var-bicycle; } -.@{fa-css-prefix}-bus:before { content: @fa-var-bus; } -.@{fa-css-prefix}-ioxhost:before { content: @fa-var-ioxhost; } -.@{fa-css-prefix}-angellist:before { content: @fa-var-angellist; } -.@{fa-css-prefix}-cc:before { content: @fa-var-cc; } -.@{fa-css-prefix}-shekel:before, -.@{fa-css-prefix}-sheqel:before, -.@{fa-css-prefix}-ils:before { content: @fa-var-ils; } -.@{fa-css-prefix}-meanpath:before { content: @fa-var-meanpath; } -.@{fa-css-prefix}-buysellads:before { content: @fa-var-buysellads; } -.@{fa-css-prefix}-connectdevelop:before { content: @fa-var-connectdevelop; } -.@{fa-css-prefix}-dashcube:before { content: @fa-var-dashcube; } -.@{fa-css-prefix}-forumbee:before { content: @fa-var-forumbee; } -.@{fa-css-prefix}-leanpub:before { content: @fa-var-leanpub; } -.@{fa-css-prefix}-sellsy:before { content: @fa-var-sellsy; } -.@{fa-css-prefix}-shirtsinbulk:before { content: @fa-var-shirtsinbulk; } -.@{fa-css-prefix}-simplybuilt:before { content: @fa-var-simplybuilt; } -.@{fa-css-prefix}-skyatlas:before { content: @fa-var-skyatlas; } -.@{fa-css-prefix}-cart-plus:before { content: @fa-var-cart-plus; } -.@{fa-css-prefix}-cart-arrow-down:before { content: @fa-var-cart-arrow-down; } -.@{fa-css-prefix}-diamond:before { content: @fa-var-diamond; } -.@{fa-css-prefix}-ship:before { content: @fa-var-ship; } -.@{fa-css-prefix}-user-secret:before { content: @fa-var-user-secret; } -.@{fa-css-prefix}-motorcycle:before { content: @fa-var-motorcycle; } -.@{fa-css-prefix}-street-view:before { content: @fa-var-street-view; } -.@{fa-css-prefix}-heartbeat:before { content: @fa-var-heartbeat; } -.@{fa-css-prefix}-venus:before { content: @fa-var-venus; } -.@{fa-css-prefix}-mars:before { content: @fa-var-mars; } -.@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; } -.@{fa-css-prefix}-intersex:before, -.@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; } -.@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; } -.@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; } -.@{fa-css-prefix}-mars-double:before { content: @fa-var-mars-double; } -.@{fa-css-prefix}-venus-mars:before { content: @fa-var-venus-mars; } -.@{fa-css-prefix}-mars-stroke:before { content: @fa-var-mars-stroke; } -.@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; } -.@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; } -.@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; } -.@{fa-css-prefix}-genderless:before { content: @fa-var-genderless; } -.@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; } -.@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; } -.@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; } -.@{fa-css-prefix}-server:before { content: @fa-var-server; } -.@{fa-css-prefix}-user-plus:before { content: @fa-var-user-plus; } -.@{fa-css-prefix}-user-times:before { content: @fa-var-user-times; } -.@{fa-css-prefix}-hotel:before, -.@{fa-css-prefix}-bed:before { content: @fa-var-bed; } -.@{fa-css-prefix}-viacoin:before { content: @fa-var-viacoin; } -.@{fa-css-prefix}-train:before { content: @fa-var-train; } -.@{fa-css-prefix}-subway:before { content: @fa-var-subway; } -.@{fa-css-prefix}-medium:before { content: @fa-var-medium; } -.@{fa-css-prefix}-yc:before, -.@{fa-css-prefix}-y-combinator:before { content: @fa-var-y-combinator; } -.@{fa-css-prefix}-optin-monster:before { content: @fa-var-optin-monster; } -.@{fa-css-prefix}-opencart:before { content: @fa-var-opencart; } -.@{fa-css-prefix}-expeditedssl:before { content: @fa-var-expeditedssl; } -.@{fa-css-prefix}-battery-4:before, -.@{fa-css-prefix}-battery:before, -.@{fa-css-prefix}-battery-full:before { content: @fa-var-battery-full; } -.@{fa-css-prefix}-battery-3:before, -.@{fa-css-prefix}-battery-three-quarters:before { content: @fa-var-battery-three-quarters; } -.@{fa-css-prefix}-battery-2:before, -.@{fa-css-prefix}-battery-half:before { content: @fa-var-battery-half; } -.@{fa-css-prefix}-battery-1:before, -.@{fa-css-prefix}-battery-quarter:before { content: @fa-var-battery-quarter; } -.@{fa-css-prefix}-battery-0:before, -.@{fa-css-prefix}-battery-empty:before { content: @fa-var-battery-empty; } -.@{fa-css-prefix}-mouse-pointer:before { content: @fa-var-mouse-pointer; } -.@{fa-css-prefix}-i-cursor:before { content: @fa-var-i-cursor; } -.@{fa-css-prefix}-object-group:before { content: @fa-var-object-group; } -.@{fa-css-prefix}-object-ungroup:before { content: @fa-var-object-ungroup; } -.@{fa-css-prefix}-sticky-note:before { content: @fa-var-sticky-note; } -.@{fa-css-prefix}-sticky-note-o:before { content: @fa-var-sticky-note-o; } -.@{fa-css-prefix}-cc-jcb:before { content: @fa-var-cc-jcb; } -.@{fa-css-prefix}-cc-diners-club:before { content: @fa-var-cc-diners-club; } -.@{fa-css-prefix}-clone:before { content: @fa-var-clone; } -.@{fa-css-prefix}-balance-scale:before { content: @fa-var-balance-scale; } -.@{fa-css-prefix}-hourglass-o:before { content: @fa-var-hourglass-o; } -.@{fa-css-prefix}-hourglass-1:before, -.@{fa-css-prefix}-hourglass-start:before { content: @fa-var-hourglass-start; } -.@{fa-css-prefix}-hourglass-2:before, -.@{fa-css-prefix}-hourglass-half:before { content: @fa-var-hourglass-half; } -.@{fa-css-prefix}-hourglass-3:before, -.@{fa-css-prefix}-hourglass-end:before { content: @fa-var-hourglass-end; } -.@{fa-css-prefix}-hourglass:before { content: @fa-var-hourglass; } -.@{fa-css-prefix}-hand-grab-o:before, -.@{fa-css-prefix}-hand-rock-o:before { content: @fa-var-hand-rock-o; } -.@{fa-css-prefix}-hand-stop-o:before, -.@{fa-css-prefix}-hand-paper-o:before { content: @fa-var-hand-paper-o; } -.@{fa-css-prefix}-hand-scissors-o:before { content: @fa-var-hand-scissors-o; } -.@{fa-css-prefix}-hand-lizard-o:before { content: @fa-var-hand-lizard-o; } -.@{fa-css-prefix}-hand-spock-o:before { content: @fa-var-hand-spock-o; } -.@{fa-css-prefix}-hand-pointer-o:before { content: @fa-var-hand-pointer-o; } -.@{fa-css-prefix}-hand-peace-o:before { content: @fa-var-hand-peace-o; } -.@{fa-css-prefix}-trademark:before { content: @fa-var-trademark; } -.@{fa-css-prefix}-registered:before { content: @fa-var-registered; } -.@{fa-css-prefix}-creative-commons:before { content: @fa-var-creative-commons; } -.@{fa-css-prefix}-gg:before { content: @fa-var-gg; } -.@{fa-css-prefix}-gg-circle:before { content: @fa-var-gg-circle; } -.@{fa-css-prefix}-tripadvisor:before { content: @fa-var-tripadvisor; } -.@{fa-css-prefix}-odnoklassniki:before { content: @fa-var-odnoklassniki; } -.@{fa-css-prefix}-odnoklassniki-square:before { content: @fa-var-odnoklassniki-square; } -.@{fa-css-prefix}-get-pocket:before { content: @fa-var-get-pocket; } -.@{fa-css-prefix}-wikipedia-w:before { content: @fa-var-wikipedia-w; } -.@{fa-css-prefix}-safari:before { content: @fa-var-safari; } -.@{fa-css-prefix}-chrome:before { content: @fa-var-chrome; } -.@{fa-css-prefix}-firefox:before { content: @fa-var-firefox; } -.@{fa-css-prefix}-opera:before { content: @fa-var-opera; } -.@{fa-css-prefix}-internet-explorer:before { content: @fa-var-internet-explorer; } -.@{fa-css-prefix}-tv:before, -.@{fa-css-prefix}-television:before { content: @fa-var-television; } -.@{fa-css-prefix}-contao:before { content: @fa-var-contao; } -.@{fa-css-prefix}-500px:before { content: @fa-var-500px; } -.@{fa-css-prefix}-amazon:before { content: @fa-var-amazon; } -.@{fa-css-prefix}-calendar-plus-o:before { content: @fa-var-calendar-plus-o; } -.@{fa-css-prefix}-calendar-minus-o:before { content: @fa-var-calendar-minus-o; } -.@{fa-css-prefix}-calendar-times-o:before { content: @fa-var-calendar-times-o; } -.@{fa-css-prefix}-calendar-check-o:before { content: @fa-var-calendar-check-o; } -.@{fa-css-prefix}-industry:before { content: @fa-var-industry; } -.@{fa-css-prefix}-map-pin:before { content: @fa-var-map-pin; } -.@{fa-css-prefix}-map-signs:before { content: @fa-var-map-signs; } -.@{fa-css-prefix}-map-o:before { content: @fa-var-map-o; } -.@{fa-css-prefix}-map:before { content: @fa-var-map; } -.@{fa-css-prefix}-commenting:before { content: @fa-var-commenting; } -.@{fa-css-prefix}-commenting-o:before { content: @fa-var-commenting-o; } -.@{fa-css-prefix}-houzz:before { content: @fa-var-houzz; } -.@{fa-css-prefix}-vimeo:before { content: @fa-var-vimeo; } -.@{fa-css-prefix}-black-tie:before { content: @fa-var-black-tie; } -.@{fa-css-prefix}-fonticons:before { content: @fa-var-fonticons; } -.@{fa-css-prefix}-reddit-alien:before { content: @fa-var-reddit-alien; } -.@{fa-css-prefix}-edge:before { content: @fa-var-edge; } -.@{fa-css-prefix}-credit-card-alt:before { content: @fa-var-credit-card-alt; } -.@{fa-css-prefix}-codiepie:before { content: @fa-var-codiepie; } -.@{fa-css-prefix}-modx:before { content: @fa-var-modx; } -.@{fa-css-prefix}-fort-awesome:before { content: @fa-var-fort-awesome; } -.@{fa-css-prefix}-usb:before { content: @fa-var-usb; } -.@{fa-css-prefix}-product-hunt:before { content: @fa-var-product-hunt; } -.@{fa-css-prefix}-mixcloud:before { content: @fa-var-mixcloud; } -.@{fa-css-prefix}-scribd:before { content: @fa-var-scribd; } -.@{fa-css-prefix}-pause-circle:before { content: @fa-var-pause-circle; } -.@{fa-css-prefix}-pause-circle-o:before { content: @fa-var-pause-circle-o; } -.@{fa-css-prefix}-stop-circle:before { content: @fa-var-stop-circle; } -.@{fa-css-prefix}-stop-circle-o:before { content: @fa-var-stop-circle-o; } -.@{fa-css-prefix}-shopping-bag:before { content: @fa-var-shopping-bag; } -.@{fa-css-prefix}-shopping-basket:before { content: @fa-var-shopping-basket; } -.@{fa-css-prefix}-hashtag:before { content: @fa-var-hashtag; } -.@{fa-css-prefix}-bluetooth:before { content: @fa-var-bluetooth; } -.@{fa-css-prefix}-bluetooth-b:before { content: @fa-var-bluetooth-b; } -.@{fa-css-prefix}-percent:before { content: @fa-var-percent; } -.@{fa-css-prefix}-gitlab:before { content: @fa-var-gitlab; } -.@{fa-css-prefix}-wpbeginner:before { content: @fa-var-wpbeginner; } -.@{fa-css-prefix}-wpforms:before { content: @fa-var-wpforms; } -.@{fa-css-prefix}-envira:before { content: @fa-var-envira; } -.@{fa-css-prefix}-universal-access:before { content: @fa-var-universal-access; } -.@{fa-css-prefix}-wheelchair-alt:before { content: @fa-var-wheelchair-alt; } -.@{fa-css-prefix}-question-circle-o:before { content: @fa-var-question-circle-o; } -.@{fa-css-prefix}-blind:before { content: @fa-var-blind; } -.@{fa-css-prefix}-audio-description:before { content: @fa-var-audio-description; } -.@{fa-css-prefix}-volume-control-phone:before { content: @fa-var-volume-control-phone; } -.@{fa-css-prefix}-braille:before { content: @fa-var-braille; } -.@{fa-css-prefix}-assistive-listening-systems:before { content: @fa-var-assistive-listening-systems; } -.@{fa-css-prefix}-asl-interpreting:before, -.@{fa-css-prefix}-american-sign-language-interpreting:before { content: @fa-var-american-sign-language-interpreting; } -.@{fa-css-prefix}-deafness:before, -.@{fa-css-prefix}-hard-of-hearing:before, -.@{fa-css-prefix}-deaf:before { content: @fa-var-deaf; } -.@{fa-css-prefix}-glide:before { content: @fa-var-glide; } -.@{fa-css-prefix}-glide-g:before { content: @fa-var-glide-g; } -.@{fa-css-prefix}-signing:before, -.@{fa-css-prefix}-sign-language:before { content: @fa-var-sign-language; } -.@{fa-css-prefix}-low-vision:before { content: @fa-var-low-vision; } -.@{fa-css-prefix}-viadeo:before { content: @fa-var-viadeo; } -.@{fa-css-prefix}-viadeo-square:before { content: @fa-var-viadeo-square; } -.@{fa-css-prefix}-snapchat:before { content: @fa-var-snapchat; } -.@{fa-css-prefix}-snapchat-ghost:before { content: @fa-var-snapchat-ghost; } -.@{fa-css-prefix}-snapchat-square:before { content: @fa-var-snapchat-square; } -.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; } -.@{fa-css-prefix}-first-order:before { content: @fa-var-first-order; } -.@{fa-css-prefix}-yoast:before { content: @fa-var-yoast; } -.@{fa-css-prefix}-themeisle:before { content: @fa-var-themeisle; } -.@{fa-css-prefix}-google-plus-circle:before, -.@{fa-css-prefix}-google-plus-official:before { content: @fa-var-google-plus-official; } -.@{fa-css-prefix}-fa:before, -.@{fa-css-prefix}-font-awesome:before { content: @fa-var-font-awesome; } -.@{fa-css-prefix}-handshake-o:before { content: @fa-var-handshake-o; } -.@{fa-css-prefix}-envelope-open:before { content: @fa-var-envelope-open; } -.@{fa-css-prefix}-envelope-open-o:before { content: @fa-var-envelope-open-o; } -.@{fa-css-prefix}-linode:before { content: @fa-var-linode; } -.@{fa-css-prefix}-address-book:before { content: @fa-var-address-book; } -.@{fa-css-prefix}-address-book-o:before { content: @fa-var-address-book-o; } -.@{fa-css-prefix}-vcard:before, -.@{fa-css-prefix}-address-card:before { content: @fa-var-address-card; } -.@{fa-css-prefix}-vcard-o:before, -.@{fa-css-prefix}-address-card-o:before { content: @fa-var-address-card-o; } -.@{fa-css-prefix}-user-circle:before { content: @fa-var-user-circle; } -.@{fa-css-prefix}-user-circle-o:before { content: @fa-var-user-circle-o; } -.@{fa-css-prefix}-user-o:before { content: @fa-var-user-o; } -.@{fa-css-prefix}-id-badge:before { content: @fa-var-id-badge; } -.@{fa-css-prefix}-drivers-license:before, -.@{fa-css-prefix}-id-card:before { content: @fa-var-id-card; } -.@{fa-css-prefix}-drivers-license-o:before, -.@{fa-css-prefix}-id-card-o:before { content: @fa-var-id-card-o; } -.@{fa-css-prefix}-quora:before { content: @fa-var-quora; } -.@{fa-css-prefix}-free-code-camp:before { content: @fa-var-free-code-camp; } -.@{fa-css-prefix}-telegram:before { content: @fa-var-telegram; } -.@{fa-css-prefix}-thermometer-4:before, -.@{fa-css-prefix}-thermometer:before, -.@{fa-css-prefix}-thermometer-full:before { content: @fa-var-thermometer-full; } -.@{fa-css-prefix}-thermometer-3:before, -.@{fa-css-prefix}-thermometer-three-quarters:before { content: @fa-var-thermometer-three-quarters; } -.@{fa-css-prefix}-thermometer-2:before, -.@{fa-css-prefix}-thermometer-half:before { content: @fa-var-thermometer-half; } -.@{fa-css-prefix}-thermometer-1:before, -.@{fa-css-prefix}-thermometer-quarter:before { content: @fa-var-thermometer-quarter; } -.@{fa-css-prefix}-thermometer-0:before, -.@{fa-css-prefix}-thermometer-empty:before { content: @fa-var-thermometer-empty; } -.@{fa-css-prefix}-shower:before { content: @fa-var-shower; } -.@{fa-css-prefix}-bathtub:before, -.@{fa-css-prefix}-s15:before, -.@{fa-css-prefix}-bath:before { content: @fa-var-bath; } -.@{fa-css-prefix}-podcast:before { content: @fa-var-podcast; } -.@{fa-css-prefix}-window-maximize:before { content: @fa-var-window-maximize; } -.@{fa-css-prefix}-window-minimize:before { content: @fa-var-window-minimize; } -.@{fa-css-prefix}-window-restore:before { content: @fa-var-window-restore; } -.@{fa-css-prefix}-times-rectangle:before, -.@{fa-css-prefix}-window-close:before { content: @fa-var-window-close; } -.@{fa-css-prefix}-times-rectangle-o:before, -.@{fa-css-prefix}-window-close-o:before { content: @fa-var-window-close-o; } -.@{fa-css-prefix}-bandcamp:before { content: @fa-var-bandcamp; } -.@{fa-css-prefix}-grav:before { content: @fa-var-grav; } -.@{fa-css-prefix}-etsy:before { content: @fa-var-etsy; } -.@{fa-css-prefix}-imdb:before { content: @fa-var-imdb; } -.@{fa-css-prefix}-ravelry:before { content: @fa-var-ravelry; } -.@{fa-css-prefix}-eercast:before { content: @fa-var-eercast; } -.@{fa-css-prefix}-microchip:before { content: @fa-var-microchip; } -.@{fa-css-prefix}-snowflake-o:before { content: @fa-var-snowflake-o; } -.@{fa-css-prefix}-superpowers:before { content: @fa-var-superpowers; } -.@{fa-css-prefix}-wpexplorer:before { content: @fa-var-wpexplorer; } -.@{fa-css-prefix}-meetup:before { content: @fa-var-meetup; } diff --git a/v3/css/font-awesome-4.7.0/less/larger.less b/v3/css/font-awesome-4.7.0/less/larger.less deleted file mode 100644 index c9d6467..0000000 --- a/v3/css/font-awesome-4.7.0/less/larger.less +++ /dev/null @@ -1,13 +0,0 @@ -// Icon Sizes -// ------------------------- - -/* makes the font 33% larger relative to the icon container */ -.@{fa-css-prefix}-lg { - font-size: (4em / 3); - line-height: (3em / 4); - vertical-align: -15%; -} -.@{fa-css-prefix}-2x { font-size: 2em; } -.@{fa-css-prefix}-3x { font-size: 3em; } -.@{fa-css-prefix}-4x { font-size: 4em; } -.@{fa-css-prefix}-5x { font-size: 5em; } diff --git a/v3/css/font-awesome-4.7.0/less/list.less b/v3/css/font-awesome-4.7.0/less/list.less deleted file mode 100644 index 0b44038..0000000 --- a/v3/css/font-awesome-4.7.0/less/list.less +++ /dev/null @@ -1,19 +0,0 @@ -// List Icons -// ------------------------- - -.@{fa-css-prefix}-ul { - padding-left: 0; - margin-left: @fa-li-width; - list-style-type: none; - > li { position: relative; } -} -.@{fa-css-prefix}-li { - position: absolute; - left: -@fa-li-width; - width: @fa-li-width; - top: (2em / 14); - text-align: center; - &.@{fa-css-prefix}-lg { - left: (-@fa-li-width + (4em / 14)); - } -} diff --git a/v3/css/font-awesome-4.7.0/less/mixins.less b/v3/css/font-awesome-4.7.0/less/mixins.less deleted file mode 100644 index beef231..0000000 --- a/v3/css/font-awesome-4.7.0/less/mixins.less +++ /dev/null @@ -1,60 +0,0 @@ -// Mixins -// -------------------------- - -.fa-icon() { - display: inline-block; - font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration - font-size: inherit; // can't have font-size inherit on line above, so need to override - text-rendering: auto; // optimizelegibility throws things off #1094 - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -} - -.fa-icon-rotate(@degrees, @rotation) { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; - -webkit-transform: rotate(@degrees); - -ms-transform: rotate(@degrees); - transform: rotate(@degrees); -} - -.fa-icon-flip(@horiz, @vert, @rotation) { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; - -webkit-transform: scale(@horiz, @vert); - -ms-transform: scale(@horiz, @vert); - transform: scale(@horiz, @vert); -} - - -// Only display content to screen readers. A la Bootstrap 4. -// -// See: http://a11yproject.com/posts/how-to-hide-content/ - -.sr-only() { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; -} - -// Use in conjunction with .sr-only to only display content when it's focused. -// -// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 -// -// Credit: HTML5 Boilerplate - -.sr-only-focusable() { - &:active, - &:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; - } -} diff --git a/v3/css/font-awesome-4.7.0/less/path.less b/v3/css/font-awesome-4.7.0/less/path.less deleted file mode 100644 index 835be41..0000000 --- a/v3/css/font-awesome-4.7.0/less/path.less +++ /dev/null @@ -1,15 +0,0 @@ -/* FONT PATH - * -------------------------- */ - -@font-face { - font-family: 'FontAwesome'; - src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); - src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), - url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), - url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), - url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), - url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); - // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts - font-weight: normal; - font-style: normal; -} diff --git a/v3/css/font-awesome-4.7.0/less/rotated-flipped.less b/v3/css/font-awesome-4.7.0/less/rotated-flipped.less deleted file mode 100644 index f6ba814..0000000 --- a/v3/css/font-awesome-4.7.0/less/rotated-flipped.less +++ /dev/null @@ -1,20 +0,0 @@ -// Rotated & Flipped Icons -// ------------------------- - -.@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } -.@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } -.@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } - -.@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } -.@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } - -// Hook for IE8-9 -// ------------------------- - -:root .@{fa-css-prefix}-rotate-90, -:root .@{fa-css-prefix}-rotate-180, -:root .@{fa-css-prefix}-rotate-270, -:root .@{fa-css-prefix}-flip-horizontal, -:root .@{fa-css-prefix}-flip-vertical { - filter: none; -} diff --git a/v3/css/font-awesome-4.7.0/less/screen-reader.less b/v3/css/font-awesome-4.7.0/less/screen-reader.less deleted file mode 100644 index 11c1881..0000000 --- a/v3/css/font-awesome-4.7.0/less/screen-reader.less +++ /dev/null @@ -1,5 +0,0 @@ -// Screen Readers -// ------------------------- - -.sr-only { .sr-only(); } -.sr-only-focusable { .sr-only-focusable(); } diff --git a/v3/css/font-awesome-4.7.0/less/stacked.less b/v3/css/font-awesome-4.7.0/less/stacked.less deleted file mode 100644 index fc53fb0..0000000 --- a/v3/css/font-awesome-4.7.0/less/stacked.less +++ /dev/null @@ -1,20 +0,0 @@ -// Stacked Icons -// ------------------------- - -.@{fa-css-prefix}-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} -.@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} -.@{fa-css-prefix}-stack-1x { line-height: inherit; } -.@{fa-css-prefix}-stack-2x { font-size: 2em; } -.@{fa-css-prefix}-inverse { color: @fa-inverse; } diff --git a/v3/css/font-awesome-4.7.0/less/variables.less b/v3/css/font-awesome-4.7.0/less/variables.less deleted file mode 100644 index 7ddbbc0..0000000 --- a/v3/css/font-awesome-4.7.0/less/variables.less +++ /dev/null @@ -1,800 +0,0 @@ -// Variables -// -------------------------- - -@fa-font-path: "../fonts"; -@fa-font-size-base: 14px; -@fa-line-height-base: 1; -//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts"; // for referencing Bootstrap CDN font files directly -@fa-css-prefix: fa; -@fa-version: "4.7.0"; -@fa-border-color: #eee; -@fa-inverse: #fff; -@fa-li-width: (30em / 14); - -@fa-var-500px: "\f26e"; -@fa-var-address-book: "\f2b9"; -@fa-var-address-book-o: "\f2ba"; -@fa-var-address-card: "\f2bb"; -@fa-var-address-card-o: "\f2bc"; -@fa-var-adjust: "\f042"; -@fa-var-adn: "\f170"; -@fa-var-align-center: "\f037"; -@fa-var-align-justify: "\f039"; -@fa-var-align-left: "\f036"; -@fa-var-align-right: "\f038"; -@fa-var-amazon: "\f270"; -@fa-var-ambulance: "\f0f9"; -@fa-var-american-sign-language-interpreting: "\f2a3"; -@fa-var-anchor: "\f13d"; -@fa-var-android: "\f17b"; -@fa-var-angellist: "\f209"; -@fa-var-angle-double-down: "\f103"; -@fa-var-angle-double-left: "\f100"; -@fa-var-angle-double-right: "\f101"; -@fa-var-angle-double-up: "\f102"; -@fa-var-angle-down: "\f107"; -@fa-var-angle-left: "\f104"; -@fa-var-angle-right: "\f105"; -@fa-var-angle-up: "\f106"; -@fa-var-apple: "\f179"; -@fa-var-archive: "\f187"; -@fa-var-area-chart: "\f1fe"; -@fa-var-arrow-circle-down: "\f0ab"; -@fa-var-arrow-circle-left: "\f0a8"; -@fa-var-arrow-circle-o-down: "\f01a"; -@fa-var-arrow-circle-o-left: "\f190"; -@fa-var-arrow-circle-o-right: "\f18e"; -@fa-var-arrow-circle-o-up: "\f01b"; -@fa-var-arrow-circle-right: "\f0a9"; -@fa-var-arrow-circle-up: "\f0aa"; -@fa-var-arrow-down: "\f063"; -@fa-var-arrow-left: "\f060"; -@fa-var-arrow-right: "\f061"; -@fa-var-arrow-up: "\f062"; -@fa-var-arrows: "\f047"; -@fa-var-arrows-alt: "\f0b2"; -@fa-var-arrows-h: "\f07e"; -@fa-var-arrows-v: "\f07d"; -@fa-var-asl-interpreting: "\f2a3"; -@fa-var-assistive-listening-systems: "\f2a2"; -@fa-var-asterisk: "\f069"; -@fa-var-at: "\f1fa"; -@fa-var-audio-description: "\f29e"; -@fa-var-automobile: "\f1b9"; -@fa-var-backward: "\f04a"; -@fa-var-balance-scale: "\f24e"; -@fa-var-ban: "\f05e"; -@fa-var-bandcamp: "\f2d5"; -@fa-var-bank: "\f19c"; -@fa-var-bar-chart: "\f080"; -@fa-var-bar-chart-o: "\f080"; -@fa-var-barcode: "\f02a"; -@fa-var-bars: "\f0c9"; -@fa-var-bath: "\f2cd"; -@fa-var-bathtub: "\f2cd"; -@fa-var-battery: "\f240"; -@fa-var-battery-0: "\f244"; -@fa-var-battery-1: "\f243"; -@fa-var-battery-2: "\f242"; -@fa-var-battery-3: "\f241"; -@fa-var-battery-4: "\f240"; -@fa-var-battery-empty: "\f244"; -@fa-var-battery-full: "\f240"; -@fa-var-battery-half: "\f242"; -@fa-var-battery-quarter: "\f243"; -@fa-var-battery-three-quarters: "\f241"; -@fa-var-bed: "\f236"; -@fa-var-beer: "\f0fc"; -@fa-var-behance: "\f1b4"; -@fa-var-behance-square: "\f1b5"; -@fa-var-bell: "\f0f3"; -@fa-var-bell-o: "\f0a2"; -@fa-var-bell-slash: "\f1f6"; -@fa-var-bell-slash-o: "\f1f7"; -@fa-var-bicycle: "\f206"; -@fa-var-binoculars: "\f1e5"; -@fa-var-birthday-cake: "\f1fd"; -@fa-var-bitbucket: "\f171"; -@fa-var-bitbucket-square: "\f172"; -@fa-var-bitcoin: "\f15a"; -@fa-var-black-tie: "\f27e"; -@fa-var-blind: "\f29d"; -@fa-var-bluetooth: "\f293"; -@fa-var-bluetooth-b: "\f294"; -@fa-var-bold: "\f032"; -@fa-var-bolt: "\f0e7"; -@fa-var-bomb: "\f1e2"; -@fa-var-book: "\f02d"; -@fa-var-bookmark: "\f02e"; -@fa-var-bookmark-o: "\f097"; -@fa-var-braille: "\f2a1"; -@fa-var-briefcase: "\f0b1"; -@fa-var-btc: "\f15a"; -@fa-var-bug: "\f188"; -@fa-var-building: "\f1ad"; -@fa-var-building-o: "\f0f7"; -@fa-var-bullhorn: "\f0a1"; -@fa-var-bullseye: "\f140"; -@fa-var-bus: "\f207"; -@fa-var-buysellads: "\f20d"; -@fa-var-cab: "\f1ba"; -@fa-var-calculator: "\f1ec"; -@fa-var-calendar: "\f073"; -@fa-var-calendar-check-o: "\f274"; -@fa-var-calendar-minus-o: "\f272"; -@fa-var-calendar-o: "\f133"; -@fa-var-calendar-plus-o: "\f271"; -@fa-var-calendar-times-o: "\f273"; -@fa-var-camera: "\f030"; -@fa-var-camera-retro: "\f083"; -@fa-var-car: "\f1b9"; -@fa-var-caret-down: "\f0d7"; -@fa-var-caret-left: "\f0d9"; -@fa-var-caret-right: "\f0da"; -@fa-var-caret-square-o-down: "\f150"; -@fa-var-caret-square-o-left: "\f191"; -@fa-var-caret-square-o-right: "\f152"; -@fa-var-caret-square-o-up: "\f151"; -@fa-var-caret-up: "\f0d8"; -@fa-var-cart-arrow-down: "\f218"; -@fa-var-cart-plus: "\f217"; -@fa-var-cc: "\f20a"; -@fa-var-cc-amex: "\f1f3"; -@fa-var-cc-diners-club: "\f24c"; -@fa-var-cc-discover: "\f1f2"; -@fa-var-cc-jcb: "\f24b"; -@fa-var-cc-mastercard: "\f1f1"; -@fa-var-cc-paypal: "\f1f4"; -@fa-var-cc-stripe: "\f1f5"; -@fa-var-cc-visa: "\f1f0"; -@fa-var-certificate: "\f0a3"; -@fa-var-chain: "\f0c1"; -@fa-var-chain-broken: "\f127"; -@fa-var-check: "\f00c"; -@fa-var-check-circle: "\f058"; -@fa-var-check-circle-o: "\f05d"; -@fa-var-check-square: "\f14a"; -@fa-var-check-square-o: "\f046"; -@fa-var-chevron-circle-down: "\f13a"; -@fa-var-chevron-circle-left: "\f137"; -@fa-var-chevron-circle-right: "\f138"; -@fa-var-chevron-circle-up: "\f139"; -@fa-var-chevron-down: "\f078"; -@fa-var-chevron-left: "\f053"; -@fa-var-chevron-right: "\f054"; -@fa-var-chevron-up: "\f077"; -@fa-var-child: "\f1ae"; -@fa-var-chrome: "\f268"; -@fa-var-circle: "\f111"; -@fa-var-circle-o: "\f10c"; -@fa-var-circle-o-notch: "\f1ce"; -@fa-var-circle-thin: "\f1db"; -@fa-var-clipboard: "\f0ea"; -@fa-var-clock-o: "\f017"; -@fa-var-clone: "\f24d"; -@fa-var-close: "\f00d"; -@fa-var-cloud: "\f0c2"; -@fa-var-cloud-download: "\f0ed"; -@fa-var-cloud-upload: "\f0ee"; -@fa-var-cny: "\f157"; -@fa-var-code: "\f121"; -@fa-var-code-fork: "\f126"; -@fa-var-codepen: "\f1cb"; -@fa-var-codiepie: "\f284"; -@fa-var-coffee: "\f0f4"; -@fa-var-cog: "\f013"; -@fa-var-cogs: "\f085"; -@fa-var-columns: "\f0db"; -@fa-var-comment: "\f075"; -@fa-var-comment-o: "\f0e5"; -@fa-var-commenting: "\f27a"; -@fa-var-commenting-o: "\f27b"; -@fa-var-comments: "\f086"; -@fa-var-comments-o: "\f0e6"; -@fa-var-compass: "\f14e"; -@fa-var-compress: "\f066"; -@fa-var-connectdevelop: "\f20e"; -@fa-var-contao: "\f26d"; -@fa-var-copy: "\f0c5"; -@fa-var-copyright: "\f1f9"; -@fa-var-creative-commons: "\f25e"; -@fa-var-credit-card: "\f09d"; -@fa-var-credit-card-alt: "\f283"; -@fa-var-crop: "\f125"; -@fa-var-crosshairs: "\f05b"; -@fa-var-css3: "\f13c"; -@fa-var-cube: "\f1b2"; -@fa-var-cubes: "\f1b3"; -@fa-var-cut: "\f0c4"; -@fa-var-cutlery: "\f0f5"; -@fa-var-dashboard: "\f0e4"; -@fa-var-dashcube: "\f210"; -@fa-var-database: "\f1c0"; -@fa-var-deaf: "\f2a4"; -@fa-var-deafness: "\f2a4"; -@fa-var-dedent: "\f03b"; -@fa-var-delicious: "\f1a5"; -@fa-var-desktop: "\f108"; -@fa-var-deviantart: "\f1bd"; -@fa-var-diamond: "\f219"; -@fa-var-digg: "\f1a6"; -@fa-var-dollar: "\f155"; -@fa-var-dot-circle-o: "\f192"; -@fa-var-download: "\f019"; -@fa-var-dribbble: "\f17d"; -@fa-var-drivers-license: "\f2c2"; -@fa-var-drivers-license-o: "\f2c3"; -@fa-var-dropbox: "\f16b"; -@fa-var-drupal: "\f1a9"; -@fa-var-edge: "\f282"; -@fa-var-edit: "\f044"; -@fa-var-eercast: "\f2da"; -@fa-var-eject: "\f052"; -@fa-var-ellipsis-h: "\f141"; -@fa-var-ellipsis-v: "\f142"; -@fa-var-empire: "\f1d1"; -@fa-var-envelope: "\f0e0"; -@fa-var-envelope-o: "\f003"; -@fa-var-envelope-open: "\f2b6"; -@fa-var-envelope-open-o: "\f2b7"; -@fa-var-envelope-square: "\f199"; -@fa-var-envira: "\f299"; -@fa-var-eraser: "\f12d"; -@fa-var-etsy: "\f2d7"; -@fa-var-eur: "\f153"; -@fa-var-euro: "\f153"; -@fa-var-exchange: "\f0ec"; -@fa-var-exclamation: "\f12a"; -@fa-var-exclamation-circle: "\f06a"; -@fa-var-exclamation-triangle: "\f071"; -@fa-var-expand: "\f065"; -@fa-var-expeditedssl: "\f23e"; -@fa-var-external-link: "\f08e"; -@fa-var-external-link-square: "\f14c"; -@fa-var-eye: "\f06e"; -@fa-var-eye-slash: "\f070"; -@fa-var-eyedropper: "\f1fb"; -@fa-var-fa: "\f2b4"; -@fa-var-facebook: "\f09a"; -@fa-var-facebook-f: "\f09a"; -@fa-var-facebook-official: "\f230"; -@fa-var-facebook-square: "\f082"; -@fa-var-fast-backward: "\f049"; -@fa-var-fast-forward: "\f050"; -@fa-var-fax: "\f1ac"; -@fa-var-feed: "\f09e"; -@fa-var-female: "\f182"; -@fa-var-fighter-jet: "\f0fb"; -@fa-var-file: "\f15b"; -@fa-var-file-archive-o: "\f1c6"; -@fa-var-file-audio-o: "\f1c7"; -@fa-var-file-code-o: "\f1c9"; -@fa-var-file-excel-o: "\f1c3"; -@fa-var-file-image-o: "\f1c5"; -@fa-var-file-movie-o: "\f1c8"; -@fa-var-file-o: "\f016"; -@fa-var-file-pdf-o: "\f1c1"; -@fa-var-file-photo-o: "\f1c5"; -@fa-var-file-picture-o: "\f1c5"; -@fa-var-file-powerpoint-o: "\f1c4"; -@fa-var-file-sound-o: "\f1c7"; -@fa-var-file-text: "\f15c"; -@fa-var-file-text-o: "\f0f6"; -@fa-var-file-video-o: "\f1c8"; -@fa-var-file-word-o: "\f1c2"; -@fa-var-file-zip-o: "\f1c6"; -@fa-var-files-o: "\f0c5"; -@fa-var-film: "\f008"; -@fa-var-filter: "\f0b0"; -@fa-var-fire: "\f06d"; -@fa-var-fire-extinguisher: "\f134"; -@fa-var-firefox: "\f269"; -@fa-var-first-order: "\f2b0"; -@fa-var-flag: "\f024"; -@fa-var-flag-checkered: "\f11e"; -@fa-var-flag-o: "\f11d"; -@fa-var-flash: "\f0e7"; -@fa-var-flask: "\f0c3"; -@fa-var-flickr: "\f16e"; -@fa-var-floppy-o: "\f0c7"; -@fa-var-folder: "\f07b"; -@fa-var-folder-o: "\f114"; -@fa-var-folder-open: "\f07c"; -@fa-var-folder-open-o: "\f115"; -@fa-var-font: "\f031"; -@fa-var-font-awesome: "\f2b4"; -@fa-var-fonticons: "\f280"; -@fa-var-fort-awesome: "\f286"; -@fa-var-forumbee: "\f211"; -@fa-var-forward: "\f04e"; -@fa-var-foursquare: "\f180"; -@fa-var-free-code-camp: "\f2c5"; -@fa-var-frown-o: "\f119"; -@fa-var-futbol-o: "\f1e3"; -@fa-var-gamepad: "\f11b"; -@fa-var-gavel: "\f0e3"; -@fa-var-gbp: "\f154"; -@fa-var-ge: "\f1d1"; -@fa-var-gear: "\f013"; -@fa-var-gears: "\f085"; -@fa-var-genderless: "\f22d"; -@fa-var-get-pocket: "\f265"; -@fa-var-gg: "\f260"; -@fa-var-gg-circle: "\f261"; -@fa-var-gift: "\f06b"; -@fa-var-git: "\f1d3"; -@fa-var-git-square: "\f1d2"; -@fa-var-github: "\f09b"; -@fa-var-github-alt: "\f113"; -@fa-var-github-square: "\f092"; -@fa-var-gitlab: "\f296"; -@fa-var-gittip: "\f184"; -@fa-var-glass: "\f000"; -@fa-var-glide: "\f2a5"; -@fa-var-glide-g: "\f2a6"; -@fa-var-globe: "\f0ac"; -@fa-var-google: "\f1a0"; -@fa-var-google-plus: "\f0d5"; -@fa-var-google-plus-circle: "\f2b3"; -@fa-var-google-plus-official: "\f2b3"; -@fa-var-google-plus-square: "\f0d4"; -@fa-var-google-wallet: "\f1ee"; -@fa-var-graduation-cap: "\f19d"; -@fa-var-gratipay: "\f184"; -@fa-var-grav: "\f2d6"; -@fa-var-group: "\f0c0"; -@fa-var-h-square: "\f0fd"; -@fa-var-hacker-news: "\f1d4"; -@fa-var-hand-grab-o: "\f255"; -@fa-var-hand-lizard-o: "\f258"; -@fa-var-hand-o-down: "\f0a7"; -@fa-var-hand-o-left: "\f0a5"; -@fa-var-hand-o-right: "\f0a4"; -@fa-var-hand-o-up: "\f0a6"; -@fa-var-hand-paper-o: "\f256"; -@fa-var-hand-peace-o: "\f25b"; -@fa-var-hand-pointer-o: "\f25a"; -@fa-var-hand-rock-o: "\f255"; -@fa-var-hand-scissors-o: "\f257"; -@fa-var-hand-spock-o: "\f259"; -@fa-var-hand-stop-o: "\f256"; -@fa-var-handshake-o: "\f2b5"; -@fa-var-hard-of-hearing: "\f2a4"; -@fa-var-hashtag: "\f292"; -@fa-var-hdd-o: "\f0a0"; -@fa-var-header: "\f1dc"; -@fa-var-headphones: "\f025"; -@fa-var-heart: "\f004"; -@fa-var-heart-o: "\f08a"; -@fa-var-heartbeat: "\f21e"; -@fa-var-history: "\f1da"; -@fa-var-home: "\f015"; -@fa-var-hospital-o: "\f0f8"; -@fa-var-hotel: "\f236"; -@fa-var-hourglass: "\f254"; -@fa-var-hourglass-1: "\f251"; -@fa-var-hourglass-2: "\f252"; -@fa-var-hourglass-3: "\f253"; -@fa-var-hourglass-end: "\f253"; -@fa-var-hourglass-half: "\f252"; -@fa-var-hourglass-o: "\f250"; -@fa-var-hourglass-start: "\f251"; -@fa-var-houzz: "\f27c"; -@fa-var-html5: "\f13b"; -@fa-var-i-cursor: "\f246"; -@fa-var-id-badge: "\f2c1"; -@fa-var-id-card: "\f2c2"; -@fa-var-id-card-o: "\f2c3"; -@fa-var-ils: "\f20b"; -@fa-var-image: "\f03e"; -@fa-var-imdb: "\f2d8"; -@fa-var-inbox: "\f01c"; -@fa-var-indent: "\f03c"; -@fa-var-industry: "\f275"; -@fa-var-info: "\f129"; -@fa-var-info-circle: "\f05a"; -@fa-var-inr: "\f156"; -@fa-var-instagram: "\f16d"; -@fa-var-institution: "\f19c"; -@fa-var-internet-explorer: "\f26b"; -@fa-var-intersex: "\f224"; -@fa-var-ioxhost: "\f208"; -@fa-var-italic: "\f033"; -@fa-var-joomla: "\f1aa"; -@fa-var-jpy: "\f157"; -@fa-var-jsfiddle: "\f1cc"; -@fa-var-key: "\f084"; -@fa-var-keyboard-o: "\f11c"; -@fa-var-krw: "\f159"; -@fa-var-language: "\f1ab"; -@fa-var-laptop: "\f109"; -@fa-var-lastfm: "\f202"; -@fa-var-lastfm-square: "\f203"; -@fa-var-leaf: "\f06c"; -@fa-var-leanpub: "\f212"; -@fa-var-legal: "\f0e3"; -@fa-var-lemon-o: "\f094"; -@fa-var-level-down: "\f149"; -@fa-var-level-up: "\f148"; -@fa-var-life-bouy: "\f1cd"; -@fa-var-life-buoy: "\f1cd"; -@fa-var-life-ring: "\f1cd"; -@fa-var-life-saver: "\f1cd"; -@fa-var-lightbulb-o: "\f0eb"; -@fa-var-line-chart: "\f201"; -@fa-var-link: "\f0c1"; -@fa-var-linkedin: "\f0e1"; -@fa-var-linkedin-square: "\f08c"; -@fa-var-linode: "\f2b8"; -@fa-var-linux: "\f17c"; -@fa-var-list: "\f03a"; -@fa-var-list-alt: "\f022"; -@fa-var-list-ol: "\f0cb"; -@fa-var-list-ul: "\f0ca"; -@fa-var-location-arrow: "\f124"; -@fa-var-lock: "\f023"; -@fa-var-long-arrow-down: "\f175"; -@fa-var-long-arrow-left: "\f177"; -@fa-var-long-arrow-right: "\f178"; -@fa-var-long-arrow-up: "\f176"; -@fa-var-low-vision: "\f2a8"; -@fa-var-magic: "\f0d0"; -@fa-var-magnet: "\f076"; -@fa-var-mail-forward: "\f064"; -@fa-var-mail-reply: "\f112"; -@fa-var-mail-reply-all: "\f122"; -@fa-var-male: "\f183"; -@fa-var-map: "\f279"; -@fa-var-map-marker: "\f041"; -@fa-var-map-o: "\f278"; -@fa-var-map-pin: "\f276"; -@fa-var-map-signs: "\f277"; -@fa-var-mars: "\f222"; -@fa-var-mars-double: "\f227"; -@fa-var-mars-stroke: "\f229"; -@fa-var-mars-stroke-h: "\f22b"; -@fa-var-mars-stroke-v: "\f22a"; -@fa-var-maxcdn: "\f136"; -@fa-var-meanpath: "\f20c"; -@fa-var-medium: "\f23a"; -@fa-var-medkit: "\f0fa"; -@fa-var-meetup: "\f2e0"; -@fa-var-meh-o: "\f11a"; -@fa-var-mercury: "\f223"; -@fa-var-microchip: "\f2db"; -@fa-var-microphone: "\f130"; -@fa-var-microphone-slash: "\f131"; -@fa-var-minus: "\f068"; -@fa-var-minus-circle: "\f056"; -@fa-var-minus-square: "\f146"; -@fa-var-minus-square-o: "\f147"; -@fa-var-mixcloud: "\f289"; -@fa-var-mobile: "\f10b"; -@fa-var-mobile-phone: "\f10b"; -@fa-var-modx: "\f285"; -@fa-var-money: "\f0d6"; -@fa-var-moon-o: "\f186"; -@fa-var-mortar-board: "\f19d"; -@fa-var-motorcycle: "\f21c"; -@fa-var-mouse-pointer: "\f245"; -@fa-var-music: "\f001"; -@fa-var-navicon: "\f0c9"; -@fa-var-neuter: "\f22c"; -@fa-var-newspaper-o: "\f1ea"; -@fa-var-object-group: "\f247"; -@fa-var-object-ungroup: "\f248"; -@fa-var-odnoklassniki: "\f263"; -@fa-var-odnoklassniki-square: "\f264"; -@fa-var-opencart: "\f23d"; -@fa-var-openid: "\f19b"; -@fa-var-opera: "\f26a"; -@fa-var-optin-monster: "\f23c"; -@fa-var-outdent: "\f03b"; -@fa-var-pagelines: "\f18c"; -@fa-var-paint-brush: "\f1fc"; -@fa-var-paper-plane: "\f1d8"; -@fa-var-paper-plane-o: "\f1d9"; -@fa-var-paperclip: "\f0c6"; -@fa-var-paragraph: "\f1dd"; -@fa-var-paste: "\f0ea"; -@fa-var-pause: "\f04c"; -@fa-var-pause-circle: "\f28b"; -@fa-var-pause-circle-o: "\f28c"; -@fa-var-paw: "\f1b0"; -@fa-var-paypal: "\f1ed"; -@fa-var-pencil: "\f040"; -@fa-var-pencil-square: "\f14b"; -@fa-var-pencil-square-o: "\f044"; -@fa-var-percent: "\f295"; -@fa-var-phone: "\f095"; -@fa-var-phone-square: "\f098"; -@fa-var-photo: "\f03e"; -@fa-var-picture-o: "\f03e"; -@fa-var-pie-chart: "\f200"; -@fa-var-pied-piper: "\f2ae"; -@fa-var-pied-piper-alt: "\f1a8"; -@fa-var-pied-piper-pp: "\f1a7"; -@fa-var-pinterest: "\f0d2"; -@fa-var-pinterest-p: "\f231"; -@fa-var-pinterest-square: "\f0d3"; -@fa-var-plane: "\f072"; -@fa-var-play: "\f04b"; -@fa-var-play-circle: "\f144"; -@fa-var-play-circle-o: "\f01d"; -@fa-var-plug: "\f1e6"; -@fa-var-plus: "\f067"; -@fa-var-plus-circle: "\f055"; -@fa-var-plus-square: "\f0fe"; -@fa-var-plus-square-o: "\f196"; -@fa-var-podcast: "\f2ce"; -@fa-var-power-off: "\f011"; -@fa-var-print: "\f02f"; -@fa-var-product-hunt: "\f288"; -@fa-var-puzzle-piece: "\f12e"; -@fa-var-qq: "\f1d6"; -@fa-var-qrcode: "\f029"; -@fa-var-question: "\f128"; -@fa-var-question-circle: "\f059"; -@fa-var-question-circle-o: "\f29c"; -@fa-var-quora: "\f2c4"; -@fa-var-quote-left: "\f10d"; -@fa-var-quote-right: "\f10e"; -@fa-var-ra: "\f1d0"; -@fa-var-random: "\f074"; -@fa-var-ravelry: "\f2d9"; -@fa-var-rebel: "\f1d0"; -@fa-var-recycle: "\f1b8"; -@fa-var-reddit: "\f1a1"; -@fa-var-reddit-alien: "\f281"; -@fa-var-reddit-square: "\f1a2"; -@fa-var-refresh: "\f021"; -@fa-var-registered: "\f25d"; -@fa-var-remove: "\f00d"; -@fa-var-renren: "\f18b"; -@fa-var-reorder: "\f0c9"; -@fa-var-repeat: "\f01e"; -@fa-var-reply: "\f112"; -@fa-var-reply-all: "\f122"; -@fa-var-resistance: "\f1d0"; -@fa-var-retweet: "\f079"; -@fa-var-rmb: "\f157"; -@fa-var-road: "\f018"; -@fa-var-rocket: "\f135"; -@fa-var-rotate-left: "\f0e2"; -@fa-var-rotate-right: "\f01e"; -@fa-var-rouble: "\f158"; -@fa-var-rss: "\f09e"; -@fa-var-rss-square: "\f143"; -@fa-var-rub: "\f158"; -@fa-var-ruble: "\f158"; -@fa-var-rupee: "\f156"; -@fa-var-s15: "\f2cd"; -@fa-var-safari: "\f267"; -@fa-var-save: "\f0c7"; -@fa-var-scissors: "\f0c4"; -@fa-var-scribd: "\f28a"; -@fa-var-search: "\f002"; -@fa-var-search-minus: "\f010"; -@fa-var-search-plus: "\f00e"; -@fa-var-sellsy: "\f213"; -@fa-var-send: "\f1d8"; -@fa-var-send-o: "\f1d9"; -@fa-var-server: "\f233"; -@fa-var-share: "\f064"; -@fa-var-share-alt: "\f1e0"; -@fa-var-share-alt-square: "\f1e1"; -@fa-var-share-square: "\f14d"; -@fa-var-share-square-o: "\f045"; -@fa-var-shekel: "\f20b"; -@fa-var-sheqel: "\f20b"; -@fa-var-shield: "\f132"; -@fa-var-ship: "\f21a"; -@fa-var-shirtsinbulk: "\f214"; -@fa-var-shopping-bag: "\f290"; -@fa-var-shopping-basket: "\f291"; -@fa-var-shopping-cart: "\f07a"; -@fa-var-shower: "\f2cc"; -@fa-var-sign-in: "\f090"; -@fa-var-sign-language: "\f2a7"; -@fa-var-sign-out: "\f08b"; -@fa-var-signal: "\f012"; -@fa-var-signing: "\f2a7"; -@fa-var-simplybuilt: "\f215"; -@fa-var-sitemap: "\f0e8"; -@fa-var-skyatlas: "\f216"; -@fa-var-skype: "\f17e"; -@fa-var-slack: "\f198"; -@fa-var-sliders: "\f1de"; -@fa-var-slideshare: "\f1e7"; -@fa-var-smile-o: "\f118"; -@fa-var-snapchat: "\f2ab"; -@fa-var-snapchat-ghost: "\f2ac"; -@fa-var-snapchat-square: "\f2ad"; -@fa-var-snowflake-o: "\f2dc"; -@fa-var-soccer-ball-o: "\f1e3"; -@fa-var-sort: "\f0dc"; -@fa-var-sort-alpha-asc: "\f15d"; -@fa-var-sort-alpha-desc: "\f15e"; -@fa-var-sort-amount-asc: "\f160"; -@fa-var-sort-amount-desc: "\f161"; -@fa-var-sort-asc: "\f0de"; -@fa-var-sort-desc: "\f0dd"; -@fa-var-sort-down: "\f0dd"; -@fa-var-sort-numeric-asc: "\f162"; -@fa-var-sort-numeric-desc: "\f163"; -@fa-var-sort-up: "\f0de"; -@fa-var-soundcloud: "\f1be"; -@fa-var-space-shuttle: "\f197"; -@fa-var-spinner: "\f110"; -@fa-var-spoon: "\f1b1"; -@fa-var-spotify: "\f1bc"; -@fa-var-square: "\f0c8"; -@fa-var-square-o: "\f096"; -@fa-var-stack-exchange: "\f18d"; -@fa-var-stack-overflow: "\f16c"; -@fa-var-star: "\f005"; -@fa-var-star-half: "\f089"; -@fa-var-star-half-empty: "\f123"; -@fa-var-star-half-full: "\f123"; -@fa-var-star-half-o: "\f123"; -@fa-var-star-o: "\f006"; -@fa-var-steam: "\f1b6"; -@fa-var-steam-square: "\f1b7"; -@fa-var-step-backward: "\f048"; -@fa-var-step-forward: "\f051"; -@fa-var-stethoscope: "\f0f1"; -@fa-var-sticky-note: "\f249"; -@fa-var-sticky-note-o: "\f24a"; -@fa-var-stop: "\f04d"; -@fa-var-stop-circle: "\f28d"; -@fa-var-stop-circle-o: "\f28e"; -@fa-var-street-view: "\f21d"; -@fa-var-strikethrough: "\f0cc"; -@fa-var-stumbleupon: "\f1a4"; -@fa-var-stumbleupon-circle: "\f1a3"; -@fa-var-subscript: "\f12c"; -@fa-var-subway: "\f239"; -@fa-var-suitcase: "\f0f2"; -@fa-var-sun-o: "\f185"; -@fa-var-superpowers: "\f2dd"; -@fa-var-superscript: "\f12b"; -@fa-var-support: "\f1cd"; -@fa-var-table: "\f0ce"; -@fa-var-tablet: "\f10a"; -@fa-var-tachometer: "\f0e4"; -@fa-var-tag: "\f02b"; -@fa-var-tags: "\f02c"; -@fa-var-tasks: "\f0ae"; -@fa-var-taxi: "\f1ba"; -@fa-var-telegram: "\f2c6"; -@fa-var-television: "\f26c"; -@fa-var-tencent-weibo: "\f1d5"; -@fa-var-terminal: "\f120"; -@fa-var-text-height: "\f034"; -@fa-var-text-width: "\f035"; -@fa-var-th: "\f00a"; -@fa-var-th-large: "\f009"; -@fa-var-th-list: "\f00b"; -@fa-var-themeisle: "\f2b2"; -@fa-var-thermometer: "\f2c7"; -@fa-var-thermometer-0: "\f2cb"; -@fa-var-thermometer-1: "\f2ca"; -@fa-var-thermometer-2: "\f2c9"; -@fa-var-thermometer-3: "\f2c8"; -@fa-var-thermometer-4: "\f2c7"; -@fa-var-thermometer-empty: "\f2cb"; -@fa-var-thermometer-full: "\f2c7"; -@fa-var-thermometer-half: "\f2c9"; -@fa-var-thermometer-quarter: "\f2ca"; -@fa-var-thermometer-three-quarters: "\f2c8"; -@fa-var-thumb-tack: "\f08d"; -@fa-var-thumbs-down: "\f165"; -@fa-var-thumbs-o-down: "\f088"; -@fa-var-thumbs-o-up: "\f087"; -@fa-var-thumbs-up: "\f164"; -@fa-var-ticket: "\f145"; -@fa-var-times: "\f00d"; -@fa-var-times-circle: "\f057"; -@fa-var-times-circle-o: "\f05c"; -@fa-var-times-rectangle: "\f2d3"; -@fa-var-times-rectangle-o: "\f2d4"; -@fa-var-tint: "\f043"; -@fa-var-toggle-down: "\f150"; -@fa-var-toggle-left: "\f191"; -@fa-var-toggle-off: "\f204"; -@fa-var-toggle-on: "\f205"; -@fa-var-toggle-right: "\f152"; -@fa-var-toggle-up: "\f151"; -@fa-var-trademark: "\f25c"; -@fa-var-train: "\f238"; -@fa-var-transgender: "\f224"; -@fa-var-transgender-alt: "\f225"; -@fa-var-trash: "\f1f8"; -@fa-var-trash-o: "\f014"; -@fa-var-tree: "\f1bb"; -@fa-var-trello: "\f181"; -@fa-var-tripadvisor: "\f262"; -@fa-var-trophy: "\f091"; -@fa-var-truck: "\f0d1"; -@fa-var-try: "\f195"; -@fa-var-tty: "\f1e4"; -@fa-var-tumblr: "\f173"; -@fa-var-tumblr-square: "\f174"; -@fa-var-turkish-lira: "\f195"; -@fa-var-tv: "\f26c"; -@fa-var-twitch: "\f1e8"; -@fa-var-twitter: "\f099"; -@fa-var-twitter-square: "\f081"; -@fa-var-umbrella: "\f0e9"; -@fa-var-underline: "\f0cd"; -@fa-var-undo: "\f0e2"; -@fa-var-universal-access: "\f29a"; -@fa-var-university: "\f19c"; -@fa-var-unlink: "\f127"; -@fa-var-unlock: "\f09c"; -@fa-var-unlock-alt: "\f13e"; -@fa-var-unsorted: "\f0dc"; -@fa-var-upload: "\f093"; -@fa-var-usb: "\f287"; -@fa-var-usd: "\f155"; -@fa-var-user: "\f007"; -@fa-var-user-circle: "\f2bd"; -@fa-var-user-circle-o: "\f2be"; -@fa-var-user-md: "\f0f0"; -@fa-var-user-o: "\f2c0"; -@fa-var-user-plus: "\f234"; -@fa-var-user-secret: "\f21b"; -@fa-var-user-times: "\f235"; -@fa-var-users: "\f0c0"; -@fa-var-vcard: "\f2bb"; -@fa-var-vcard-o: "\f2bc"; -@fa-var-venus: "\f221"; -@fa-var-venus-double: "\f226"; -@fa-var-venus-mars: "\f228"; -@fa-var-viacoin: "\f237"; -@fa-var-viadeo: "\f2a9"; -@fa-var-viadeo-square: "\f2aa"; -@fa-var-video-camera: "\f03d"; -@fa-var-vimeo: "\f27d"; -@fa-var-vimeo-square: "\f194"; -@fa-var-vine: "\f1ca"; -@fa-var-vk: "\f189"; -@fa-var-volume-control-phone: "\f2a0"; -@fa-var-volume-down: "\f027"; -@fa-var-volume-off: "\f026"; -@fa-var-volume-up: "\f028"; -@fa-var-warning: "\f071"; -@fa-var-wechat: "\f1d7"; -@fa-var-weibo: "\f18a"; -@fa-var-weixin: "\f1d7"; -@fa-var-whatsapp: "\f232"; -@fa-var-wheelchair: "\f193"; -@fa-var-wheelchair-alt: "\f29b"; -@fa-var-wifi: "\f1eb"; -@fa-var-wikipedia-w: "\f266"; -@fa-var-window-close: "\f2d3"; -@fa-var-window-close-o: "\f2d4"; -@fa-var-window-maximize: "\f2d0"; -@fa-var-window-minimize: "\f2d1"; -@fa-var-window-restore: "\f2d2"; -@fa-var-windows: "\f17a"; -@fa-var-won: "\f159"; -@fa-var-wordpress: "\f19a"; -@fa-var-wpbeginner: "\f297"; -@fa-var-wpexplorer: "\f2de"; -@fa-var-wpforms: "\f298"; -@fa-var-wrench: "\f0ad"; -@fa-var-xing: "\f168"; -@fa-var-xing-square: "\f169"; -@fa-var-y-combinator: "\f23b"; -@fa-var-y-combinator-square: "\f1d4"; -@fa-var-yahoo: "\f19e"; -@fa-var-yc: "\f23b"; -@fa-var-yc-square: "\f1d4"; -@fa-var-yelp: "\f1e9"; -@fa-var-yen: "\f157"; -@fa-var-yoast: "\f2b1"; -@fa-var-youtube: "\f167"; -@fa-var-youtube-play: "\f16a"; -@fa-var-youtube-square: "\f166"; - diff --git a/v3/css/font-awesome-4.7.0/scss/_animated.scss b/v3/css/font-awesome-4.7.0/scss/_animated.scss deleted file mode 100644 index 8a020db..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_animated.scss +++ /dev/null @@ -1,34 +0,0 @@ -// Spinning Icons -// -------------------------- - -.#{$fa-css-prefix}-spin { - -webkit-animation: fa-spin 2s infinite linear; - animation: fa-spin 2s infinite linear; -} - -.#{$fa-css-prefix}-pulse { - -webkit-animation: fa-spin 1s infinite steps(8); - animation: fa-spin 1s infinite steps(8); -} - -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} - -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} diff --git a/v3/css/font-awesome-4.7.0/scss/_bordered-pulled.scss b/v3/css/font-awesome-4.7.0/scss/_bordered-pulled.scss deleted file mode 100644 index d4b85a0..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_bordered-pulled.scss +++ /dev/null @@ -1,25 +0,0 @@ -// Bordered & Pulled -// ------------------------- - -.#{$fa-css-prefix}-border { - padding: .2em .25em .15em; - border: solid .08em $fa-border-color; - border-radius: .1em; -} - -.#{$fa-css-prefix}-pull-left { float: left; } -.#{$fa-css-prefix}-pull-right { float: right; } - -.#{$fa-css-prefix} { - &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } - &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } -} - -/* Deprecated as of 4.4.0 */ -.pull-right { float: right; } -.pull-left { float: left; } - -.#{$fa-css-prefix} { - &.pull-left { margin-right: .3em; } - &.pull-right { margin-left: .3em; } -} diff --git a/v3/css/font-awesome-4.7.0/scss/_core.scss b/v3/css/font-awesome-4.7.0/scss/_core.scss deleted file mode 100644 index 7425ef8..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_core.scss +++ /dev/null @@ -1,12 +0,0 @@ -// Base Class Definition -// ------------------------- - -.#{$fa-css-prefix} { - display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration - font-size: inherit; // can't have font-size inherit on line above, so need to override - text-rendering: auto; // optimizelegibility throws things off #1094 - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -} diff --git a/v3/css/font-awesome-4.7.0/scss/_fixed-width.scss b/v3/css/font-awesome-4.7.0/scss/_fixed-width.scss deleted file mode 100644 index b221c98..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_fixed-width.scss +++ /dev/null @@ -1,6 +0,0 @@ -// Fixed Width Icons -// ------------------------- -.#{$fa-css-prefix}-fw { - width: (18em / 14); - text-align: center; -} diff --git a/v3/css/font-awesome-4.7.0/scss/_icons.scss b/v3/css/font-awesome-4.7.0/scss/_icons.scss deleted file mode 100644 index e63e702..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_icons.scss +++ /dev/null @@ -1,789 +0,0 @@ -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ - -.#{$fa-css-prefix}-glass:before { content: $fa-var-glass; } -.#{$fa-css-prefix}-music:before { content: $fa-var-music; } -.#{$fa-css-prefix}-search:before { content: $fa-var-search; } -.#{$fa-css-prefix}-envelope-o:before { content: $fa-var-envelope-o; } -.#{$fa-css-prefix}-heart:before { content: $fa-var-heart; } -.#{$fa-css-prefix}-star:before { content: $fa-var-star; } -.#{$fa-css-prefix}-star-o:before { content: $fa-var-star-o; } -.#{$fa-css-prefix}-user:before { content: $fa-var-user; } -.#{$fa-css-prefix}-film:before { content: $fa-var-film; } -.#{$fa-css-prefix}-th-large:before { content: $fa-var-th-large; } -.#{$fa-css-prefix}-th:before { content: $fa-var-th; } -.#{$fa-css-prefix}-th-list:before { content: $fa-var-th-list; } -.#{$fa-css-prefix}-check:before { content: $fa-var-check; } -.#{$fa-css-prefix}-remove:before, -.#{$fa-css-prefix}-close:before, -.#{$fa-css-prefix}-times:before { content: $fa-var-times; } -.#{$fa-css-prefix}-search-plus:before { content: $fa-var-search-plus; } -.#{$fa-css-prefix}-search-minus:before { content: $fa-var-search-minus; } -.#{$fa-css-prefix}-power-off:before { content: $fa-var-power-off; } -.#{$fa-css-prefix}-signal:before { content: $fa-var-signal; } -.#{$fa-css-prefix}-gear:before, -.#{$fa-css-prefix}-cog:before { content: $fa-var-cog; } -.#{$fa-css-prefix}-trash-o:before { content: $fa-var-trash-o; } -.#{$fa-css-prefix}-home:before { content: $fa-var-home; } -.#{$fa-css-prefix}-file-o:before { content: $fa-var-file-o; } -.#{$fa-css-prefix}-clock-o:before { content: $fa-var-clock-o; } -.#{$fa-css-prefix}-road:before { content: $fa-var-road; } -.#{$fa-css-prefix}-download:before { content: $fa-var-download; } -.#{$fa-css-prefix}-arrow-circle-o-down:before { content: $fa-var-arrow-circle-o-down; } -.#{$fa-css-prefix}-arrow-circle-o-up:before { content: $fa-var-arrow-circle-o-up; } -.#{$fa-css-prefix}-inbox:before { content: $fa-var-inbox; } -.#{$fa-css-prefix}-play-circle-o:before { content: $fa-var-play-circle-o; } -.#{$fa-css-prefix}-rotate-right:before, -.#{$fa-css-prefix}-repeat:before { content: $fa-var-repeat; } -.#{$fa-css-prefix}-refresh:before { content: $fa-var-refresh; } -.#{$fa-css-prefix}-list-alt:before { content: $fa-var-list-alt; } -.#{$fa-css-prefix}-lock:before { content: $fa-var-lock; } -.#{$fa-css-prefix}-flag:before { content: $fa-var-flag; } -.#{$fa-css-prefix}-headphones:before { content: $fa-var-headphones; } -.#{$fa-css-prefix}-volume-off:before { content: $fa-var-volume-off; } -.#{$fa-css-prefix}-volume-down:before { content: $fa-var-volume-down; } -.#{$fa-css-prefix}-volume-up:before { content: $fa-var-volume-up; } -.#{$fa-css-prefix}-qrcode:before { content: $fa-var-qrcode; } -.#{$fa-css-prefix}-barcode:before { content: $fa-var-barcode; } -.#{$fa-css-prefix}-tag:before { content: $fa-var-tag; } -.#{$fa-css-prefix}-tags:before { content: $fa-var-tags; } -.#{$fa-css-prefix}-book:before { content: $fa-var-book; } -.#{$fa-css-prefix}-bookmark:before { content: $fa-var-bookmark; } -.#{$fa-css-prefix}-print:before { content: $fa-var-print; } -.#{$fa-css-prefix}-camera:before { content: $fa-var-camera; } -.#{$fa-css-prefix}-font:before { content: $fa-var-font; } -.#{$fa-css-prefix}-bold:before { content: $fa-var-bold; } -.#{$fa-css-prefix}-italic:before { content: $fa-var-italic; } -.#{$fa-css-prefix}-text-height:before { content: $fa-var-text-height; } -.#{$fa-css-prefix}-text-width:before { content: $fa-var-text-width; } -.#{$fa-css-prefix}-align-left:before { content: $fa-var-align-left; } -.#{$fa-css-prefix}-align-center:before { content: $fa-var-align-center; } -.#{$fa-css-prefix}-align-right:before { content: $fa-var-align-right; } -.#{$fa-css-prefix}-align-justify:before { content: $fa-var-align-justify; } -.#{$fa-css-prefix}-list:before { content: $fa-var-list; } -.#{$fa-css-prefix}-dedent:before, -.#{$fa-css-prefix}-outdent:before { content: $fa-var-outdent; } -.#{$fa-css-prefix}-indent:before { content: $fa-var-indent; } -.#{$fa-css-prefix}-video-camera:before { content: $fa-var-video-camera; } -.#{$fa-css-prefix}-photo:before, -.#{$fa-css-prefix}-image:before, -.#{$fa-css-prefix}-picture-o:before { content: $fa-var-picture-o; } -.#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; } -.#{$fa-css-prefix}-map-marker:before { content: $fa-var-map-marker; } -.#{$fa-css-prefix}-adjust:before { content: $fa-var-adjust; } -.#{$fa-css-prefix}-tint:before { content: $fa-var-tint; } -.#{$fa-css-prefix}-edit:before, -.#{$fa-css-prefix}-pencil-square-o:before { content: $fa-var-pencil-square-o; } -.#{$fa-css-prefix}-share-square-o:before { content: $fa-var-share-square-o; } -.#{$fa-css-prefix}-check-square-o:before { content: $fa-var-check-square-o; } -.#{$fa-css-prefix}-arrows:before { content: $fa-var-arrows; } -.#{$fa-css-prefix}-step-backward:before { content: $fa-var-step-backward; } -.#{$fa-css-prefix}-fast-backward:before { content: $fa-var-fast-backward; } -.#{$fa-css-prefix}-backward:before { content: $fa-var-backward; } -.#{$fa-css-prefix}-play:before { content: $fa-var-play; } -.#{$fa-css-prefix}-pause:before { content: $fa-var-pause; } -.#{$fa-css-prefix}-stop:before { content: $fa-var-stop; } -.#{$fa-css-prefix}-forward:before { content: $fa-var-forward; } -.#{$fa-css-prefix}-fast-forward:before { content: $fa-var-fast-forward; } -.#{$fa-css-prefix}-step-forward:before { content: $fa-var-step-forward; } -.#{$fa-css-prefix}-eject:before { content: $fa-var-eject; } -.#{$fa-css-prefix}-chevron-left:before { content: $fa-var-chevron-left; } -.#{$fa-css-prefix}-chevron-right:before { content: $fa-var-chevron-right; } -.#{$fa-css-prefix}-plus-circle:before { content: $fa-var-plus-circle; } -.#{$fa-css-prefix}-minus-circle:before { content: $fa-var-minus-circle; } -.#{$fa-css-prefix}-times-circle:before { content: $fa-var-times-circle; } -.#{$fa-css-prefix}-check-circle:before { content: $fa-var-check-circle; } -.#{$fa-css-prefix}-question-circle:before { content: $fa-var-question-circle; } -.#{$fa-css-prefix}-info-circle:before { content: $fa-var-info-circle; } -.#{$fa-css-prefix}-crosshairs:before { content: $fa-var-crosshairs; } -.#{$fa-css-prefix}-times-circle-o:before { content: $fa-var-times-circle-o; } -.#{$fa-css-prefix}-check-circle-o:before { content: $fa-var-check-circle-o; } -.#{$fa-css-prefix}-ban:before { content: $fa-var-ban; } -.#{$fa-css-prefix}-arrow-left:before { content: $fa-var-arrow-left; } -.#{$fa-css-prefix}-arrow-right:before { content: $fa-var-arrow-right; } -.#{$fa-css-prefix}-arrow-up:before { content: $fa-var-arrow-up; } -.#{$fa-css-prefix}-arrow-down:before { content: $fa-var-arrow-down; } -.#{$fa-css-prefix}-mail-forward:before, -.#{$fa-css-prefix}-share:before { content: $fa-var-share; } -.#{$fa-css-prefix}-expand:before { content: $fa-var-expand; } -.#{$fa-css-prefix}-compress:before { content: $fa-var-compress; } -.#{$fa-css-prefix}-plus:before { content: $fa-var-plus; } -.#{$fa-css-prefix}-minus:before { content: $fa-var-minus; } -.#{$fa-css-prefix}-asterisk:before { content: $fa-var-asterisk; } -.#{$fa-css-prefix}-exclamation-circle:before { content: $fa-var-exclamation-circle; } -.#{$fa-css-prefix}-gift:before { content: $fa-var-gift; } -.#{$fa-css-prefix}-leaf:before { content: $fa-var-leaf; } -.#{$fa-css-prefix}-fire:before { content: $fa-var-fire; } -.#{$fa-css-prefix}-eye:before { content: $fa-var-eye; } -.#{$fa-css-prefix}-eye-slash:before { content: $fa-var-eye-slash; } -.#{$fa-css-prefix}-warning:before, -.#{$fa-css-prefix}-exclamation-triangle:before { content: $fa-var-exclamation-triangle; } -.#{$fa-css-prefix}-plane:before { content: $fa-var-plane; } -.#{$fa-css-prefix}-calendar:before { content: $fa-var-calendar; } -.#{$fa-css-prefix}-random:before { content: $fa-var-random; } -.#{$fa-css-prefix}-comment:before { content: $fa-var-comment; } -.#{$fa-css-prefix}-magnet:before { content: $fa-var-magnet; } -.#{$fa-css-prefix}-chevron-up:before { content: $fa-var-chevron-up; } -.#{$fa-css-prefix}-chevron-down:before { content: $fa-var-chevron-down; } -.#{$fa-css-prefix}-retweet:before { content: $fa-var-retweet; } -.#{$fa-css-prefix}-shopping-cart:before { content: $fa-var-shopping-cart; } -.#{$fa-css-prefix}-folder:before { content: $fa-var-folder; } -.#{$fa-css-prefix}-folder-open:before { content: $fa-var-folder-open; } -.#{$fa-css-prefix}-arrows-v:before { content: $fa-var-arrows-v; } -.#{$fa-css-prefix}-arrows-h:before { content: $fa-var-arrows-h; } -.#{$fa-css-prefix}-bar-chart-o:before, -.#{$fa-css-prefix}-bar-chart:before { content: $fa-var-bar-chart; } -.#{$fa-css-prefix}-twitter-square:before { content: $fa-var-twitter-square; } -.#{$fa-css-prefix}-facebook-square:before { content: $fa-var-facebook-square; } -.#{$fa-css-prefix}-camera-retro:before { content: $fa-var-camera-retro; } -.#{$fa-css-prefix}-key:before { content: $fa-var-key; } -.#{$fa-css-prefix}-gears:before, -.#{$fa-css-prefix}-cogs:before { content: $fa-var-cogs; } -.#{$fa-css-prefix}-comments:before { content: $fa-var-comments; } -.#{$fa-css-prefix}-thumbs-o-up:before { content: $fa-var-thumbs-o-up; } -.#{$fa-css-prefix}-thumbs-o-down:before { content: $fa-var-thumbs-o-down; } -.#{$fa-css-prefix}-star-half:before { content: $fa-var-star-half; } -.#{$fa-css-prefix}-heart-o:before { content: $fa-var-heart-o; } -.#{$fa-css-prefix}-sign-out:before { content: $fa-var-sign-out; } -.#{$fa-css-prefix}-linkedin-square:before { content: $fa-var-linkedin-square; } -.#{$fa-css-prefix}-thumb-tack:before { content: $fa-var-thumb-tack; } -.#{$fa-css-prefix}-external-link:before { content: $fa-var-external-link; } -.#{$fa-css-prefix}-sign-in:before { content: $fa-var-sign-in; } -.#{$fa-css-prefix}-trophy:before { content: $fa-var-trophy; } -.#{$fa-css-prefix}-github-square:before { content: $fa-var-github-square; } -.#{$fa-css-prefix}-upload:before { content: $fa-var-upload; } -.#{$fa-css-prefix}-lemon-o:before { content: $fa-var-lemon-o; } -.#{$fa-css-prefix}-phone:before { content: $fa-var-phone; } -.#{$fa-css-prefix}-square-o:before { content: $fa-var-square-o; } -.#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; } -.#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; } -.#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; } -.#{$fa-css-prefix}-facebook-f:before, -.#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; } -.#{$fa-css-prefix}-github:before { content: $fa-var-github; } -.#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; } -.#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; } -.#{$fa-css-prefix}-feed:before, -.#{$fa-css-prefix}-rss:before { content: $fa-var-rss; } -.#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; } -.#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; } -.#{$fa-css-prefix}-bell:before { content: $fa-var-bell; } -.#{$fa-css-prefix}-certificate:before { content: $fa-var-certificate; } -.#{$fa-css-prefix}-hand-o-right:before { content: $fa-var-hand-o-right; } -.#{$fa-css-prefix}-hand-o-left:before { content: $fa-var-hand-o-left; } -.#{$fa-css-prefix}-hand-o-up:before { content: $fa-var-hand-o-up; } -.#{$fa-css-prefix}-hand-o-down:before { content: $fa-var-hand-o-down; } -.#{$fa-css-prefix}-arrow-circle-left:before { content: $fa-var-arrow-circle-left; } -.#{$fa-css-prefix}-arrow-circle-right:before { content: $fa-var-arrow-circle-right; } -.#{$fa-css-prefix}-arrow-circle-up:before { content: $fa-var-arrow-circle-up; } -.#{$fa-css-prefix}-arrow-circle-down:before { content: $fa-var-arrow-circle-down; } -.#{$fa-css-prefix}-globe:before { content: $fa-var-globe; } -.#{$fa-css-prefix}-wrench:before { content: $fa-var-wrench; } -.#{$fa-css-prefix}-tasks:before { content: $fa-var-tasks; } -.#{$fa-css-prefix}-filter:before { content: $fa-var-filter; } -.#{$fa-css-prefix}-briefcase:before { content: $fa-var-briefcase; } -.#{$fa-css-prefix}-arrows-alt:before { content: $fa-var-arrows-alt; } -.#{$fa-css-prefix}-group:before, -.#{$fa-css-prefix}-users:before { content: $fa-var-users; } -.#{$fa-css-prefix}-chain:before, -.#{$fa-css-prefix}-link:before { content: $fa-var-link; } -.#{$fa-css-prefix}-cloud:before { content: $fa-var-cloud; } -.#{$fa-css-prefix}-flask:before { content: $fa-var-flask; } -.#{$fa-css-prefix}-cut:before, -.#{$fa-css-prefix}-scissors:before { content: $fa-var-scissors; } -.#{$fa-css-prefix}-copy:before, -.#{$fa-css-prefix}-files-o:before { content: $fa-var-files-o; } -.#{$fa-css-prefix}-paperclip:before { content: $fa-var-paperclip; } -.#{$fa-css-prefix}-save:before, -.#{$fa-css-prefix}-floppy-o:before { content: $fa-var-floppy-o; } -.#{$fa-css-prefix}-square:before { content: $fa-var-square; } -.#{$fa-css-prefix}-navicon:before, -.#{$fa-css-prefix}-reorder:before, -.#{$fa-css-prefix}-bars:before { content: $fa-var-bars; } -.#{$fa-css-prefix}-list-ul:before { content: $fa-var-list-ul; } -.#{$fa-css-prefix}-list-ol:before { content: $fa-var-list-ol; } -.#{$fa-css-prefix}-strikethrough:before { content: $fa-var-strikethrough; } -.#{$fa-css-prefix}-underline:before { content: $fa-var-underline; } -.#{$fa-css-prefix}-table:before { content: $fa-var-table; } -.#{$fa-css-prefix}-magic:before { content: $fa-var-magic; } -.#{$fa-css-prefix}-truck:before { content: $fa-var-truck; } -.#{$fa-css-prefix}-pinterest:before { content: $fa-var-pinterest; } -.#{$fa-css-prefix}-pinterest-square:before { content: $fa-var-pinterest-square; } -.#{$fa-css-prefix}-google-plus-square:before { content: $fa-var-google-plus-square; } -.#{$fa-css-prefix}-google-plus:before { content: $fa-var-google-plus; } -.#{$fa-css-prefix}-money:before { content: $fa-var-money; } -.#{$fa-css-prefix}-caret-down:before { content: $fa-var-caret-down; } -.#{$fa-css-prefix}-caret-up:before { content: $fa-var-caret-up; } -.#{$fa-css-prefix}-caret-left:before { content: $fa-var-caret-left; } -.#{$fa-css-prefix}-caret-right:before { content: $fa-var-caret-right; } -.#{$fa-css-prefix}-columns:before { content: $fa-var-columns; } -.#{$fa-css-prefix}-unsorted:before, -.#{$fa-css-prefix}-sort:before { content: $fa-var-sort; } -.#{$fa-css-prefix}-sort-down:before, -.#{$fa-css-prefix}-sort-desc:before { content: $fa-var-sort-desc; } -.#{$fa-css-prefix}-sort-up:before, -.#{$fa-css-prefix}-sort-asc:before { content: $fa-var-sort-asc; } -.#{$fa-css-prefix}-envelope:before { content: $fa-var-envelope; } -.#{$fa-css-prefix}-linkedin:before { content: $fa-var-linkedin; } -.#{$fa-css-prefix}-rotate-left:before, -.#{$fa-css-prefix}-undo:before { content: $fa-var-undo; } -.#{$fa-css-prefix}-legal:before, -.#{$fa-css-prefix}-gavel:before { content: $fa-var-gavel; } -.#{$fa-css-prefix}-dashboard:before, -.#{$fa-css-prefix}-tachometer:before { content: $fa-var-tachometer; } -.#{$fa-css-prefix}-comment-o:before { content: $fa-var-comment-o; } -.#{$fa-css-prefix}-comments-o:before { content: $fa-var-comments-o; } -.#{$fa-css-prefix}-flash:before, -.#{$fa-css-prefix}-bolt:before { content: $fa-var-bolt; } -.#{$fa-css-prefix}-sitemap:before { content: $fa-var-sitemap; } -.#{$fa-css-prefix}-umbrella:before { content: $fa-var-umbrella; } -.#{$fa-css-prefix}-paste:before, -.#{$fa-css-prefix}-clipboard:before { content: $fa-var-clipboard; } -.#{$fa-css-prefix}-lightbulb-o:before { content: $fa-var-lightbulb-o; } -.#{$fa-css-prefix}-exchange:before { content: $fa-var-exchange; } -.#{$fa-css-prefix}-cloud-download:before { content: $fa-var-cloud-download; } -.#{$fa-css-prefix}-cloud-upload:before { content: $fa-var-cloud-upload; } -.#{$fa-css-prefix}-user-md:before { content: $fa-var-user-md; } -.#{$fa-css-prefix}-stethoscope:before { content: $fa-var-stethoscope; } -.#{$fa-css-prefix}-suitcase:before { content: $fa-var-suitcase; } -.#{$fa-css-prefix}-bell-o:before { content: $fa-var-bell-o; } -.#{$fa-css-prefix}-coffee:before { content: $fa-var-coffee; } -.#{$fa-css-prefix}-cutlery:before { content: $fa-var-cutlery; } -.#{$fa-css-prefix}-file-text-o:before { content: $fa-var-file-text-o; } -.#{$fa-css-prefix}-building-o:before { content: $fa-var-building-o; } -.#{$fa-css-prefix}-hospital-o:before { content: $fa-var-hospital-o; } -.#{$fa-css-prefix}-ambulance:before { content: $fa-var-ambulance; } -.#{$fa-css-prefix}-medkit:before { content: $fa-var-medkit; } -.#{$fa-css-prefix}-fighter-jet:before { content: $fa-var-fighter-jet; } -.#{$fa-css-prefix}-beer:before { content: $fa-var-beer; } -.#{$fa-css-prefix}-h-square:before { content: $fa-var-h-square; } -.#{$fa-css-prefix}-plus-square:before { content: $fa-var-plus-square; } -.#{$fa-css-prefix}-angle-double-left:before { content: $fa-var-angle-double-left; } -.#{$fa-css-prefix}-angle-double-right:before { content: $fa-var-angle-double-right; } -.#{$fa-css-prefix}-angle-double-up:before { content: $fa-var-angle-double-up; } -.#{$fa-css-prefix}-angle-double-down:before { content: $fa-var-angle-double-down; } -.#{$fa-css-prefix}-angle-left:before { content: $fa-var-angle-left; } -.#{$fa-css-prefix}-angle-right:before { content: $fa-var-angle-right; } -.#{$fa-css-prefix}-angle-up:before { content: $fa-var-angle-up; } -.#{$fa-css-prefix}-angle-down:before { content: $fa-var-angle-down; } -.#{$fa-css-prefix}-desktop:before { content: $fa-var-desktop; } -.#{$fa-css-prefix}-laptop:before { content: $fa-var-laptop; } -.#{$fa-css-prefix}-tablet:before { content: $fa-var-tablet; } -.#{$fa-css-prefix}-mobile-phone:before, -.#{$fa-css-prefix}-mobile:before { content: $fa-var-mobile; } -.#{$fa-css-prefix}-circle-o:before { content: $fa-var-circle-o; } -.#{$fa-css-prefix}-quote-left:before { content: $fa-var-quote-left; } -.#{$fa-css-prefix}-quote-right:before { content: $fa-var-quote-right; } -.#{$fa-css-prefix}-spinner:before { content: $fa-var-spinner; } -.#{$fa-css-prefix}-circle:before { content: $fa-var-circle; } -.#{$fa-css-prefix}-mail-reply:before, -.#{$fa-css-prefix}-reply:before { content: $fa-var-reply; } -.#{$fa-css-prefix}-github-alt:before { content: $fa-var-github-alt; } -.#{$fa-css-prefix}-folder-o:before { content: $fa-var-folder-o; } -.#{$fa-css-prefix}-folder-open-o:before { content: $fa-var-folder-open-o; } -.#{$fa-css-prefix}-smile-o:before { content: $fa-var-smile-o; } -.#{$fa-css-prefix}-frown-o:before { content: $fa-var-frown-o; } -.#{$fa-css-prefix}-meh-o:before { content: $fa-var-meh-o; } -.#{$fa-css-prefix}-gamepad:before { content: $fa-var-gamepad; } -.#{$fa-css-prefix}-keyboard-o:before { content: $fa-var-keyboard-o; } -.#{$fa-css-prefix}-flag-o:before { content: $fa-var-flag-o; } -.#{$fa-css-prefix}-flag-checkered:before { content: $fa-var-flag-checkered; } -.#{$fa-css-prefix}-terminal:before { content: $fa-var-terminal; } -.#{$fa-css-prefix}-code:before { content: $fa-var-code; } -.#{$fa-css-prefix}-mail-reply-all:before, -.#{$fa-css-prefix}-reply-all:before { content: $fa-var-reply-all; } -.#{$fa-css-prefix}-star-half-empty:before, -.#{$fa-css-prefix}-star-half-full:before, -.#{$fa-css-prefix}-star-half-o:before { content: $fa-var-star-half-o; } -.#{$fa-css-prefix}-location-arrow:before { content: $fa-var-location-arrow; } -.#{$fa-css-prefix}-crop:before { content: $fa-var-crop; } -.#{$fa-css-prefix}-code-fork:before { content: $fa-var-code-fork; } -.#{$fa-css-prefix}-unlink:before, -.#{$fa-css-prefix}-chain-broken:before { content: $fa-var-chain-broken; } -.#{$fa-css-prefix}-question:before { content: $fa-var-question; } -.#{$fa-css-prefix}-info:before { content: $fa-var-info; } -.#{$fa-css-prefix}-exclamation:before { content: $fa-var-exclamation; } -.#{$fa-css-prefix}-superscript:before { content: $fa-var-superscript; } -.#{$fa-css-prefix}-subscript:before { content: $fa-var-subscript; } -.#{$fa-css-prefix}-eraser:before { content: $fa-var-eraser; } -.#{$fa-css-prefix}-puzzle-piece:before { content: $fa-var-puzzle-piece; } -.#{$fa-css-prefix}-microphone:before { content: $fa-var-microphone; } -.#{$fa-css-prefix}-microphone-slash:before { content: $fa-var-microphone-slash; } -.#{$fa-css-prefix}-shield:before { content: $fa-var-shield; } -.#{$fa-css-prefix}-calendar-o:before { content: $fa-var-calendar-o; } -.#{$fa-css-prefix}-fire-extinguisher:before { content: $fa-var-fire-extinguisher; } -.#{$fa-css-prefix}-rocket:before { content: $fa-var-rocket; } -.#{$fa-css-prefix}-maxcdn:before { content: $fa-var-maxcdn; } -.#{$fa-css-prefix}-chevron-circle-left:before { content: $fa-var-chevron-circle-left; } -.#{$fa-css-prefix}-chevron-circle-right:before { content: $fa-var-chevron-circle-right; } -.#{$fa-css-prefix}-chevron-circle-up:before { content: $fa-var-chevron-circle-up; } -.#{$fa-css-prefix}-chevron-circle-down:before { content: $fa-var-chevron-circle-down; } -.#{$fa-css-prefix}-html5:before { content: $fa-var-html5; } -.#{$fa-css-prefix}-css3:before { content: $fa-var-css3; } -.#{$fa-css-prefix}-anchor:before { content: $fa-var-anchor; } -.#{$fa-css-prefix}-unlock-alt:before { content: $fa-var-unlock-alt; } -.#{$fa-css-prefix}-bullseye:before { content: $fa-var-bullseye; } -.#{$fa-css-prefix}-ellipsis-h:before { content: $fa-var-ellipsis-h; } -.#{$fa-css-prefix}-ellipsis-v:before { content: $fa-var-ellipsis-v; } -.#{$fa-css-prefix}-rss-square:before { content: $fa-var-rss-square; } -.#{$fa-css-prefix}-play-circle:before { content: $fa-var-play-circle; } -.#{$fa-css-prefix}-ticket:before { content: $fa-var-ticket; } -.#{$fa-css-prefix}-minus-square:before { content: $fa-var-minus-square; } -.#{$fa-css-prefix}-minus-square-o:before { content: $fa-var-minus-square-o; } -.#{$fa-css-prefix}-level-up:before { content: $fa-var-level-up; } -.#{$fa-css-prefix}-level-down:before { content: $fa-var-level-down; } -.#{$fa-css-prefix}-check-square:before { content: $fa-var-check-square; } -.#{$fa-css-prefix}-pencil-square:before { content: $fa-var-pencil-square; } -.#{$fa-css-prefix}-external-link-square:before { content: $fa-var-external-link-square; } -.#{$fa-css-prefix}-share-square:before { content: $fa-var-share-square; } -.#{$fa-css-prefix}-compass:before { content: $fa-var-compass; } -.#{$fa-css-prefix}-toggle-down:before, -.#{$fa-css-prefix}-caret-square-o-down:before { content: $fa-var-caret-square-o-down; } -.#{$fa-css-prefix}-toggle-up:before, -.#{$fa-css-prefix}-caret-square-o-up:before { content: $fa-var-caret-square-o-up; } -.#{$fa-css-prefix}-toggle-right:before, -.#{$fa-css-prefix}-caret-square-o-right:before { content: $fa-var-caret-square-o-right; } -.#{$fa-css-prefix}-euro:before, -.#{$fa-css-prefix}-eur:before { content: $fa-var-eur; } -.#{$fa-css-prefix}-gbp:before { content: $fa-var-gbp; } -.#{$fa-css-prefix}-dollar:before, -.#{$fa-css-prefix}-usd:before { content: $fa-var-usd; } -.#{$fa-css-prefix}-rupee:before, -.#{$fa-css-prefix}-inr:before { content: $fa-var-inr; } -.#{$fa-css-prefix}-cny:before, -.#{$fa-css-prefix}-rmb:before, -.#{$fa-css-prefix}-yen:before, -.#{$fa-css-prefix}-jpy:before { content: $fa-var-jpy; } -.#{$fa-css-prefix}-ruble:before, -.#{$fa-css-prefix}-rouble:before, -.#{$fa-css-prefix}-rub:before { content: $fa-var-rub; } -.#{$fa-css-prefix}-won:before, -.#{$fa-css-prefix}-krw:before { content: $fa-var-krw; } -.#{$fa-css-prefix}-bitcoin:before, -.#{$fa-css-prefix}-btc:before { content: $fa-var-btc; } -.#{$fa-css-prefix}-file:before { content: $fa-var-file; } -.#{$fa-css-prefix}-file-text:before { content: $fa-var-file-text; } -.#{$fa-css-prefix}-sort-alpha-asc:before { content: $fa-var-sort-alpha-asc; } -.#{$fa-css-prefix}-sort-alpha-desc:before { content: $fa-var-sort-alpha-desc; } -.#{$fa-css-prefix}-sort-amount-asc:before { content: $fa-var-sort-amount-asc; } -.#{$fa-css-prefix}-sort-amount-desc:before { content: $fa-var-sort-amount-desc; } -.#{$fa-css-prefix}-sort-numeric-asc:before { content: $fa-var-sort-numeric-asc; } -.#{$fa-css-prefix}-sort-numeric-desc:before { content: $fa-var-sort-numeric-desc; } -.#{$fa-css-prefix}-thumbs-up:before { content: $fa-var-thumbs-up; } -.#{$fa-css-prefix}-thumbs-down:before { content: $fa-var-thumbs-down; } -.#{$fa-css-prefix}-youtube-square:before { content: $fa-var-youtube-square; } -.#{$fa-css-prefix}-youtube:before { content: $fa-var-youtube; } -.#{$fa-css-prefix}-xing:before { content: $fa-var-xing; } -.#{$fa-css-prefix}-xing-square:before { content: $fa-var-xing-square; } -.#{$fa-css-prefix}-youtube-play:before { content: $fa-var-youtube-play; } -.#{$fa-css-prefix}-dropbox:before { content: $fa-var-dropbox; } -.#{$fa-css-prefix}-stack-overflow:before { content: $fa-var-stack-overflow; } -.#{$fa-css-prefix}-instagram:before { content: $fa-var-instagram; } -.#{$fa-css-prefix}-flickr:before { content: $fa-var-flickr; } -.#{$fa-css-prefix}-adn:before { content: $fa-var-adn; } -.#{$fa-css-prefix}-bitbucket:before { content: $fa-var-bitbucket; } -.#{$fa-css-prefix}-bitbucket-square:before { content: $fa-var-bitbucket-square; } -.#{$fa-css-prefix}-tumblr:before { content: $fa-var-tumblr; } -.#{$fa-css-prefix}-tumblr-square:before { content: $fa-var-tumblr-square; } -.#{$fa-css-prefix}-long-arrow-down:before { content: $fa-var-long-arrow-down; } -.#{$fa-css-prefix}-long-arrow-up:before { content: $fa-var-long-arrow-up; } -.#{$fa-css-prefix}-long-arrow-left:before { content: $fa-var-long-arrow-left; } -.#{$fa-css-prefix}-long-arrow-right:before { content: $fa-var-long-arrow-right; } -.#{$fa-css-prefix}-apple:before { content: $fa-var-apple; } -.#{$fa-css-prefix}-windows:before { content: $fa-var-windows; } -.#{$fa-css-prefix}-android:before { content: $fa-var-android; } -.#{$fa-css-prefix}-linux:before { content: $fa-var-linux; } -.#{$fa-css-prefix}-dribbble:before { content: $fa-var-dribbble; } -.#{$fa-css-prefix}-skype:before { content: $fa-var-skype; } -.#{$fa-css-prefix}-foursquare:before { content: $fa-var-foursquare; } -.#{$fa-css-prefix}-trello:before { content: $fa-var-trello; } -.#{$fa-css-prefix}-female:before { content: $fa-var-female; } -.#{$fa-css-prefix}-male:before { content: $fa-var-male; } -.#{$fa-css-prefix}-gittip:before, -.#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; } -.#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; } -.#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; } -.#{$fa-css-prefix}-archive:before { content: $fa-var-archive; } -.#{$fa-css-prefix}-bug:before { content: $fa-var-bug; } -.#{$fa-css-prefix}-vk:before { content: $fa-var-vk; } -.#{$fa-css-prefix}-weibo:before { content: $fa-var-weibo; } -.#{$fa-css-prefix}-renren:before { content: $fa-var-renren; } -.#{$fa-css-prefix}-pagelines:before { content: $fa-var-pagelines; } -.#{$fa-css-prefix}-stack-exchange:before { content: $fa-var-stack-exchange; } -.#{$fa-css-prefix}-arrow-circle-o-right:before { content: $fa-var-arrow-circle-o-right; } -.#{$fa-css-prefix}-arrow-circle-o-left:before { content: $fa-var-arrow-circle-o-left; } -.#{$fa-css-prefix}-toggle-left:before, -.#{$fa-css-prefix}-caret-square-o-left:before { content: $fa-var-caret-square-o-left; } -.#{$fa-css-prefix}-dot-circle-o:before { content: $fa-var-dot-circle-o; } -.#{$fa-css-prefix}-wheelchair:before { content: $fa-var-wheelchair; } -.#{$fa-css-prefix}-vimeo-square:before { content: $fa-var-vimeo-square; } -.#{$fa-css-prefix}-turkish-lira:before, -.#{$fa-css-prefix}-try:before { content: $fa-var-try; } -.#{$fa-css-prefix}-plus-square-o:before { content: $fa-var-plus-square-o; } -.#{$fa-css-prefix}-space-shuttle:before { content: $fa-var-space-shuttle; } -.#{$fa-css-prefix}-slack:before { content: $fa-var-slack; } -.#{$fa-css-prefix}-envelope-square:before { content: $fa-var-envelope-square; } -.#{$fa-css-prefix}-wordpress:before { content: $fa-var-wordpress; } -.#{$fa-css-prefix}-openid:before { content: $fa-var-openid; } -.#{$fa-css-prefix}-institution:before, -.#{$fa-css-prefix}-bank:before, -.#{$fa-css-prefix}-university:before { content: $fa-var-university; } -.#{$fa-css-prefix}-mortar-board:before, -.#{$fa-css-prefix}-graduation-cap:before { content: $fa-var-graduation-cap; } -.#{$fa-css-prefix}-yahoo:before { content: $fa-var-yahoo; } -.#{$fa-css-prefix}-google:before { content: $fa-var-google; } -.#{$fa-css-prefix}-reddit:before { content: $fa-var-reddit; } -.#{$fa-css-prefix}-reddit-square:before { content: $fa-var-reddit-square; } -.#{$fa-css-prefix}-stumbleupon-circle:before { content: $fa-var-stumbleupon-circle; } -.#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; } -.#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; } -.#{$fa-css-prefix}-digg:before { content: $fa-var-digg; } -.#{$fa-css-prefix}-pied-piper-pp:before { content: $fa-var-pied-piper-pp; } -.#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; } -.#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; } -.#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; } -.#{$fa-css-prefix}-language:before { content: $fa-var-language; } -.#{$fa-css-prefix}-fax:before { content: $fa-var-fax; } -.#{$fa-css-prefix}-building:before { content: $fa-var-building; } -.#{$fa-css-prefix}-child:before { content: $fa-var-child; } -.#{$fa-css-prefix}-paw:before { content: $fa-var-paw; } -.#{$fa-css-prefix}-spoon:before { content: $fa-var-spoon; } -.#{$fa-css-prefix}-cube:before { content: $fa-var-cube; } -.#{$fa-css-prefix}-cubes:before { content: $fa-var-cubes; } -.#{$fa-css-prefix}-behance:before { content: $fa-var-behance; } -.#{$fa-css-prefix}-behance-square:before { content: $fa-var-behance-square; } -.#{$fa-css-prefix}-steam:before { content: $fa-var-steam; } -.#{$fa-css-prefix}-steam-square:before { content: $fa-var-steam-square; } -.#{$fa-css-prefix}-recycle:before { content: $fa-var-recycle; } -.#{$fa-css-prefix}-automobile:before, -.#{$fa-css-prefix}-car:before { content: $fa-var-car; } -.#{$fa-css-prefix}-cab:before, -.#{$fa-css-prefix}-taxi:before { content: $fa-var-taxi; } -.#{$fa-css-prefix}-tree:before { content: $fa-var-tree; } -.#{$fa-css-prefix}-spotify:before { content: $fa-var-spotify; } -.#{$fa-css-prefix}-deviantart:before { content: $fa-var-deviantart; } -.#{$fa-css-prefix}-soundcloud:before { content: $fa-var-soundcloud; } -.#{$fa-css-prefix}-database:before { content: $fa-var-database; } -.#{$fa-css-prefix}-file-pdf-o:before { content: $fa-var-file-pdf-o; } -.#{$fa-css-prefix}-file-word-o:before { content: $fa-var-file-word-o; } -.#{$fa-css-prefix}-file-excel-o:before { content: $fa-var-file-excel-o; } -.#{$fa-css-prefix}-file-powerpoint-o:before { content: $fa-var-file-powerpoint-o; } -.#{$fa-css-prefix}-file-photo-o:before, -.#{$fa-css-prefix}-file-picture-o:before, -.#{$fa-css-prefix}-file-image-o:before { content: $fa-var-file-image-o; } -.#{$fa-css-prefix}-file-zip-o:before, -.#{$fa-css-prefix}-file-archive-o:before { content: $fa-var-file-archive-o; } -.#{$fa-css-prefix}-file-sound-o:before, -.#{$fa-css-prefix}-file-audio-o:before { content: $fa-var-file-audio-o; } -.#{$fa-css-prefix}-file-movie-o:before, -.#{$fa-css-prefix}-file-video-o:before { content: $fa-var-file-video-o; } -.#{$fa-css-prefix}-file-code-o:before { content: $fa-var-file-code-o; } -.#{$fa-css-prefix}-vine:before { content: $fa-var-vine; } -.#{$fa-css-prefix}-codepen:before { content: $fa-var-codepen; } -.#{$fa-css-prefix}-jsfiddle:before { content: $fa-var-jsfiddle; } -.#{$fa-css-prefix}-life-bouy:before, -.#{$fa-css-prefix}-life-buoy:before, -.#{$fa-css-prefix}-life-saver:before, -.#{$fa-css-prefix}-support:before, -.#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; } -.#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; } -.#{$fa-css-prefix}-ra:before, -.#{$fa-css-prefix}-resistance:before, -.#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; } -.#{$fa-css-prefix}-ge:before, -.#{$fa-css-prefix}-empire:before { content: $fa-var-empire; } -.#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; } -.#{$fa-css-prefix}-git:before { content: $fa-var-git; } -.#{$fa-css-prefix}-y-combinator-square:before, -.#{$fa-css-prefix}-yc-square:before, -.#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; } -.#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; } -.#{$fa-css-prefix}-qq:before { content: $fa-var-qq; } -.#{$fa-css-prefix}-wechat:before, -.#{$fa-css-prefix}-weixin:before { content: $fa-var-weixin; } -.#{$fa-css-prefix}-send:before, -.#{$fa-css-prefix}-paper-plane:before { content: $fa-var-paper-plane; } -.#{$fa-css-prefix}-send-o:before, -.#{$fa-css-prefix}-paper-plane-o:before { content: $fa-var-paper-plane-o; } -.#{$fa-css-prefix}-history:before { content: $fa-var-history; } -.#{$fa-css-prefix}-circle-thin:before { content: $fa-var-circle-thin; } -.#{$fa-css-prefix}-header:before { content: $fa-var-header; } -.#{$fa-css-prefix}-paragraph:before { content: $fa-var-paragraph; } -.#{$fa-css-prefix}-sliders:before { content: $fa-var-sliders; } -.#{$fa-css-prefix}-share-alt:before { content: $fa-var-share-alt; } -.#{$fa-css-prefix}-share-alt-square:before { content: $fa-var-share-alt-square; } -.#{$fa-css-prefix}-bomb:before { content: $fa-var-bomb; } -.#{$fa-css-prefix}-soccer-ball-o:before, -.#{$fa-css-prefix}-futbol-o:before { content: $fa-var-futbol-o; } -.#{$fa-css-prefix}-tty:before { content: $fa-var-tty; } -.#{$fa-css-prefix}-binoculars:before { content: $fa-var-binoculars; } -.#{$fa-css-prefix}-plug:before { content: $fa-var-plug; } -.#{$fa-css-prefix}-slideshare:before { content: $fa-var-slideshare; } -.#{$fa-css-prefix}-twitch:before { content: $fa-var-twitch; } -.#{$fa-css-prefix}-yelp:before { content: $fa-var-yelp; } -.#{$fa-css-prefix}-newspaper-o:before { content: $fa-var-newspaper-o; } -.#{$fa-css-prefix}-wifi:before { content: $fa-var-wifi; } -.#{$fa-css-prefix}-calculator:before { content: $fa-var-calculator; } -.#{$fa-css-prefix}-paypal:before { content: $fa-var-paypal; } -.#{$fa-css-prefix}-google-wallet:before { content: $fa-var-google-wallet; } -.#{$fa-css-prefix}-cc-visa:before { content: $fa-var-cc-visa; } -.#{$fa-css-prefix}-cc-mastercard:before { content: $fa-var-cc-mastercard; } -.#{$fa-css-prefix}-cc-discover:before { content: $fa-var-cc-discover; } -.#{$fa-css-prefix}-cc-amex:before { content: $fa-var-cc-amex; } -.#{$fa-css-prefix}-cc-paypal:before { content: $fa-var-cc-paypal; } -.#{$fa-css-prefix}-cc-stripe:before { content: $fa-var-cc-stripe; } -.#{$fa-css-prefix}-bell-slash:before { content: $fa-var-bell-slash; } -.#{$fa-css-prefix}-bell-slash-o:before { content: $fa-var-bell-slash-o; } -.#{$fa-css-prefix}-trash:before { content: $fa-var-trash; } -.#{$fa-css-prefix}-copyright:before { content: $fa-var-copyright; } -.#{$fa-css-prefix}-at:before { content: $fa-var-at; } -.#{$fa-css-prefix}-eyedropper:before { content: $fa-var-eyedropper; } -.#{$fa-css-prefix}-paint-brush:before { content: $fa-var-paint-brush; } -.#{$fa-css-prefix}-birthday-cake:before { content: $fa-var-birthday-cake; } -.#{$fa-css-prefix}-area-chart:before { content: $fa-var-area-chart; } -.#{$fa-css-prefix}-pie-chart:before { content: $fa-var-pie-chart; } -.#{$fa-css-prefix}-line-chart:before { content: $fa-var-line-chart; } -.#{$fa-css-prefix}-lastfm:before { content: $fa-var-lastfm; } -.#{$fa-css-prefix}-lastfm-square:before { content: $fa-var-lastfm-square; } -.#{$fa-css-prefix}-toggle-off:before { content: $fa-var-toggle-off; } -.#{$fa-css-prefix}-toggle-on:before { content: $fa-var-toggle-on; } -.#{$fa-css-prefix}-bicycle:before { content: $fa-var-bicycle; } -.#{$fa-css-prefix}-bus:before { content: $fa-var-bus; } -.#{$fa-css-prefix}-ioxhost:before { content: $fa-var-ioxhost; } -.#{$fa-css-prefix}-angellist:before { content: $fa-var-angellist; } -.#{$fa-css-prefix}-cc:before { content: $fa-var-cc; } -.#{$fa-css-prefix}-shekel:before, -.#{$fa-css-prefix}-sheqel:before, -.#{$fa-css-prefix}-ils:before { content: $fa-var-ils; } -.#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; } -.#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; } -.#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; } -.#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; } -.#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; } -.#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; } -.#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; } -.#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; } -.#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; } -.#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; } -.#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; } -.#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; } -.#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; } -.#{$fa-css-prefix}-ship:before { content: $fa-var-ship; } -.#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; } -.#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; } -.#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; } -.#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; } -.#{$fa-css-prefix}-venus:before { content: $fa-var-venus; } -.#{$fa-css-prefix}-mars:before { content: $fa-var-mars; } -.#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; } -.#{$fa-css-prefix}-intersex:before, -.#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; } -.#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; } -.#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; } -.#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; } -.#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; } -.#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; } -.#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; } -.#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; } -.#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; } -.#{$fa-css-prefix}-genderless:before { content: $fa-var-genderless; } -.#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; } -.#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; } -.#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; } -.#{$fa-css-prefix}-server:before { content: $fa-var-server; } -.#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; } -.#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; } -.#{$fa-css-prefix}-hotel:before, -.#{$fa-css-prefix}-bed:before { content: $fa-var-bed; } -.#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; } -.#{$fa-css-prefix}-train:before { content: $fa-var-train; } -.#{$fa-css-prefix}-subway:before { content: $fa-var-subway; } -.#{$fa-css-prefix}-medium:before { content: $fa-var-medium; } -.#{$fa-css-prefix}-yc:before, -.#{$fa-css-prefix}-y-combinator:before { content: $fa-var-y-combinator; } -.#{$fa-css-prefix}-optin-monster:before { content: $fa-var-optin-monster; } -.#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; } -.#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; } -.#{$fa-css-prefix}-battery-4:before, -.#{$fa-css-prefix}-battery:before, -.#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; } -.#{$fa-css-prefix}-battery-3:before, -.#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; } -.#{$fa-css-prefix}-battery-2:before, -.#{$fa-css-prefix}-battery-half:before { content: $fa-var-battery-half; } -.#{$fa-css-prefix}-battery-1:before, -.#{$fa-css-prefix}-battery-quarter:before { content: $fa-var-battery-quarter; } -.#{$fa-css-prefix}-battery-0:before, -.#{$fa-css-prefix}-battery-empty:before { content: $fa-var-battery-empty; } -.#{$fa-css-prefix}-mouse-pointer:before { content: $fa-var-mouse-pointer; } -.#{$fa-css-prefix}-i-cursor:before { content: $fa-var-i-cursor; } -.#{$fa-css-prefix}-object-group:before { content: $fa-var-object-group; } -.#{$fa-css-prefix}-object-ungroup:before { content: $fa-var-object-ungroup; } -.#{$fa-css-prefix}-sticky-note:before { content: $fa-var-sticky-note; } -.#{$fa-css-prefix}-sticky-note-o:before { content: $fa-var-sticky-note-o; } -.#{$fa-css-prefix}-cc-jcb:before { content: $fa-var-cc-jcb; } -.#{$fa-css-prefix}-cc-diners-club:before { content: $fa-var-cc-diners-club; } -.#{$fa-css-prefix}-clone:before { content: $fa-var-clone; } -.#{$fa-css-prefix}-balance-scale:before { content: $fa-var-balance-scale; } -.#{$fa-css-prefix}-hourglass-o:before { content: $fa-var-hourglass-o; } -.#{$fa-css-prefix}-hourglass-1:before, -.#{$fa-css-prefix}-hourglass-start:before { content: $fa-var-hourglass-start; } -.#{$fa-css-prefix}-hourglass-2:before, -.#{$fa-css-prefix}-hourglass-half:before { content: $fa-var-hourglass-half; } -.#{$fa-css-prefix}-hourglass-3:before, -.#{$fa-css-prefix}-hourglass-end:before { content: $fa-var-hourglass-end; } -.#{$fa-css-prefix}-hourglass:before { content: $fa-var-hourglass; } -.#{$fa-css-prefix}-hand-grab-o:before, -.#{$fa-css-prefix}-hand-rock-o:before { content: $fa-var-hand-rock-o; } -.#{$fa-css-prefix}-hand-stop-o:before, -.#{$fa-css-prefix}-hand-paper-o:before { content: $fa-var-hand-paper-o; } -.#{$fa-css-prefix}-hand-scissors-o:before { content: $fa-var-hand-scissors-o; } -.#{$fa-css-prefix}-hand-lizard-o:before { content: $fa-var-hand-lizard-o; } -.#{$fa-css-prefix}-hand-spock-o:before { content: $fa-var-hand-spock-o; } -.#{$fa-css-prefix}-hand-pointer-o:before { content: $fa-var-hand-pointer-o; } -.#{$fa-css-prefix}-hand-peace-o:before { content: $fa-var-hand-peace-o; } -.#{$fa-css-prefix}-trademark:before { content: $fa-var-trademark; } -.#{$fa-css-prefix}-registered:before { content: $fa-var-registered; } -.#{$fa-css-prefix}-creative-commons:before { content: $fa-var-creative-commons; } -.#{$fa-css-prefix}-gg:before { content: $fa-var-gg; } -.#{$fa-css-prefix}-gg-circle:before { content: $fa-var-gg-circle; } -.#{$fa-css-prefix}-tripadvisor:before { content: $fa-var-tripadvisor; } -.#{$fa-css-prefix}-odnoklassniki:before { content: $fa-var-odnoklassniki; } -.#{$fa-css-prefix}-odnoklassniki-square:before { content: $fa-var-odnoklassniki-square; } -.#{$fa-css-prefix}-get-pocket:before { content: $fa-var-get-pocket; } -.#{$fa-css-prefix}-wikipedia-w:before { content: $fa-var-wikipedia-w; } -.#{$fa-css-prefix}-safari:before { content: $fa-var-safari; } -.#{$fa-css-prefix}-chrome:before { content: $fa-var-chrome; } -.#{$fa-css-prefix}-firefox:before { content: $fa-var-firefox; } -.#{$fa-css-prefix}-opera:before { content: $fa-var-opera; } -.#{$fa-css-prefix}-internet-explorer:before { content: $fa-var-internet-explorer; } -.#{$fa-css-prefix}-tv:before, -.#{$fa-css-prefix}-television:before { content: $fa-var-television; } -.#{$fa-css-prefix}-contao:before { content: $fa-var-contao; } -.#{$fa-css-prefix}-500px:before { content: $fa-var-500px; } -.#{$fa-css-prefix}-amazon:before { content: $fa-var-amazon; } -.#{$fa-css-prefix}-calendar-plus-o:before { content: $fa-var-calendar-plus-o; } -.#{$fa-css-prefix}-calendar-minus-o:before { content: $fa-var-calendar-minus-o; } -.#{$fa-css-prefix}-calendar-times-o:before { content: $fa-var-calendar-times-o; } -.#{$fa-css-prefix}-calendar-check-o:before { content: $fa-var-calendar-check-o; } -.#{$fa-css-prefix}-industry:before { content: $fa-var-industry; } -.#{$fa-css-prefix}-map-pin:before { content: $fa-var-map-pin; } -.#{$fa-css-prefix}-map-signs:before { content: $fa-var-map-signs; } -.#{$fa-css-prefix}-map-o:before { content: $fa-var-map-o; } -.#{$fa-css-prefix}-map:before { content: $fa-var-map; } -.#{$fa-css-prefix}-commenting:before { content: $fa-var-commenting; } -.#{$fa-css-prefix}-commenting-o:before { content: $fa-var-commenting-o; } -.#{$fa-css-prefix}-houzz:before { content: $fa-var-houzz; } -.#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; } -.#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; } -.#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; } -.#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; } -.#{$fa-css-prefix}-edge:before { content: $fa-var-edge; } -.#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; } -.#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; } -.#{$fa-css-prefix}-modx:before { content: $fa-var-modx; } -.#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; } -.#{$fa-css-prefix}-usb:before { content: $fa-var-usb; } -.#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; } -.#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; } -.#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; } -.#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; } -.#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; } -.#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; } -.#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; } -.#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; } -.#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; } -.#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; } -.#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; } -.#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; } -.#{$fa-css-prefix}-percent:before { content: $fa-var-percent; } -.#{$fa-css-prefix}-gitlab:before { content: $fa-var-gitlab; } -.#{$fa-css-prefix}-wpbeginner:before { content: $fa-var-wpbeginner; } -.#{$fa-css-prefix}-wpforms:before { content: $fa-var-wpforms; } -.#{$fa-css-prefix}-envira:before { content: $fa-var-envira; } -.#{$fa-css-prefix}-universal-access:before { content: $fa-var-universal-access; } -.#{$fa-css-prefix}-wheelchair-alt:before { content: $fa-var-wheelchair-alt; } -.#{$fa-css-prefix}-question-circle-o:before { content: $fa-var-question-circle-o; } -.#{$fa-css-prefix}-blind:before { content: $fa-var-blind; } -.#{$fa-css-prefix}-audio-description:before { content: $fa-var-audio-description; } -.#{$fa-css-prefix}-volume-control-phone:before { content: $fa-var-volume-control-phone; } -.#{$fa-css-prefix}-braille:before { content: $fa-var-braille; } -.#{$fa-css-prefix}-assistive-listening-systems:before { content: $fa-var-assistive-listening-systems; } -.#{$fa-css-prefix}-asl-interpreting:before, -.#{$fa-css-prefix}-american-sign-language-interpreting:before { content: $fa-var-american-sign-language-interpreting; } -.#{$fa-css-prefix}-deafness:before, -.#{$fa-css-prefix}-hard-of-hearing:before, -.#{$fa-css-prefix}-deaf:before { content: $fa-var-deaf; } -.#{$fa-css-prefix}-glide:before { content: $fa-var-glide; } -.#{$fa-css-prefix}-glide-g:before { content: $fa-var-glide-g; } -.#{$fa-css-prefix}-signing:before, -.#{$fa-css-prefix}-sign-language:before { content: $fa-var-sign-language; } -.#{$fa-css-prefix}-low-vision:before { content: $fa-var-low-vision; } -.#{$fa-css-prefix}-viadeo:before { content: $fa-var-viadeo; } -.#{$fa-css-prefix}-viadeo-square:before { content: $fa-var-viadeo-square; } -.#{$fa-css-prefix}-snapchat:before { content: $fa-var-snapchat; } -.#{$fa-css-prefix}-snapchat-ghost:before { content: $fa-var-snapchat-ghost; } -.#{$fa-css-prefix}-snapchat-square:before { content: $fa-var-snapchat-square; } -.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } -.#{$fa-css-prefix}-first-order:before { content: $fa-var-first-order; } -.#{$fa-css-prefix}-yoast:before { content: $fa-var-yoast; } -.#{$fa-css-prefix}-themeisle:before { content: $fa-var-themeisle; } -.#{$fa-css-prefix}-google-plus-circle:before, -.#{$fa-css-prefix}-google-plus-official:before { content: $fa-var-google-plus-official; } -.#{$fa-css-prefix}-fa:before, -.#{$fa-css-prefix}-font-awesome:before { content: $fa-var-font-awesome; } -.#{$fa-css-prefix}-handshake-o:before { content: $fa-var-handshake-o; } -.#{$fa-css-prefix}-envelope-open:before { content: $fa-var-envelope-open; } -.#{$fa-css-prefix}-envelope-open-o:before { content: $fa-var-envelope-open-o; } -.#{$fa-css-prefix}-linode:before { content: $fa-var-linode; } -.#{$fa-css-prefix}-address-book:before { content: $fa-var-address-book; } -.#{$fa-css-prefix}-address-book-o:before { content: $fa-var-address-book-o; } -.#{$fa-css-prefix}-vcard:before, -.#{$fa-css-prefix}-address-card:before { content: $fa-var-address-card; } -.#{$fa-css-prefix}-vcard-o:before, -.#{$fa-css-prefix}-address-card-o:before { content: $fa-var-address-card-o; } -.#{$fa-css-prefix}-user-circle:before { content: $fa-var-user-circle; } -.#{$fa-css-prefix}-user-circle-o:before { content: $fa-var-user-circle-o; } -.#{$fa-css-prefix}-user-o:before { content: $fa-var-user-o; } -.#{$fa-css-prefix}-id-badge:before { content: $fa-var-id-badge; } -.#{$fa-css-prefix}-drivers-license:before, -.#{$fa-css-prefix}-id-card:before { content: $fa-var-id-card; } -.#{$fa-css-prefix}-drivers-license-o:before, -.#{$fa-css-prefix}-id-card-o:before { content: $fa-var-id-card-o; } -.#{$fa-css-prefix}-quora:before { content: $fa-var-quora; } -.#{$fa-css-prefix}-free-code-camp:before { content: $fa-var-free-code-camp; } -.#{$fa-css-prefix}-telegram:before { content: $fa-var-telegram; } -.#{$fa-css-prefix}-thermometer-4:before, -.#{$fa-css-prefix}-thermometer:before, -.#{$fa-css-prefix}-thermometer-full:before { content: $fa-var-thermometer-full; } -.#{$fa-css-prefix}-thermometer-3:before, -.#{$fa-css-prefix}-thermometer-three-quarters:before { content: $fa-var-thermometer-three-quarters; } -.#{$fa-css-prefix}-thermometer-2:before, -.#{$fa-css-prefix}-thermometer-half:before { content: $fa-var-thermometer-half; } -.#{$fa-css-prefix}-thermometer-1:before, -.#{$fa-css-prefix}-thermometer-quarter:before { content: $fa-var-thermometer-quarter; } -.#{$fa-css-prefix}-thermometer-0:before, -.#{$fa-css-prefix}-thermometer-empty:before { content: $fa-var-thermometer-empty; } -.#{$fa-css-prefix}-shower:before { content: $fa-var-shower; } -.#{$fa-css-prefix}-bathtub:before, -.#{$fa-css-prefix}-s15:before, -.#{$fa-css-prefix}-bath:before { content: $fa-var-bath; } -.#{$fa-css-prefix}-podcast:before { content: $fa-var-podcast; } -.#{$fa-css-prefix}-window-maximize:before { content: $fa-var-window-maximize; } -.#{$fa-css-prefix}-window-minimize:before { content: $fa-var-window-minimize; } -.#{$fa-css-prefix}-window-restore:before { content: $fa-var-window-restore; } -.#{$fa-css-prefix}-times-rectangle:before, -.#{$fa-css-prefix}-window-close:before { content: $fa-var-window-close; } -.#{$fa-css-prefix}-times-rectangle-o:before, -.#{$fa-css-prefix}-window-close-o:before { content: $fa-var-window-close-o; } -.#{$fa-css-prefix}-bandcamp:before { content: $fa-var-bandcamp; } -.#{$fa-css-prefix}-grav:before { content: $fa-var-grav; } -.#{$fa-css-prefix}-etsy:before { content: $fa-var-etsy; } -.#{$fa-css-prefix}-imdb:before { content: $fa-var-imdb; } -.#{$fa-css-prefix}-ravelry:before { content: $fa-var-ravelry; } -.#{$fa-css-prefix}-eercast:before { content: $fa-var-eercast; } -.#{$fa-css-prefix}-microchip:before { content: $fa-var-microchip; } -.#{$fa-css-prefix}-snowflake-o:before { content: $fa-var-snowflake-o; } -.#{$fa-css-prefix}-superpowers:before { content: $fa-var-superpowers; } -.#{$fa-css-prefix}-wpexplorer:before { content: $fa-var-wpexplorer; } -.#{$fa-css-prefix}-meetup:before { content: $fa-var-meetup; } diff --git a/v3/css/font-awesome-4.7.0/scss/_larger.scss b/v3/css/font-awesome-4.7.0/scss/_larger.scss deleted file mode 100644 index 41e9a81..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_larger.scss +++ /dev/null @@ -1,13 +0,0 @@ -// Icon Sizes -// ------------------------- - -/* makes the font 33% larger relative to the icon container */ -.#{$fa-css-prefix}-lg { - font-size: (4em / 3); - line-height: (3em / 4); - vertical-align: -15%; -} -.#{$fa-css-prefix}-2x { font-size: 2em; } -.#{$fa-css-prefix}-3x { font-size: 3em; } -.#{$fa-css-prefix}-4x { font-size: 4em; } -.#{$fa-css-prefix}-5x { font-size: 5em; } diff --git a/v3/css/font-awesome-4.7.0/scss/_list.scss b/v3/css/font-awesome-4.7.0/scss/_list.scss deleted file mode 100644 index 7d1e4d5..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_list.scss +++ /dev/null @@ -1,19 +0,0 @@ -// List Icons -// ------------------------- - -.#{$fa-css-prefix}-ul { - padding-left: 0; - margin-left: $fa-li-width; - list-style-type: none; - > li { position: relative; } -} -.#{$fa-css-prefix}-li { - position: absolute; - left: -$fa-li-width; - width: $fa-li-width; - top: (2em / 14); - text-align: center; - &.#{$fa-css-prefix}-lg { - left: -$fa-li-width + (4em / 14); - } -} diff --git a/v3/css/font-awesome-4.7.0/scss/_mixins.scss b/v3/css/font-awesome-4.7.0/scss/_mixins.scss deleted file mode 100644 index c3bbd57..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_mixins.scss +++ /dev/null @@ -1,60 +0,0 @@ -// Mixins -// -------------------------- - -@mixin fa-icon() { - display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration - font-size: inherit; // can't have font-size inherit on line above, so need to override - text-rendering: auto; // optimizelegibility throws things off #1094 - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -} - -@mixin fa-icon-rotate($degrees, $rotation) { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; - -webkit-transform: rotate($degrees); - -ms-transform: rotate($degrees); - transform: rotate($degrees); -} - -@mixin fa-icon-flip($horiz, $vert, $rotation) { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; - -webkit-transform: scale($horiz, $vert); - -ms-transform: scale($horiz, $vert); - transform: scale($horiz, $vert); -} - - -// Only display content to screen readers. A la Bootstrap 4. -// -// See: http://a11yproject.com/posts/how-to-hide-content/ - -@mixin sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; -} - -// Use in conjunction with .sr-only to only display content when it's focused. -// -// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 -// -// Credit: HTML5 Boilerplate - -@mixin sr-only-focusable { - &:active, - &:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; - } -} diff --git a/v3/css/font-awesome-4.7.0/scss/_path.scss b/v3/css/font-awesome-4.7.0/scss/_path.scss deleted file mode 100644 index bb457c2..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_path.scss +++ /dev/null @@ -1,15 +0,0 @@ -/* FONT PATH - * -------------------------- */ - -@font-face { - font-family: 'FontAwesome'; - src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); - src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), - url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), - url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), - url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), - url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); -// src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts - font-weight: normal; - font-style: normal; -} diff --git a/v3/css/font-awesome-4.7.0/scss/_rotated-flipped.scss b/v3/css/font-awesome-4.7.0/scss/_rotated-flipped.scss deleted file mode 100644 index a3558fd..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_rotated-flipped.scss +++ /dev/null @@ -1,20 +0,0 @@ -// Rotated & Flipped Icons -// ------------------------- - -.#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } -.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } -.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } - -.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } -.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } - -// Hook for IE8-9 -// ------------------------- - -:root .#{$fa-css-prefix}-rotate-90, -:root .#{$fa-css-prefix}-rotate-180, -:root .#{$fa-css-prefix}-rotate-270, -:root .#{$fa-css-prefix}-flip-horizontal, -:root .#{$fa-css-prefix}-flip-vertical { - filter: none; -} diff --git a/v3/css/font-awesome-4.7.0/scss/_screen-reader.scss b/v3/css/font-awesome-4.7.0/scss/_screen-reader.scss deleted file mode 100644 index 637426f..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_screen-reader.scss +++ /dev/null @@ -1,5 +0,0 @@ -// Screen Readers -// ------------------------- - -.sr-only { @include sr-only(); } -.sr-only-focusable { @include sr-only-focusable(); } diff --git a/v3/css/font-awesome-4.7.0/scss/_stacked.scss b/v3/css/font-awesome-4.7.0/scss/_stacked.scss deleted file mode 100644 index aef7403..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_stacked.scss +++ /dev/null @@ -1,20 +0,0 @@ -// Stacked Icons -// ------------------------- - -.#{$fa-css-prefix}-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} -.#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} -.#{$fa-css-prefix}-stack-1x { line-height: inherit; } -.#{$fa-css-prefix}-stack-2x { font-size: 2em; } -.#{$fa-css-prefix}-inverse { color: $fa-inverse; } diff --git a/v3/css/font-awesome-4.7.0/scss/_variables.scss b/v3/css/font-awesome-4.7.0/scss/_variables.scss deleted file mode 100644 index 498fc4a..0000000 --- a/v3/css/font-awesome-4.7.0/scss/_variables.scss +++ /dev/null @@ -1,800 +0,0 @@ -// Variables -// -------------------------- - -$fa-font-path: "../fonts" !default; -$fa-font-size-base: 14px !default; -$fa-line-height-base: 1 !default; -//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts" !default; // for referencing Bootstrap CDN font files directly -$fa-css-prefix: fa !default; -$fa-version: "4.7.0" !default; -$fa-border-color: #eee !default; -$fa-inverse: #fff !default; -$fa-li-width: (30em / 14) !default; - -$fa-var-500px: "\f26e"; -$fa-var-address-book: "\f2b9"; -$fa-var-address-book-o: "\f2ba"; -$fa-var-address-card: "\f2bb"; -$fa-var-address-card-o: "\f2bc"; -$fa-var-adjust: "\f042"; -$fa-var-adn: "\f170"; -$fa-var-align-center: "\f037"; -$fa-var-align-justify: "\f039"; -$fa-var-align-left: "\f036"; -$fa-var-align-right: "\f038"; -$fa-var-amazon: "\f270"; -$fa-var-ambulance: "\f0f9"; -$fa-var-american-sign-language-interpreting: "\f2a3"; -$fa-var-anchor: "\f13d"; -$fa-var-android: "\f17b"; -$fa-var-angellist: "\f209"; -$fa-var-angle-double-down: "\f103"; -$fa-var-angle-double-left: "\f100"; -$fa-var-angle-double-right: "\f101"; -$fa-var-angle-double-up: "\f102"; -$fa-var-angle-down: "\f107"; -$fa-var-angle-left: "\f104"; -$fa-var-angle-right: "\f105"; -$fa-var-angle-up: "\f106"; -$fa-var-apple: "\f179"; -$fa-var-archive: "\f187"; -$fa-var-area-chart: "\f1fe"; -$fa-var-arrow-circle-down: "\f0ab"; -$fa-var-arrow-circle-left: "\f0a8"; -$fa-var-arrow-circle-o-down: "\f01a"; -$fa-var-arrow-circle-o-left: "\f190"; -$fa-var-arrow-circle-o-right: "\f18e"; -$fa-var-arrow-circle-o-up: "\f01b"; -$fa-var-arrow-circle-right: "\f0a9"; -$fa-var-arrow-circle-up: "\f0aa"; -$fa-var-arrow-down: "\f063"; -$fa-var-arrow-left: "\f060"; -$fa-var-arrow-right: "\f061"; -$fa-var-arrow-up: "\f062"; -$fa-var-arrows: "\f047"; -$fa-var-arrows-alt: "\f0b2"; -$fa-var-arrows-h: "\f07e"; -$fa-var-arrows-v: "\f07d"; -$fa-var-asl-interpreting: "\f2a3"; -$fa-var-assistive-listening-systems: "\f2a2"; -$fa-var-asterisk: "\f069"; -$fa-var-at: "\f1fa"; -$fa-var-audio-description: "\f29e"; -$fa-var-automobile: "\f1b9"; -$fa-var-backward: "\f04a"; -$fa-var-balance-scale: "\f24e"; -$fa-var-ban: "\f05e"; -$fa-var-bandcamp: "\f2d5"; -$fa-var-bank: "\f19c"; -$fa-var-bar-chart: "\f080"; -$fa-var-bar-chart-o: "\f080"; -$fa-var-barcode: "\f02a"; -$fa-var-bars: "\f0c9"; -$fa-var-bath: "\f2cd"; -$fa-var-bathtub: "\f2cd"; -$fa-var-battery: "\f240"; -$fa-var-battery-0: "\f244"; -$fa-var-battery-1: "\f243"; -$fa-var-battery-2: "\f242"; -$fa-var-battery-3: "\f241"; -$fa-var-battery-4: "\f240"; -$fa-var-battery-empty: "\f244"; -$fa-var-battery-full: "\f240"; -$fa-var-battery-half: "\f242"; -$fa-var-battery-quarter: "\f243"; -$fa-var-battery-three-quarters: "\f241"; -$fa-var-bed: "\f236"; -$fa-var-beer: "\f0fc"; -$fa-var-behance: "\f1b4"; -$fa-var-behance-square: "\f1b5"; -$fa-var-bell: "\f0f3"; -$fa-var-bell-o: "\f0a2"; -$fa-var-bell-slash: "\f1f6"; -$fa-var-bell-slash-o: "\f1f7"; -$fa-var-bicycle: "\f206"; -$fa-var-binoculars: "\f1e5"; -$fa-var-birthday-cake: "\f1fd"; -$fa-var-bitbucket: "\f171"; -$fa-var-bitbucket-square: "\f172"; -$fa-var-bitcoin: "\f15a"; -$fa-var-black-tie: "\f27e"; -$fa-var-blind: "\f29d"; -$fa-var-bluetooth: "\f293"; -$fa-var-bluetooth-b: "\f294"; -$fa-var-bold: "\f032"; -$fa-var-bolt: "\f0e7"; -$fa-var-bomb: "\f1e2"; -$fa-var-book: "\f02d"; -$fa-var-bookmark: "\f02e"; -$fa-var-bookmark-o: "\f097"; -$fa-var-braille: "\f2a1"; -$fa-var-briefcase: "\f0b1"; -$fa-var-btc: "\f15a"; -$fa-var-bug: "\f188"; -$fa-var-building: "\f1ad"; -$fa-var-building-o: "\f0f7"; -$fa-var-bullhorn: "\f0a1"; -$fa-var-bullseye: "\f140"; -$fa-var-bus: "\f207"; -$fa-var-buysellads: "\f20d"; -$fa-var-cab: "\f1ba"; -$fa-var-calculator: "\f1ec"; -$fa-var-calendar: "\f073"; -$fa-var-calendar-check-o: "\f274"; -$fa-var-calendar-minus-o: "\f272"; -$fa-var-calendar-o: "\f133"; -$fa-var-calendar-plus-o: "\f271"; -$fa-var-calendar-times-o: "\f273"; -$fa-var-camera: "\f030"; -$fa-var-camera-retro: "\f083"; -$fa-var-car: "\f1b9"; -$fa-var-caret-down: "\f0d7"; -$fa-var-caret-left: "\f0d9"; -$fa-var-caret-right: "\f0da"; -$fa-var-caret-square-o-down: "\f150"; -$fa-var-caret-square-o-left: "\f191"; -$fa-var-caret-square-o-right: "\f152"; -$fa-var-caret-square-o-up: "\f151"; -$fa-var-caret-up: "\f0d8"; -$fa-var-cart-arrow-down: "\f218"; -$fa-var-cart-plus: "\f217"; -$fa-var-cc: "\f20a"; -$fa-var-cc-amex: "\f1f3"; -$fa-var-cc-diners-club: "\f24c"; -$fa-var-cc-discover: "\f1f2"; -$fa-var-cc-jcb: "\f24b"; -$fa-var-cc-mastercard: "\f1f1"; -$fa-var-cc-paypal: "\f1f4"; -$fa-var-cc-stripe: "\f1f5"; -$fa-var-cc-visa: "\f1f0"; -$fa-var-certificate: "\f0a3"; -$fa-var-chain: "\f0c1"; -$fa-var-chain-broken: "\f127"; -$fa-var-check: "\f00c"; -$fa-var-check-circle: "\f058"; -$fa-var-check-circle-o: "\f05d"; -$fa-var-check-square: "\f14a"; -$fa-var-check-square-o: "\f046"; -$fa-var-chevron-circle-down: "\f13a"; -$fa-var-chevron-circle-left: "\f137"; -$fa-var-chevron-circle-right: "\f138"; -$fa-var-chevron-circle-up: "\f139"; -$fa-var-chevron-down: "\f078"; -$fa-var-chevron-left: "\f053"; -$fa-var-chevron-right: "\f054"; -$fa-var-chevron-up: "\f077"; -$fa-var-child: "\f1ae"; -$fa-var-chrome: "\f268"; -$fa-var-circle: "\f111"; -$fa-var-circle-o: "\f10c"; -$fa-var-circle-o-notch: "\f1ce"; -$fa-var-circle-thin: "\f1db"; -$fa-var-clipboard: "\f0ea"; -$fa-var-clock-o: "\f017"; -$fa-var-clone: "\f24d"; -$fa-var-close: "\f00d"; -$fa-var-cloud: "\f0c2"; -$fa-var-cloud-download: "\f0ed"; -$fa-var-cloud-upload: "\f0ee"; -$fa-var-cny: "\f157"; -$fa-var-code: "\f121"; -$fa-var-code-fork: "\f126"; -$fa-var-codepen: "\f1cb"; -$fa-var-codiepie: "\f284"; -$fa-var-coffee: "\f0f4"; -$fa-var-cog: "\f013"; -$fa-var-cogs: "\f085"; -$fa-var-columns: "\f0db"; -$fa-var-comment: "\f075"; -$fa-var-comment-o: "\f0e5"; -$fa-var-commenting: "\f27a"; -$fa-var-commenting-o: "\f27b"; -$fa-var-comments: "\f086"; -$fa-var-comments-o: "\f0e6"; -$fa-var-compass: "\f14e"; -$fa-var-compress: "\f066"; -$fa-var-connectdevelop: "\f20e"; -$fa-var-contao: "\f26d"; -$fa-var-copy: "\f0c5"; -$fa-var-copyright: "\f1f9"; -$fa-var-creative-commons: "\f25e"; -$fa-var-credit-card: "\f09d"; -$fa-var-credit-card-alt: "\f283"; -$fa-var-crop: "\f125"; -$fa-var-crosshairs: "\f05b"; -$fa-var-css3: "\f13c"; -$fa-var-cube: "\f1b2"; -$fa-var-cubes: "\f1b3"; -$fa-var-cut: "\f0c4"; -$fa-var-cutlery: "\f0f5"; -$fa-var-dashboard: "\f0e4"; -$fa-var-dashcube: "\f210"; -$fa-var-database: "\f1c0"; -$fa-var-deaf: "\f2a4"; -$fa-var-deafness: "\f2a4"; -$fa-var-dedent: "\f03b"; -$fa-var-delicious: "\f1a5"; -$fa-var-desktop: "\f108"; -$fa-var-deviantart: "\f1bd"; -$fa-var-diamond: "\f219"; -$fa-var-digg: "\f1a6"; -$fa-var-dollar: "\f155"; -$fa-var-dot-circle-o: "\f192"; -$fa-var-download: "\f019"; -$fa-var-dribbble: "\f17d"; -$fa-var-drivers-license: "\f2c2"; -$fa-var-drivers-license-o: "\f2c3"; -$fa-var-dropbox: "\f16b"; -$fa-var-drupal: "\f1a9"; -$fa-var-edge: "\f282"; -$fa-var-edit: "\f044"; -$fa-var-eercast: "\f2da"; -$fa-var-eject: "\f052"; -$fa-var-ellipsis-h: "\f141"; -$fa-var-ellipsis-v: "\f142"; -$fa-var-empire: "\f1d1"; -$fa-var-envelope: "\f0e0"; -$fa-var-envelope-o: "\f003"; -$fa-var-envelope-open: "\f2b6"; -$fa-var-envelope-open-o: "\f2b7"; -$fa-var-envelope-square: "\f199"; -$fa-var-envira: "\f299"; -$fa-var-eraser: "\f12d"; -$fa-var-etsy: "\f2d7"; -$fa-var-eur: "\f153"; -$fa-var-euro: "\f153"; -$fa-var-exchange: "\f0ec"; -$fa-var-exclamation: "\f12a"; -$fa-var-exclamation-circle: "\f06a"; -$fa-var-exclamation-triangle: "\f071"; -$fa-var-expand: "\f065"; -$fa-var-expeditedssl: "\f23e"; -$fa-var-external-link: "\f08e"; -$fa-var-external-link-square: "\f14c"; -$fa-var-eye: "\f06e"; -$fa-var-eye-slash: "\f070"; -$fa-var-eyedropper: "\f1fb"; -$fa-var-fa: "\f2b4"; -$fa-var-facebook: "\f09a"; -$fa-var-facebook-f: "\f09a"; -$fa-var-facebook-official: "\f230"; -$fa-var-facebook-square: "\f082"; -$fa-var-fast-backward: "\f049"; -$fa-var-fast-forward: "\f050"; -$fa-var-fax: "\f1ac"; -$fa-var-feed: "\f09e"; -$fa-var-female: "\f182"; -$fa-var-fighter-jet: "\f0fb"; -$fa-var-file: "\f15b"; -$fa-var-file-archive-o: "\f1c6"; -$fa-var-file-audio-o: "\f1c7"; -$fa-var-file-code-o: "\f1c9"; -$fa-var-file-excel-o: "\f1c3"; -$fa-var-file-image-o: "\f1c5"; -$fa-var-file-movie-o: "\f1c8"; -$fa-var-file-o: "\f016"; -$fa-var-file-pdf-o: "\f1c1"; -$fa-var-file-photo-o: "\f1c5"; -$fa-var-file-picture-o: "\f1c5"; -$fa-var-file-powerpoint-o: "\f1c4"; -$fa-var-file-sound-o: "\f1c7"; -$fa-var-file-text: "\f15c"; -$fa-var-file-text-o: "\f0f6"; -$fa-var-file-video-o: "\f1c8"; -$fa-var-file-word-o: "\f1c2"; -$fa-var-file-zip-o: "\f1c6"; -$fa-var-files-o: "\f0c5"; -$fa-var-film: "\f008"; -$fa-var-filter: "\f0b0"; -$fa-var-fire: "\f06d"; -$fa-var-fire-extinguisher: "\f134"; -$fa-var-firefox: "\f269"; -$fa-var-first-order: "\f2b0"; -$fa-var-flag: "\f024"; -$fa-var-flag-checkered: "\f11e"; -$fa-var-flag-o: "\f11d"; -$fa-var-flash: "\f0e7"; -$fa-var-flask: "\f0c3"; -$fa-var-flickr: "\f16e"; -$fa-var-floppy-o: "\f0c7"; -$fa-var-folder: "\f07b"; -$fa-var-folder-o: "\f114"; -$fa-var-folder-open: "\f07c"; -$fa-var-folder-open-o: "\f115"; -$fa-var-font: "\f031"; -$fa-var-font-awesome: "\f2b4"; -$fa-var-fonticons: "\f280"; -$fa-var-fort-awesome: "\f286"; -$fa-var-forumbee: "\f211"; -$fa-var-forward: "\f04e"; -$fa-var-foursquare: "\f180"; -$fa-var-free-code-camp: "\f2c5"; -$fa-var-frown-o: "\f119"; -$fa-var-futbol-o: "\f1e3"; -$fa-var-gamepad: "\f11b"; -$fa-var-gavel: "\f0e3"; -$fa-var-gbp: "\f154"; -$fa-var-ge: "\f1d1"; -$fa-var-gear: "\f013"; -$fa-var-gears: "\f085"; -$fa-var-genderless: "\f22d"; -$fa-var-get-pocket: "\f265"; -$fa-var-gg: "\f260"; -$fa-var-gg-circle: "\f261"; -$fa-var-gift: "\f06b"; -$fa-var-git: "\f1d3"; -$fa-var-git-square: "\f1d2"; -$fa-var-github: "\f09b"; -$fa-var-github-alt: "\f113"; -$fa-var-github-square: "\f092"; -$fa-var-gitlab: "\f296"; -$fa-var-gittip: "\f184"; -$fa-var-glass: "\f000"; -$fa-var-glide: "\f2a5"; -$fa-var-glide-g: "\f2a6"; -$fa-var-globe: "\f0ac"; -$fa-var-google: "\f1a0"; -$fa-var-google-plus: "\f0d5"; -$fa-var-google-plus-circle: "\f2b3"; -$fa-var-google-plus-official: "\f2b3"; -$fa-var-google-plus-square: "\f0d4"; -$fa-var-google-wallet: "\f1ee"; -$fa-var-graduation-cap: "\f19d"; -$fa-var-gratipay: "\f184"; -$fa-var-grav: "\f2d6"; -$fa-var-group: "\f0c0"; -$fa-var-h-square: "\f0fd"; -$fa-var-hacker-news: "\f1d4"; -$fa-var-hand-grab-o: "\f255"; -$fa-var-hand-lizard-o: "\f258"; -$fa-var-hand-o-down: "\f0a7"; -$fa-var-hand-o-left: "\f0a5"; -$fa-var-hand-o-right: "\f0a4"; -$fa-var-hand-o-up: "\f0a6"; -$fa-var-hand-paper-o: "\f256"; -$fa-var-hand-peace-o: "\f25b"; -$fa-var-hand-pointer-o: "\f25a"; -$fa-var-hand-rock-o: "\f255"; -$fa-var-hand-scissors-o: "\f257"; -$fa-var-hand-spock-o: "\f259"; -$fa-var-hand-stop-o: "\f256"; -$fa-var-handshake-o: "\f2b5"; -$fa-var-hard-of-hearing: "\f2a4"; -$fa-var-hashtag: "\f292"; -$fa-var-hdd-o: "\f0a0"; -$fa-var-header: "\f1dc"; -$fa-var-headphones: "\f025"; -$fa-var-heart: "\f004"; -$fa-var-heart-o: "\f08a"; -$fa-var-heartbeat: "\f21e"; -$fa-var-history: "\f1da"; -$fa-var-home: "\f015"; -$fa-var-hospital-o: "\f0f8"; -$fa-var-hotel: "\f236"; -$fa-var-hourglass: "\f254"; -$fa-var-hourglass-1: "\f251"; -$fa-var-hourglass-2: "\f252"; -$fa-var-hourglass-3: "\f253"; -$fa-var-hourglass-end: "\f253"; -$fa-var-hourglass-half: "\f252"; -$fa-var-hourglass-o: "\f250"; -$fa-var-hourglass-start: "\f251"; -$fa-var-houzz: "\f27c"; -$fa-var-html5: "\f13b"; -$fa-var-i-cursor: "\f246"; -$fa-var-id-badge: "\f2c1"; -$fa-var-id-card: "\f2c2"; -$fa-var-id-card-o: "\f2c3"; -$fa-var-ils: "\f20b"; -$fa-var-image: "\f03e"; -$fa-var-imdb: "\f2d8"; -$fa-var-inbox: "\f01c"; -$fa-var-indent: "\f03c"; -$fa-var-industry: "\f275"; -$fa-var-info: "\f129"; -$fa-var-info-circle: "\f05a"; -$fa-var-inr: "\f156"; -$fa-var-instagram: "\f16d"; -$fa-var-institution: "\f19c"; -$fa-var-internet-explorer: "\f26b"; -$fa-var-intersex: "\f224"; -$fa-var-ioxhost: "\f208"; -$fa-var-italic: "\f033"; -$fa-var-joomla: "\f1aa"; -$fa-var-jpy: "\f157"; -$fa-var-jsfiddle: "\f1cc"; -$fa-var-key: "\f084"; -$fa-var-keyboard-o: "\f11c"; -$fa-var-krw: "\f159"; -$fa-var-language: "\f1ab"; -$fa-var-laptop: "\f109"; -$fa-var-lastfm: "\f202"; -$fa-var-lastfm-square: "\f203"; -$fa-var-leaf: "\f06c"; -$fa-var-leanpub: "\f212"; -$fa-var-legal: "\f0e3"; -$fa-var-lemon-o: "\f094"; -$fa-var-level-down: "\f149"; -$fa-var-level-up: "\f148"; -$fa-var-life-bouy: "\f1cd"; -$fa-var-life-buoy: "\f1cd"; -$fa-var-life-ring: "\f1cd"; -$fa-var-life-saver: "\f1cd"; -$fa-var-lightbulb-o: "\f0eb"; -$fa-var-line-chart: "\f201"; -$fa-var-link: "\f0c1"; -$fa-var-linkedin: "\f0e1"; -$fa-var-linkedin-square: "\f08c"; -$fa-var-linode: "\f2b8"; -$fa-var-linux: "\f17c"; -$fa-var-list: "\f03a"; -$fa-var-list-alt: "\f022"; -$fa-var-list-ol: "\f0cb"; -$fa-var-list-ul: "\f0ca"; -$fa-var-location-arrow: "\f124"; -$fa-var-lock: "\f023"; -$fa-var-long-arrow-down: "\f175"; -$fa-var-long-arrow-left: "\f177"; -$fa-var-long-arrow-right: "\f178"; -$fa-var-long-arrow-up: "\f176"; -$fa-var-low-vision: "\f2a8"; -$fa-var-magic: "\f0d0"; -$fa-var-magnet: "\f076"; -$fa-var-mail-forward: "\f064"; -$fa-var-mail-reply: "\f112"; -$fa-var-mail-reply-all: "\f122"; -$fa-var-male: "\f183"; -$fa-var-map: "\f279"; -$fa-var-map-marker: "\f041"; -$fa-var-map-o: "\f278"; -$fa-var-map-pin: "\f276"; -$fa-var-map-signs: "\f277"; -$fa-var-mars: "\f222"; -$fa-var-mars-double: "\f227"; -$fa-var-mars-stroke: "\f229"; -$fa-var-mars-stroke-h: "\f22b"; -$fa-var-mars-stroke-v: "\f22a"; -$fa-var-maxcdn: "\f136"; -$fa-var-meanpath: "\f20c"; -$fa-var-medium: "\f23a"; -$fa-var-medkit: "\f0fa"; -$fa-var-meetup: "\f2e0"; -$fa-var-meh-o: "\f11a"; -$fa-var-mercury: "\f223"; -$fa-var-microchip: "\f2db"; -$fa-var-microphone: "\f130"; -$fa-var-microphone-slash: "\f131"; -$fa-var-minus: "\f068"; -$fa-var-minus-circle: "\f056"; -$fa-var-minus-square: "\f146"; -$fa-var-minus-square-o: "\f147"; -$fa-var-mixcloud: "\f289"; -$fa-var-mobile: "\f10b"; -$fa-var-mobile-phone: "\f10b"; -$fa-var-modx: "\f285"; -$fa-var-money: "\f0d6"; -$fa-var-moon-o: "\f186"; -$fa-var-mortar-board: "\f19d"; -$fa-var-motorcycle: "\f21c"; -$fa-var-mouse-pointer: "\f245"; -$fa-var-music: "\f001"; -$fa-var-navicon: "\f0c9"; -$fa-var-neuter: "\f22c"; -$fa-var-newspaper-o: "\f1ea"; -$fa-var-object-group: "\f247"; -$fa-var-object-ungroup: "\f248"; -$fa-var-odnoklassniki: "\f263"; -$fa-var-odnoklassniki-square: "\f264"; -$fa-var-opencart: "\f23d"; -$fa-var-openid: "\f19b"; -$fa-var-opera: "\f26a"; -$fa-var-optin-monster: "\f23c"; -$fa-var-outdent: "\f03b"; -$fa-var-pagelines: "\f18c"; -$fa-var-paint-brush: "\f1fc"; -$fa-var-paper-plane: "\f1d8"; -$fa-var-paper-plane-o: "\f1d9"; -$fa-var-paperclip: "\f0c6"; -$fa-var-paragraph: "\f1dd"; -$fa-var-paste: "\f0ea"; -$fa-var-pause: "\f04c"; -$fa-var-pause-circle: "\f28b"; -$fa-var-pause-circle-o: "\f28c"; -$fa-var-paw: "\f1b0"; -$fa-var-paypal: "\f1ed"; -$fa-var-pencil: "\f040"; -$fa-var-pencil-square: "\f14b"; -$fa-var-pencil-square-o: "\f044"; -$fa-var-percent: "\f295"; -$fa-var-phone: "\f095"; -$fa-var-phone-square: "\f098"; -$fa-var-photo: "\f03e"; -$fa-var-picture-o: "\f03e"; -$fa-var-pie-chart: "\f200"; -$fa-var-pied-piper: "\f2ae"; -$fa-var-pied-piper-alt: "\f1a8"; -$fa-var-pied-piper-pp: "\f1a7"; -$fa-var-pinterest: "\f0d2"; -$fa-var-pinterest-p: "\f231"; -$fa-var-pinterest-square: "\f0d3"; -$fa-var-plane: "\f072"; -$fa-var-play: "\f04b"; -$fa-var-play-circle: "\f144"; -$fa-var-play-circle-o: "\f01d"; -$fa-var-plug: "\f1e6"; -$fa-var-plus: "\f067"; -$fa-var-plus-circle: "\f055"; -$fa-var-plus-square: "\f0fe"; -$fa-var-plus-square-o: "\f196"; -$fa-var-podcast: "\f2ce"; -$fa-var-power-off: "\f011"; -$fa-var-print: "\f02f"; -$fa-var-product-hunt: "\f288"; -$fa-var-puzzle-piece: "\f12e"; -$fa-var-qq: "\f1d6"; -$fa-var-qrcode: "\f029"; -$fa-var-question: "\f128"; -$fa-var-question-circle: "\f059"; -$fa-var-question-circle-o: "\f29c"; -$fa-var-quora: "\f2c4"; -$fa-var-quote-left: "\f10d"; -$fa-var-quote-right: "\f10e"; -$fa-var-ra: "\f1d0"; -$fa-var-random: "\f074"; -$fa-var-ravelry: "\f2d9"; -$fa-var-rebel: "\f1d0"; -$fa-var-recycle: "\f1b8"; -$fa-var-reddit: "\f1a1"; -$fa-var-reddit-alien: "\f281"; -$fa-var-reddit-square: "\f1a2"; -$fa-var-refresh: "\f021"; -$fa-var-registered: "\f25d"; -$fa-var-remove: "\f00d"; -$fa-var-renren: "\f18b"; -$fa-var-reorder: "\f0c9"; -$fa-var-repeat: "\f01e"; -$fa-var-reply: "\f112"; -$fa-var-reply-all: "\f122"; -$fa-var-resistance: "\f1d0"; -$fa-var-retweet: "\f079"; -$fa-var-rmb: "\f157"; -$fa-var-road: "\f018"; -$fa-var-rocket: "\f135"; -$fa-var-rotate-left: "\f0e2"; -$fa-var-rotate-right: "\f01e"; -$fa-var-rouble: "\f158"; -$fa-var-rss: "\f09e"; -$fa-var-rss-square: "\f143"; -$fa-var-rub: "\f158"; -$fa-var-ruble: "\f158"; -$fa-var-rupee: "\f156"; -$fa-var-s15: "\f2cd"; -$fa-var-safari: "\f267"; -$fa-var-save: "\f0c7"; -$fa-var-scissors: "\f0c4"; -$fa-var-scribd: "\f28a"; -$fa-var-search: "\f002"; -$fa-var-search-minus: "\f010"; -$fa-var-search-plus: "\f00e"; -$fa-var-sellsy: "\f213"; -$fa-var-send: "\f1d8"; -$fa-var-send-o: "\f1d9"; -$fa-var-server: "\f233"; -$fa-var-share: "\f064"; -$fa-var-share-alt: "\f1e0"; -$fa-var-share-alt-square: "\f1e1"; -$fa-var-share-square: "\f14d"; -$fa-var-share-square-o: "\f045"; -$fa-var-shekel: "\f20b"; -$fa-var-sheqel: "\f20b"; -$fa-var-shield: "\f132"; -$fa-var-ship: "\f21a"; -$fa-var-shirtsinbulk: "\f214"; -$fa-var-shopping-bag: "\f290"; -$fa-var-shopping-basket: "\f291"; -$fa-var-shopping-cart: "\f07a"; -$fa-var-shower: "\f2cc"; -$fa-var-sign-in: "\f090"; -$fa-var-sign-language: "\f2a7"; -$fa-var-sign-out: "\f08b"; -$fa-var-signal: "\f012"; -$fa-var-signing: "\f2a7"; -$fa-var-simplybuilt: "\f215"; -$fa-var-sitemap: "\f0e8"; -$fa-var-skyatlas: "\f216"; -$fa-var-skype: "\f17e"; -$fa-var-slack: "\f198"; -$fa-var-sliders: "\f1de"; -$fa-var-slideshare: "\f1e7"; -$fa-var-smile-o: "\f118"; -$fa-var-snapchat: "\f2ab"; -$fa-var-snapchat-ghost: "\f2ac"; -$fa-var-snapchat-square: "\f2ad"; -$fa-var-snowflake-o: "\f2dc"; -$fa-var-soccer-ball-o: "\f1e3"; -$fa-var-sort: "\f0dc"; -$fa-var-sort-alpha-asc: "\f15d"; -$fa-var-sort-alpha-desc: "\f15e"; -$fa-var-sort-amount-asc: "\f160"; -$fa-var-sort-amount-desc: "\f161"; -$fa-var-sort-asc: "\f0de"; -$fa-var-sort-desc: "\f0dd"; -$fa-var-sort-down: "\f0dd"; -$fa-var-sort-numeric-asc: "\f162"; -$fa-var-sort-numeric-desc: "\f163"; -$fa-var-sort-up: "\f0de"; -$fa-var-soundcloud: "\f1be"; -$fa-var-space-shuttle: "\f197"; -$fa-var-spinner: "\f110"; -$fa-var-spoon: "\f1b1"; -$fa-var-spotify: "\f1bc"; -$fa-var-square: "\f0c8"; -$fa-var-square-o: "\f096"; -$fa-var-stack-exchange: "\f18d"; -$fa-var-stack-overflow: "\f16c"; -$fa-var-star: "\f005"; -$fa-var-star-half: "\f089"; -$fa-var-star-half-empty: "\f123"; -$fa-var-star-half-full: "\f123"; -$fa-var-star-half-o: "\f123"; -$fa-var-star-o: "\f006"; -$fa-var-steam: "\f1b6"; -$fa-var-steam-square: "\f1b7"; -$fa-var-step-backward: "\f048"; -$fa-var-step-forward: "\f051"; -$fa-var-stethoscope: "\f0f1"; -$fa-var-sticky-note: "\f249"; -$fa-var-sticky-note-o: "\f24a"; -$fa-var-stop: "\f04d"; -$fa-var-stop-circle: "\f28d"; -$fa-var-stop-circle-o: "\f28e"; -$fa-var-street-view: "\f21d"; -$fa-var-strikethrough: "\f0cc"; -$fa-var-stumbleupon: "\f1a4"; -$fa-var-stumbleupon-circle: "\f1a3"; -$fa-var-subscript: "\f12c"; -$fa-var-subway: "\f239"; -$fa-var-suitcase: "\f0f2"; -$fa-var-sun-o: "\f185"; -$fa-var-superpowers: "\f2dd"; -$fa-var-superscript: "\f12b"; -$fa-var-support: "\f1cd"; -$fa-var-table: "\f0ce"; -$fa-var-tablet: "\f10a"; -$fa-var-tachometer: "\f0e4"; -$fa-var-tag: "\f02b"; -$fa-var-tags: "\f02c"; -$fa-var-tasks: "\f0ae"; -$fa-var-taxi: "\f1ba"; -$fa-var-telegram: "\f2c6"; -$fa-var-television: "\f26c"; -$fa-var-tencent-weibo: "\f1d5"; -$fa-var-terminal: "\f120"; -$fa-var-text-height: "\f034"; -$fa-var-text-width: "\f035"; -$fa-var-th: "\f00a"; -$fa-var-th-large: "\f009"; -$fa-var-th-list: "\f00b"; -$fa-var-themeisle: "\f2b2"; -$fa-var-thermometer: "\f2c7"; -$fa-var-thermometer-0: "\f2cb"; -$fa-var-thermometer-1: "\f2ca"; -$fa-var-thermometer-2: "\f2c9"; -$fa-var-thermometer-3: "\f2c8"; -$fa-var-thermometer-4: "\f2c7"; -$fa-var-thermometer-empty: "\f2cb"; -$fa-var-thermometer-full: "\f2c7"; -$fa-var-thermometer-half: "\f2c9"; -$fa-var-thermometer-quarter: "\f2ca"; -$fa-var-thermometer-three-quarters: "\f2c8"; -$fa-var-thumb-tack: "\f08d"; -$fa-var-thumbs-down: "\f165"; -$fa-var-thumbs-o-down: "\f088"; -$fa-var-thumbs-o-up: "\f087"; -$fa-var-thumbs-up: "\f164"; -$fa-var-ticket: "\f145"; -$fa-var-times: "\f00d"; -$fa-var-times-circle: "\f057"; -$fa-var-times-circle-o: "\f05c"; -$fa-var-times-rectangle: "\f2d3"; -$fa-var-times-rectangle-o: "\f2d4"; -$fa-var-tint: "\f043"; -$fa-var-toggle-down: "\f150"; -$fa-var-toggle-left: "\f191"; -$fa-var-toggle-off: "\f204"; -$fa-var-toggle-on: "\f205"; -$fa-var-toggle-right: "\f152"; -$fa-var-toggle-up: "\f151"; -$fa-var-trademark: "\f25c"; -$fa-var-train: "\f238"; -$fa-var-transgender: "\f224"; -$fa-var-transgender-alt: "\f225"; -$fa-var-trash: "\f1f8"; -$fa-var-trash-o: "\f014"; -$fa-var-tree: "\f1bb"; -$fa-var-trello: "\f181"; -$fa-var-tripadvisor: "\f262"; -$fa-var-trophy: "\f091"; -$fa-var-truck: "\f0d1"; -$fa-var-try: "\f195"; -$fa-var-tty: "\f1e4"; -$fa-var-tumblr: "\f173"; -$fa-var-tumblr-square: "\f174"; -$fa-var-turkish-lira: "\f195"; -$fa-var-tv: "\f26c"; -$fa-var-twitch: "\f1e8"; -$fa-var-twitter: "\f099"; -$fa-var-twitter-square: "\f081"; -$fa-var-umbrella: "\f0e9"; -$fa-var-underline: "\f0cd"; -$fa-var-undo: "\f0e2"; -$fa-var-universal-access: "\f29a"; -$fa-var-university: "\f19c"; -$fa-var-unlink: "\f127"; -$fa-var-unlock: "\f09c"; -$fa-var-unlock-alt: "\f13e"; -$fa-var-unsorted: "\f0dc"; -$fa-var-upload: "\f093"; -$fa-var-usb: "\f287"; -$fa-var-usd: "\f155"; -$fa-var-user: "\f007"; -$fa-var-user-circle: "\f2bd"; -$fa-var-user-circle-o: "\f2be"; -$fa-var-user-md: "\f0f0"; -$fa-var-user-o: "\f2c0"; -$fa-var-user-plus: "\f234"; -$fa-var-user-secret: "\f21b"; -$fa-var-user-times: "\f235"; -$fa-var-users: "\f0c0"; -$fa-var-vcard: "\f2bb"; -$fa-var-vcard-o: "\f2bc"; -$fa-var-venus: "\f221"; -$fa-var-venus-double: "\f226"; -$fa-var-venus-mars: "\f228"; -$fa-var-viacoin: "\f237"; -$fa-var-viadeo: "\f2a9"; -$fa-var-viadeo-square: "\f2aa"; -$fa-var-video-camera: "\f03d"; -$fa-var-vimeo: "\f27d"; -$fa-var-vimeo-square: "\f194"; -$fa-var-vine: "\f1ca"; -$fa-var-vk: "\f189"; -$fa-var-volume-control-phone: "\f2a0"; -$fa-var-volume-down: "\f027"; -$fa-var-volume-off: "\f026"; -$fa-var-volume-up: "\f028"; -$fa-var-warning: "\f071"; -$fa-var-wechat: "\f1d7"; -$fa-var-weibo: "\f18a"; -$fa-var-weixin: "\f1d7"; -$fa-var-whatsapp: "\f232"; -$fa-var-wheelchair: "\f193"; -$fa-var-wheelchair-alt: "\f29b"; -$fa-var-wifi: "\f1eb"; -$fa-var-wikipedia-w: "\f266"; -$fa-var-window-close: "\f2d3"; -$fa-var-window-close-o: "\f2d4"; -$fa-var-window-maximize: "\f2d0"; -$fa-var-window-minimize: "\f2d1"; -$fa-var-window-restore: "\f2d2"; -$fa-var-windows: "\f17a"; -$fa-var-won: "\f159"; -$fa-var-wordpress: "\f19a"; -$fa-var-wpbeginner: "\f297"; -$fa-var-wpexplorer: "\f2de"; -$fa-var-wpforms: "\f298"; -$fa-var-wrench: "\f0ad"; -$fa-var-xing: "\f168"; -$fa-var-xing-square: "\f169"; -$fa-var-y-combinator: "\f23b"; -$fa-var-y-combinator-square: "\f1d4"; -$fa-var-yahoo: "\f19e"; -$fa-var-yc: "\f23b"; -$fa-var-yc-square: "\f1d4"; -$fa-var-yelp: "\f1e9"; -$fa-var-yen: "\f157"; -$fa-var-yoast: "\f2b1"; -$fa-var-youtube: "\f167"; -$fa-var-youtube-play: "\f16a"; -$fa-var-youtube-square: "\f166"; - diff --git a/v3/css/font-awesome-4.7.0/scss/font-awesome.scss b/v3/css/font-awesome-4.7.0/scss/font-awesome.scss deleted file mode 100644 index f1c83aa..0000000 --- a/v3/css/font-awesome-4.7.0/scss/font-awesome.scss +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */ - -@import "variables"; -@import "mixins"; -@import "path"; -@import "core"; -@import "larger"; -@import "fixed-width"; -@import "list"; -@import "bordered-pulled"; -@import "animated"; -@import "rotated-flipped"; -@import "stacked"; -@import "icons"; -@import "screen-reader"; diff --git a/v3/css/github-corners/corner.css b/v3/css/github-corners/corner.css deleted file mode 100644 index 2a2eaa4..0000000 --- a/v3/css/github-corners/corner.css +++ /dev/null @@ -1,39 +0,0 @@ -.github-corner:hover .octo-arm { - animation: octocat-wave 560ms ease-in-out; -} - -@keyframes octocat-wave { - 0% { - transform: rotate(0deg); - } - - 20% { - transform: rotate(-25deg); - } - - 40% { - transform: rotate(10deg); - } - - 60% { - transform: rotate(-25deg); - } - - 80% { - transform: rotate(10deg); - } - - 100% { - transform: rotate(0deg); - } -} - -@media (max-width: 500px) { - .github-corner:hover .octo-arm { - animation: none; - } - - .github-corner .octo-arm { - animation: octocat-wave 560ms ease-in-out; - } -} diff --git a/v3/css/github-corners/styles.css b/v3/css/github-corners/styles.css deleted file mode 100644 index ab3cf63..0000000 --- a/v3/css/github-corners/styles.css +++ /dev/null @@ -1,132 +0,0 @@ -/** - * GitHub Corners, page css - * Author: Tim Holman - */ - -@import "corner.css"; - -* { - box-sizing: border-box; -} - -html, body { - font-family: Helvetica, Arial, sans-serif; - background: #fff; - margin: 0px; -} - -h1 { - font-size: 30px; -} - -p { - font-size: 16px; - font-weight: 100; - line-height: 24px; -} - -.wrapper { - padding-right: 20px; - padding-left: 20px; - padding-top: 50px; - max-width: 580px; - margin: auto; -} - -/** - * Demo Code - */ - -.version { - border: 2px solid #eee; - margin-top: 25px; - height: 200px; - display: -webkit-flex; - display: flex; -} - -.version-section { - padding: 10px; - height: 100%; -} - -.version-section.dark { - background: #151513; -} - -.demo { - border-right: 2px solid #eee; - position: relative; - width: 200px; -} - -.code { - vertical-align: top; - width: 336px; -} - -.code textarea { - border: 2px solid #eee; - outline: 0px; - height: 100%; - width: 100%; - font-family: monospace; - font-size: 10px; -} - -/** - * Footer - */ - -footer { - width: 100%; - margin-top: 25px; - margin-bottom: 100px; - background: #F3F3F3; - height: 50px; - padding: 15px; - padding-left: 25px; - padding-right: 25px; - color: #2D2D2D; - font-size: 13px; - letter-spacing: 1px; - font-family: monospace; -} - -footer a { - color: #2d2d2d; -} - -footer span { - margin-left: 10px; - margin-right: 10px; -} - -.twitter-share-button { - margin-bottom: -4px; -} - -/** - * Media - */ - -@media (max-width: 500px) { - - footer { - text-align: center; - height: auto; - line-height: 30px; - } - - footer span { - display: none; - } - - footer a { - display: block; - } - - .twitter-share-button { - margin-bottom: -8px; - } -} diff --git a/v3/css/images/BC_Features.png b/v3/css/images/BC_Features.png deleted file mode 100644 index c3f7a5f..0000000 Binary files a/v3/css/images/BC_Features.png and /dev/null differ diff --git a/v3/css/images/Banner.png b/v3/css/images/Banner.png deleted file mode 100644 index 9faebb9..0000000 Binary files a/v3/css/images/Banner.png and /dev/null differ diff --git a/v3/css/images/Brainchop3D.gif b/v3/css/images/Brainchop3D.gif deleted file mode 100644 index 89aecc4..0000000 Binary files a/v3/css/images/Brainchop3D.gif and /dev/null differ diff --git a/v3/css/images/BrainchopBanner.png b/v3/css/images/BrainchopBanner.png deleted file mode 100644 index faf5815..0000000 Binary files a/v3/css/images/BrainchopBanner.png and /dev/null differ diff --git a/v3/css/images/BrainchopMoreRobustModels.gif b/v3/css/images/BrainchopMoreRobustModels.gif deleted file mode 100644 index ff1b8ff..0000000 Binary files a/v3/css/images/BrainchopMoreRobustModels.gif and /dev/null differ diff --git a/v3/css/images/BrainchopThreeJS.gif b/v3/css/images/BrainchopThreeJS.gif deleted file mode 100644 index 95316d0..0000000 Binary files a/v3/css/images/BrainchopThreeJS.gif and /dev/null differ diff --git a/v3/css/images/DL_Arch.png b/v3/css/images/DL_Arch.png deleted file mode 100644 index fbc471e..0000000 Binary files a/v3/css/images/DL_Arch.png and /dev/null differ diff --git a/v3/css/images/Input3DEnhancements.gif b/v3/css/images/Input3DEnhancements.gif deleted file mode 100644 index c398ef4..0000000 Binary files a/v3/css/images/Input3DEnhancements.gif and /dev/null differ diff --git a/v3/css/images/Poster.png b/v3/css/images/Poster.png deleted file mode 100644 index ee9f522..0000000 Binary files a/v3/css/images/Poster.png and /dev/null differ diff --git a/v3/css/images/SimpleUI.png b/v3/css/images/SimpleUI.png deleted file mode 100644 index 538a9bf..0000000 Binary files a/v3/css/images/SimpleUI.png and /dev/null differ diff --git a/v3/css/images/brainchop_Arch.png b/v3/css/images/brainchop_Arch.png deleted file mode 100644 index 7a25129..0000000 Binary files a/v3/css/images/brainchop_Arch.png and /dev/null differ diff --git a/v3/css/images/brainchop_Arch2.png b/v3/css/images/brainchop_Arch2.png deleted file mode 100644 index 8fad1d0..0000000 Binary files a/v3/css/images/brainchop_Arch2.png and /dev/null differ diff --git a/v3/css/images/brainchop_Arch3.png b/v3/css/images/brainchop_Arch3.png deleted file mode 100644 index d719a27..0000000 Binary files a/v3/css/images/brainchop_Arch3.png and /dev/null differ diff --git a/v3/css/logo/TReNDS_logo.jpg b/v3/css/logo/TReNDS_logo.jpg deleted file mode 100644 index bc0c69f..0000000 Binary files a/v3/css/logo/TReNDS_logo.jpg and /dev/null differ diff --git a/v3/css/logo/brainchop_logo.png b/v3/css/logo/brainchop_logo.png deleted file mode 100644 index 79aa9e5..0000000 Binary files a/v3/css/logo/brainchop_logo.png and /dev/null differ diff --git a/v3/css/media/brainchopV1_3.mp4 b/v3/css/media/brainchopV1_3.mp4 deleted file mode 100644 index 0ad5944..0000000 Binary files a/v3/css/media/brainchopV1_3.mp4 and /dev/null differ diff --git a/v3/css/news/ISBI_2023.png b/v3/css/news/ISBI_2023.png deleted file mode 100644 index d936d12..0000000 Binary files a/v3/css/news/ISBI_2023.png and /dev/null differ diff --git a/v3/css/news/ISBI_2024.jpeg b/v3/css/news/ISBI_2024.jpeg deleted file mode 100644 index 74100c6..0000000 Binary files a/v3/css/news/ISBI_2024.jpeg and /dev/null differ diff --git a/v3/css/news/JOSS_Logo.png b/v3/css/news/JOSS_Logo.png deleted file mode 100644 index be23d7a..0000000 Binary files a/v3/css/news/JOSS_Logo.png and /dev/null differ diff --git a/v3/css/news/OHBM_2023.jpeg b/v3/css/news/OHBM_2023.jpeg deleted file mode 100644 index 2c41ae1..0000000 Binary files a/v3/css/news/OHBM_2023.jpeg and /dev/null differ diff --git a/v3/css/news/Pytorch_Poster.jpg b/v3/css/news/Pytorch_Poster.jpg deleted file mode 100644 index c9f1194..0000000 Binary files a/v3/css/news/Pytorch_Poster.jpg and /dev/null differ diff --git a/v3/css/news/TF_CommunityAward.png b/v3/css/news/TF_CommunityAward.png deleted file mode 100644 index e5b3c68..0000000 Binary files a/v3/css/news/TF_CommunityAward.png and /dev/null differ diff --git a/v3/css/news/TF_show_tell.png b/v3/css/news/TF_show_tell.png deleted file mode 100644 index 526f2fd..0000000 Binary files a/v3/css/news/TF_show_tell.png and /dev/null differ diff --git a/v3/css/style.css b/v3/css/style.css deleted file mode 100644 index a2eadee..0000000 --- a/v3/css/style.css +++ /dev/null @@ -1,85 +0,0 @@ -.bt_1 button:active{ - background: #396D9E !important; -} - -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(1) > div{ - border-radius: 20px; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line{ - background-color: #F3F3F6; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_toolbar.toolbarclass.webix_layout_toolbar > div > div > div.webix_view.webix_control.webix_el_label > div > p{ - margin: 0!important; - text-transform: uppercase; - font-weight: bold; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_toolbar.toolbarclass.webix_layout_toolbar > div > div > div.webix_view.webix_control.webix_el_label > div > p > span{ - margin: 0!important; - text-transform: uppercase; - font-weight: normal; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(1) > div:nth-child(2) > div > div.webix_view.webix_control.webix_el_select > div > select{ - background: white; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(1) > div:nth-child(1) > div > div.webix_view.webix_layout_line > div.webix_view.webix_control.webix_el_button.webix_uploader.webix_secondary.bt_1 > div > button, body > div.webix_view.webix_layout_line > div.webix_view.webix_toolbar.toolbarclass.webix_layout_toolbar > div > div > div.webix_view.webix_control.webix_el_button.webix_secondary.bt_1 > div > button{ - color:#FFFFFF; - font-size: 13px; - background: black!important; - border-radius: 10px!important; - color: white!important; - box-shadow: none !important; - border: none !important; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(2){ - border-radius: 20px; - padding: 20px 0; - background: white; - max-height: fit-content!important; - height: auto!important; - border-width: 1px; - -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(3){ - border-radius: 20px; - padding: 20px 0; - background: white; - border-width: 1px; - max-height: fit-content!important; - height: auto!important; -} -#feedbackId{ - color: black; -} -#githubId{ - color: black; -} - -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(2) > div:nth-child(2){ - max-height: fit-content!important; - height: auto!important; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line > div:nth-child(3) > div:nth-child(2){ - max-height: fit-content!important; - height: auto!important; -} -body > div.webix_view.webix_layout_line > div.webix_view.webix_layout_line{ - margin-top: 0px !important; -} -.webix_view.webix_control .webix_disabled_box .webix_button{ - color: #000000!important; - background: #ffffff!important; - text-shadow: none!important; - border: 1px solid!important; - border-radius: 10px!important; -} -.bt_1 button.webix_button{ - font-size: 13px; - background: black!important; - border-radius: 10px!important; - color: white!important; - box-shadow: none !important; - border: none !important; -} - - - diff --git a/v3/css/svg/Artboard2.svg b/v3/css/svg/Artboard2.svg deleted file mode 100644 index 94f2543..0000000 --- a/v3/css/svg/Artboard2.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/v3/css/svg/BRAINCHOP.svg b/v3/css/svg/BRAINCHOP.svg deleted file mode 100644 index 1343912..0000000 --- a/v3/css/svg/BRAINCHOP.svg +++ /dev/null @@ -1 +0,0 @@ -BRAINCHOP diff --git a/v3/css/svg/info-circle-solid.svg b/v3/css/svg/info-circle-solid.svg deleted file mode 100644 index e38914a..0000000 --- a/v3/css/svg/info-circle-solid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/v3/css/svg/out3D-1.svg b/v3/css/svg/out3D-1.svg deleted file mode 100644 index 4b28bf1..0000000 --- a/v3/css/svg/out3D-1.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/v3/css/svg/outChart-1.svg b/v3/css/svg/outChart-1.svg deleted file mode 100644 index a3b69e4..0000000 --- a/v3/css/svg/outChart-1.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/v3/css/w3.css b/v3/css/w3.css deleted file mode 100644 index fd89e8d..0000000 --- a/v3/css/w3.css +++ /dev/null @@ -1,235 +0,0 @@ -/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */ -html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit} -/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */ -html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} -article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item} -audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline} -audio:not([controls]){display:none;height:0}[hidden],template{display:none} -a{background-color:transparent}a:active,a:hover{outline-width:0} -abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted} -b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000} -small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} -sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none} -code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible} -button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold} -button,input{overflow:visible}button,select{text-transform:none} -button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button} -button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0} -button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText} -fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} -legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto} -[type=checkbox],[type=radio]{padding:0} -[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto} -[type=search]{-webkit-appearance:textfield;outline-offset:-2px} -[type=search]::-webkit-search-decoration{-webkit-appearance:none} -::-webkit-file-upload-button{-webkit-appearance:button;font:inherit} -/* End extract */ -html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden} -h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px} -.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace} -h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px} -hr{border:0;border-top:1px solid #eee;margin:20px 0} -.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit} -.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc} -.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1} -.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1} -.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center} -.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top} -.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px} -.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap} -.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)} -.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} -.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none} -.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none} -.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%} -.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none} -.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block} -.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s} -.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%} -.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc} -.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer} -.w3-dropdown-hover:hover .w3-dropdown-content{display:block} -.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000} -.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000} -.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1} -.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px} -.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto} -.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%} -.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%} -.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px} -.w3-main,#main{transition:margin-left .4s} -.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)} -.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px} -.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto} -.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0} -.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left} -.w3-bar .w3-button{white-space:normal} -.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0} -.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%} -.w3-responsive{display:block;overflow-x:auto} -.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before, -.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both} -.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%} -.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%} -.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%} -.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%} -@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%} -.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%} -.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}} -@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%} -.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%} -.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}} -.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px} -.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px} -.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell} -.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom} -.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important} -@media (max-width:1205px){.w3-auto{max-width:95%}} -@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px} -.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative} -.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center} -.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}} -@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}} -@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}} -@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}} -@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}} -.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0} -.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2} -.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0} -.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0} -.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)} -.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)} -.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)} -.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} -.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} -.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none} -.w3-display-position{position:absolute} -.w3-circle{border-radius:50%} -.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px} -.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px} -.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px} -.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px} -.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word} -.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%} -.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)} -.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)} -.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}} -.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}} -.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}} -.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}} -.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}} -.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}} -.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}} -.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}} -.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important} -.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1} -.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75} -.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)} -.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)} -.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)} -.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important} -.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important} -.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important} -.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important} -.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important} -.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important} -.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important} -.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important} -.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important} -.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important} -.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important} -.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important} -.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important} -.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important} -.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important} -.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important} -.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important} -.w3-left{float:left!important}.w3-right{float:right!important} -.w3-button:hover{color:#000!important;background-color:#ccc!important} -.w3-transparent,.w3-hover-none:hover{background-color:transparent!important} -.w3-hover-none:hover{box-shadow:none!important} -/* Colors */ -.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important} -.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important} -.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important} -.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important} -.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important} -.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important} -.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important} -.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} -.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important} -.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important} -.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important} -.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important} -.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important} -.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important} -.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important} -.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important} -.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important} -.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important} -.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important} -.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important} -.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important} -.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important} -.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important} -.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important} -.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important} -.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important} -.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important} -.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important} -.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important} -.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important} -.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important} -.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important} -.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important} -.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important} -.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important} -.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important} -.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important} -.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important} -.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important} -.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important} -.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important} -.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important} -.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important} -.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important} -.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important} -.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important} -.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important} -.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important} -.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important} -.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important} -.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important} -.w3-text-white,.w3-hover-text-white:hover{color:#fff!important} -.w3-text-black,.w3-hover-text-black:hover{color:#000!important} -.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important} -.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important} -.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important} -.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important} -.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important} -.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important} -.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important} -.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important} -.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important} -.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important} -.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important} -.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important} -.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important} -.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important} -.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important} -.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important} -.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important} -.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important} -.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important} -.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important} -.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important} -.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important} -.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important} -.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important} -.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important} -.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important} -.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important} -.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important} -.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important} -.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important} -.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} \ No newline at end of file diff --git a/v3/data/labels.nii.gz b/v3/data/labels.nii.gz deleted file mode 100644 index e7dbca9..0000000 Binary files a/v3/data/labels.nii.gz and /dev/null differ diff --git a/v3/data/t1_c.nii.gz b/v3/data/t1_c.nii.gz deleted file mode 100644 index ed11d44..0000000 Binary files a/v3/data/t1_c.nii.gz and /dev/null differ diff --git a/v3/docs/CODE_OF_CONDUCT.md b/v3/docs/CODE_OF_CONDUCT.md deleted file mode 100644 index 18c9147..0000000 --- a/v3/docs/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/v3/docs/CONTRIBUTING.md b/v3/docs/CONTRIBUTING.md deleted file mode 100644 index f167c29..0000000 --- a/v3/docs/CONTRIBUTING.md +++ /dev/null @@ -1,89 +0,0 @@ -Thank you for taking the time to contribute to Brainchop!. - -The following is our set of guidelines to ease your contribution. - -#### Table Of Contents - -[Code of Conduct](#code-of-conduct) - -[Having a Question](#Having-a-question) - -[ Project Structure ](#Project-Structure) - -[How To Contribute?](#how-can-i-contribute) - * [Reporting Bugs](#reporting-bugs) - * [Your First Code Contribution](#your-first-code-contribution) - - - - -## Code of Conduct - -This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/neuroneural/brainchop/wiki/CODE-OF-CONDUCT). By participating, you are expected to uphold this code. Please report unacceptable behavior to [brainchop@github.com](mailto:brainchop@github.com). - -## Having a question? - -> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. - -* [Brainchop Discussions Board](https://github.com/neuroneural/brainchop/discussions) -* [Brainchop Q&A](https://github.com/neuroneural/brainchop/discussions/categories/q-a) - - - -## Project Structure - - * **Root** - * style - * lib - * js - * mainMeshNetFunctions.js ( Brainchop main functions) - * mainNiftiReadingFunctions.js (Nifti functions) - * mainParameters.js - * python - * MRI (data) - * ExperimentalMode - * ModelToLoad (Models) - * model11_gw_ae - * colorLUT.json (Seg ROI Color Data) - * labels.json (Seg ROI Labels) - * group1-shard1of1.bin (Model Weights Binary file) - * model.json (Model JSON file) - - - -## How Can I Contribute? - -### Reporting Bugs - -Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). To report a bug create an issue to explain the problem and include additional details to help maintainers reproduce the problem: - -* **Use a clear and descriptive title** for the issue to identify the problem. -* **Describe the exact steps which reproduce the problem** in as many details as possible. -* **Provide specific examples to demonstrate the steps**. -* **Describe the behavior you observed after following the steps** -* **Explain which behavior you expected to see instead and why.** -* **Include screenshots and animated GIFs** - - -Include details about your configuration and environment: - -* **Which version of OS, Browser, GPU, CPU you are using**? -* **What size of RAM you are using**? -* **Which model you used with the problem**? - - - - -## Your First Code Contribution - -### Pull Requests - - -Please follow these steps to have your contribution considered by the maintainers: - -1. Fork Brainchop repository to have a copy on your github. -2. Push your updates to the forked Brainchop repository on your github. -3. Into your forked Brainchop repository create a pull request, it should show you the base repository (original one) and the head repository (forked one), choose master branch for each and create pull request -4. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing
What if the status checks are failing?If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.
- -5. Don't delete your forked repository until your pull request approved and merged. diff --git a/v3/docs/index.rst b/v3/docs/index.rst deleted file mode 100644 index 9a98640..0000000 --- a/v3/docs/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -Brainchop -=========== - -Brainchop is a client-side web-application for automatic segmentation of MRI volumes that brings automatic volumetric segmentation capability to neuroimaging by running a robustly pre-trained deep learning model. The app does not require technical sophistication from the user and is designed for locally and privately segmenting user’s T1 volumes. Results of the segmentation may be easily saved locally after the computation. An intuitive interactive interface that does not require any special training nor specific instruction to run enables access to a state of the art deep learning brain segmentation for anyone with a modern browser (e.g. Firefox, Chrome etc) and commonly available hardware. Additionally, we make implementation of brainchop freely available releasing its pure Javascript code as open-source. - -.. toctree:: - :maxdepth: 2 - - About - System-Requirements - Local-Setup - Input-Data - Inference-Model - Data-Postprocessing - Brainchop-Zoo - Performance - Contribution-Guidelines - - -Resources -========= - -* **Demo**: Brainchop (https://neuroneural.github.io/brainchop/). -* **Project source**: Brainchop (https://github.com/neuroneural/brainchop). - diff --git a/v3/experimental_mode/CanvasVersion/MRI Sample/labels.nii.gz b/v3/experimental_mode/CanvasVersion/MRI Sample/labels.nii.gz deleted file mode 100644 index e7dbca9..0000000 Binary files a/v3/experimental_mode/CanvasVersion/MRI Sample/labels.nii.gz and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/MRI Sample/t1_c.nii.gz b/v3/experimental_mode/CanvasVersion/MRI Sample/t1_c.nii.gz deleted file mode 100644 index ed11d44..0000000 Binary files a/v3/experimental_mode/CanvasVersion/MRI Sample/t1_c.nii.gz and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/README.md b/v3/experimental_mode/CanvasVersion/README.md deleted file mode 100644 index 722c0ec..0000000 --- a/v3/experimental_mode/CanvasVersion/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# 3D Brain Segmentation v1.0.0 -Mohamed Masoud - Sergey Plis - 2021 - - -## Whole Brain Inference at the Browser -Demo shows segmenation of 3D brain MRI at the browser based on a pretrained MeshNet model. - -Main settings - -input_shape=(1, 38, 38, 38, 1) - - -To run the server: - -Open a terminal window. - -Navigate to the directory root directory . - -Execute the command to start the server. - For Python 2 run server with free port 80xx (e.g. `python -m SimpleHTTPServer 8020` ) - For Python 3 run server with free port 80xx (e.g. `python -m http.server 8020` ) - - - - -In the browser url - - `http://localhost:8020/` - - - - Open browser console by press F12 to see the sample outputs - - - click on Browse File button, and navigate to "MRI Sample" folder - - - Please DON'T change batch size, it is static to 1 for now. - - - - -## Demo - - -![Interface](https://github.com/Mmasoud1/Portfolios/blob/master/ShowMe/BrainInference/Compare2_3DCC.gif) - - - diff --git a/v3/experimental_mode/CanvasVersion/index.html b/v3/experimental_mode/CanvasVersion/index.html deleted file mode 100644 index b93a27a..0000000 --- a/v3/experimental_mode/CanvasVersion/index.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - -

MeshNet timing with tfjs for whole Brain

- -

Measure the performance of tfjs when loading a pretrained MeshNet model in the browser to make whole brain inference.

- -

Select NIfTI file(*.nii, *.nii.gz) :

-

Select NIfTI label file(*.nii, *.nii.gz) :

- -
-
-
-
-
-
-
-

-

-

-

-
-
-
-
-
-
- -
- - - - - - -

Slice :

-
- -
- - - - - - - - - - - - - - - - -

Batch Size :

Num of Channels :

Num of Classes :

Background Label :

Num of Overlapped Batches :

-

Metrics: - -

-
-
- - - -

- - -

-

-

- - - - \ No newline at end of file diff --git a/v3/experimental_mode/CanvasVersion/js/mainMeshNetFunctions.js b/v3/experimental_mode/CanvasVersion/js/mainMeshNetFunctions.js deleted file mode 100644 index 97a25c6..0000000 --- a/v3/experimental_mode/CanvasVersion/js/mainMeshNetFunctions.js +++ /dev/null @@ -1,1506 +0,0 @@ -/* -========================================================= -* 3D xSegmentation Demo - v1.0.0 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, 38, 38, 38, 1] -* Model : Meshnet -* -* Author: Mohamed Masoud , (Sergey Plis Lab) - 2021 -========================================================= - - - -========================================================= - 3D Brain Segmentation -=========================================================*/ - -(function(){ - - - allOutputSlices = []; - maxLabel = 0; - allOutputSlices2DCC = []; - allOutputSlices3DCC = []; - - // Return 1-Dim Array of pixel value, this 1 dim represent one channel - getSliceData1D = (sliceIdx, niftiHeader, niftiImage) => { - // get nifti dimensions - let cols = niftiHeader.dims[1]; // Slice width - let rows = niftiHeader.dims[2]; // Slice height - - let typedData; - - if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedData = new Uint8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedData = new Int16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedData = new Int32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedData = new Float32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedData = new Float64Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedData = new Int8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedData = new Uint16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedData = new Uint32Array(niftiImage); - } else { - return; - } - - // offset to specified slice - let sliceSize = cols * rows; - - let sliceOffset = sliceSize * sliceIdx; - - let data1DimArr = []; - - // draw pixels - for (let row = 0; row < rows; row++) { - let rowOffset = row * cols; - - for (let col = 0; col < cols; col++) { - let offset = sliceOffset + rowOffset + col; - let value = typedData[offset]; - // Create 1Dim Array of pixel value, this 1 dim represent one channel - data1DimArr[(rowOffset + col)] = value & 0xFF; - - } - } - - return data1DimArr; - } - - // to use with ml5j - computeConfusionMatrix = (trueLabels, predictedLabels) => { - const CM = ConfusionMatrix.fromLabels(trueLabels, predictedLabels); - return CM.getAccuracy(); - - } - - // to use with bci.js - compConfusionMat = (predictedLabels, trueLabels) => { - const CM = confusionMatrix(predictedLabels, trueLabels); - return accuracy(CM); - - } - - generateColors = (s, l, num_colors) => { - let colors = [] - let delta = Math.trunc(360 / num_colors) - - for (let i = 0; i < num_colors; i++) { - let h = i * delta - colors.push("hsla("+ h + "," + s +"%," + l+ "%" + ")") - } - - return colors - } - - getRgbObject = (rgbString) => { - - let RGB = {}; - let rgbArray = rgbString; - rgbArray = rgbArray.replace(/[^\d,]/g, '').split(','); - let rgbKeys=["r","g","b"]; - RGB=rgbKeys.reduce((obj, key, index) => ({ ...obj, [key]:parseInt(rgbArray[index]) }), {}); - return RGB; - } - - hslToRgb = (hsl) => { - let sep = hsl.indexOf(",") > -1 ? "," : " "; - hsl = hsl.substr(5).split(")")[0].split(sep); - - if (hsl.indexOf("/") > -1) - hsl.splice(3,1); - - let h = hsl[0], - s = hsl[1].substr(0,hsl[1].length - 1) / 100, - l = hsl[2].substr(0,hsl[2].length - 1) / 100; - - - - let c = (1 - Math.abs(2 * l - 1)) * s, - x = c * (1 - Math.abs((h / 60) % 2 - 1)), - m = l - c/2, - r = 0, - g = 0, - b = 0; - if (0 <= h && h < 60) { - r = c; g = x; b = 0; - } else if (60 <= h && h < 120) { - r = x; g = c; b = 0; - } else if (120 <= h && h < 180) { - r = 0; g = c; b = x; - } else if (180 <= h && h < 240) { - r = 0; g = x; b = c; - } else if (240 <= h && h < 300) { - r = x; g = 0; b = c; - } else if (300 <= h && h < 360) { - r = c; g = 0; b = x; - } - r = Math.round((r + m) * 255); - g = Math.round((g + m) * 255); - b = Math.round((b + m) * 255); - - - return "rgb(" + r + "," + g + "," + b + ")"; - } - - // For Dice calculations - intersect = (ar1, ar2) => { - const intersection = []; - for(let i = 0; i < ar1.length ; i++) { - if(ar1[i] == ar2[i]) { - intersection.push(ar1[i]); - } - } - - return intersection; - } - - diceCoefficient = (ar1, ar2) => { - return ( 2 * intersect(ar1, ar2).length ) / ( ar1.length + ar2.length ); - } - - - drawConfusionMat = async(groundTruthLabels, predictedLabels, elemId) => { - - if(elemId == "accuracyTitleFilter3DCC") { - const values = await tfvis.metrics.confusionMatrix(groundTruthLabels, predictedLabels); - const data = { values }; - const surface = { name: 'Confusion Matrix 3D CC', tab: 'Charts' }; - tfvis.render.confusionMatrix(surface, data); - } - } - - calculateAccuracy = async(groundTruthLabels, predictedLabels, elemId) => { - document.getElementById(elemId).innerHTML = "Accuracy: " + - ( await tfvis.metrics.accuracy(groundTruthLabels, predictedLabels) ).toFixed(3); - - } - - drawOutputCanvas = (canvas, sliceIdx, niftiHeader, niftiImage, outputSlices) => { - - let n_classes = parseInt(document.getElementById("numOfClassesId").value); - let isColorEnable = document.getElementById("mriColoring").checked; - // get nifti dimensions - let cols = niftiHeader.dims[1]; - let rows = niftiHeader.dims[2]; - - // set canvas dimensions to nifti slice dimensions - canvas.width = cols; - canvas.height = rows; - - // make canvas image data - let ctx = canvas.getContext("2d"); - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - let colors = generateColors(100, 50, n_classes); - let bgLabelValue = parseInt(document.getElementById("bgLabelId").value); - - for (let pixelIdx = 0; pixelIdx < outputSlices[sliceIdx].length; pixelIdx++) { - if(isColorEnable) { - let color = { r: 0, g: 0, b: 0 }; - if(outputSlices[sliceIdx][pixelIdx] != bgLabelValue) { - color = getRgbObject(hslToRgb(colors[outputSlices[sliceIdx][pixelIdx]])); - } - canvasImageData.data[pixelIdx * 4] = color.r & 0xFF; - canvasImageData.data[pixelIdx * 4 + 1] = color.g & 0xFF; - canvasImageData.data[pixelIdx * 4 + 2] = color.b & 0xFF; - canvasImageData.data[pixelIdx * 4 + 3] = 0xFF; - - } else { - let value = Math.ceil(outputSlices[sliceIdx][pixelIdx]*255/(n_classes - 1)); - - canvasImageData.data[pixelIdx * 4] = value & 0xFF; - canvasImageData.data[pixelIdx * 4 + 1] = value & 0xFF; - canvasImageData.data[pixelIdx * 4 + 2] = value & 0xFF; - canvasImageData.data[pixelIdx * 4 + 3] = 0xFF; - } - - } - - ctx.putImageData(canvasImageData, 0, 0); - - // console.log("canvasImageData :", canvasImageData.data) - - let elemId = null; - - if(canvas.id == "outputCanvas") { - document.getElementById("predTitle").innerHTML = "Model Output"; - elemId = "accuracyTitleModelPred"; - } - - if(canvas.id == "out2dCC") { - document.getElementById("CC2DTitle").innerHTML = "Filter by 2D CC"; - elemId = "accuracyTitleFilter2DCC"; - } - - if(canvas.id == "out3dCC") { - document.getElementById("CC3DTitle").innerHTML = "Filter by 3D CC"; - elemId = "accuracyTitleFilter3DCC"; - } - - let gtCanvas = document.getElementById('gtCanvas'); - let ctxGt = gtCanvas.getContext("2d"); - - let trueLabels = ctxGt.getImageData(0, 0, gtCanvas.width, gtCanvas.height) - - // trueLabels.data is Uint8ClampedArray and need to convert to regular array first such that - // normalArray = Array.prototype.slice.call(trueLabels.data); - - - if(! isColorEnable){ - if(gtLabelLoaded) { - - const labels = tf.tensor1d( Array.prototype.slice.call(trueLabels.data) ); - const predictions = tf.tensor1d( Array.prototype.slice.call(canvasImageData.data) ); - - if(document.getElementById("metricsId").value == "DiceCoef") { - document.getElementById(elemId).innerHTML = "Dice Coef: " + - diceCoefficient( Array.prototype.slice.call(trueLabels.data) , - Array.prototype.slice.call(canvasImageData.data) - ).toFixed(4); - } - - - if(document.getElementById("metricsId").value == "Accuracy") { - calculateAccuracy(labels, predictions, elemId); - labels.dispose(); - predictions.dispose(); - - } - - - if(elemId = "accuracyTitleFilter3DCC") { - // drawConfusionMat(labels, predictions, elemId); - } - - } - - } else { - document.getElementById(elemId).innerHTML = ""; - } - - } - - - - - getMaxRegionMaskByContour= (canvasImageData) => { // slice matrix - - let mat = cv.matFromImageData(canvasImageData); - - let mask = cv.Mat.zeros(mat.cols, mat.rows, cv.CV_8UC3); - - let mask_gray = new cv.Mat (); - let mask_binary = new cv.Mat (); - let contours = new cv.MatVector(); - let hierarchy = new cv.Mat(); - - // Grayscale conversion - cv.cvtColor (mat, mask_gray, cv.COLOR_RGBA2GRAY, 0); - - cv.findContours(mask_gray, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE); // cv.CHAIN_APPROX_SIMPLE - - let maxContourArea = 0 - let maxContourAreaIdx = -1 - for (let i = 0; i < contours.size(); ++i) { - let cnt = contours.get(i); - let area = cv.contourArea(cnt, false) - if(maxContourArea < area){ - maxContourArea = area; - maxContourAreaIdx = i; - - } - - cnt.delete(); - } - - let color = new cv.Scalar(255, 255, 255); - cv.drawContours(mask, contours, maxContourAreaIdx, color, -1); //cv.LINE_8 - - cv.cvtColor (mask, mask_gray, cv.COLOR_RGBA2GRAY, 0); - cv.threshold (mask_gray, mask_binary, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU); - - - mat.delete(); - mask.delete(); - mask_gray.delete(); - - contours.delete(); - hierarchy.delete(); - - return mask_binary.data; - } - - - - postProcessSlices = (outputSlices) => { - let canvas = document.createElement("CANVAS"); - - // get nifti dimensions - let cols = niftiHeader.dims[1]; - let rows = niftiHeader.dims[2]; - - // set canvas dimensions to nifti slice dimensions - canvas.width = cols; - canvas.height = rows; - - // make canvas image data - let ctx = canvas.getContext("2d"); - - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - let bgLabelValue = parseInt(document.getElementById("bgLabelId").value); - - for(let sliceIdx = 0; sliceIdx < outputSlices.length; sliceIdx++) { - - for (let pixelIdx = 0; pixelIdx < outputSlices[sliceIdx].length; pixelIdx++) { - - let color = { r: 0, g: 0, b: 0 }; - if(outputSlices[sliceIdx][pixelIdx] != bgLabelValue) { - color = { r: 255, g: 255, b: 255 }; - } - - canvasImageData.data[pixelIdx * 4] = color.r & 0xFF; - canvasImageData.data[pixelIdx * 4 + 1] = color.g & 0xFF; - canvasImageData.data[pixelIdx * 4 + 2] = color.b & 0xFF; - canvasImageData.data[pixelIdx * 4 + 3] = 0xFF; - } - - let maskData = getMaxRegionMaskByContour(canvasImageData); - - // show slice max area only - for( let idx = 0; idx < maskData.length; idx += 1) { - - if(maskData[idx] == bgLabelValue ) { - outputSlices[sliceIdx][idx] = 0; - } - } - - } - - return outputSlices; - } - -/////////////******************* 3D Connected Components**************************///////////////// - - getBinaryMaskData1D = (sliceData) => { // greyImage is one channel 2D image with values 0-255 - - let maskBinaryData1D = []; - for (let idx = 0; idx < sliceData.length; idx++) { - - if(sliceData[idx] > 0) { - maskBinaryData1D[idx] = 1; - } else { - maskBinaryData1D[idx] = 0; - } - } - - return maskBinaryData1D; - } - - getBinaryMaskImage = (greyImage) => { // greyImage is one channel 2D image with values 0-255 - let binaryMaskImage = greyImage.clone(); // from opencvjs - let value = null; - - for (let idx = 0; idx < greyImage.data.length; idx++) { - - if(greyImage.data[idx] > 0) { - value = 255; - } else { - value = 0; - } - - binaryMaskImage.data[idx] = value; - binaryMaskImage.data[idx + 1] = value; - binaryMaskImage.data[idx + 2] = value; - binaryMaskImage.data[idx + 3] = 255; // Alpha channel - } - - return binaryMaskImage; - } - - convertBinaryDataTo2D = (binaryData1D, imgHeight, imgWidth) => { - return tf.tensor(binaryData1D, [imgHeight, imgWidth]).arraySync(); - } - - - getConComponentsFor2D = (binaryMaskData2D, imgHeight, imgWidth) => { - // initiat label - let label1D = []; - resetEquivalenceTable(); - for(let idx = 0; idx < imgHeight * imgWidth; idx++) { - label1D[idx] = 0; - } - - let label2D = convertBinaryDataTo2D(label1D, imgHeight, imgWidth); - - // maxLabel initiation to zero, starting label for 2d and 3d labeling - maxLabel = 0; - - // 1st pass - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( binaryMaskData2D[row][col] != 0) { - label2D[row][col] = checkNeighbors2D(label2D, row, col, maxLabel) - if(maxLabel < label2D[row][col]) { - maxLabel = label2D[row][col]; - } - - } - } - } - - // adjust Equivalence table labels such that eqvTabel[3] = 2 && eqvTabel[2] = 1 => eqvTabel[3] = 1 - for(let labelIdx = equivalenceTabel.length - 1; labelIdx > 0; labelIdx = labelIdx-1 ) { - adjustEquivalenceTable (labelIdx); - } - - // 2nd pass : relabeling the slice after eqvTable adjustment - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( label2D[row][col] != 0) { - label2D[row][col] = equivalenceTabel[label2D[row][col]]; - } - } - } - - return label2D; - } - - - getMaxLabelFor2D = (label2D, imgHeight, imgWidth) => { - - let maxLabelFor2D = 0; - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( label2D[row][col] > maxLabelFor2D) { - maxLabelFor2D = label2D[row][col]; - } - } - } - - return maxLabelFor2D; - } - - - getMaxLabelFor3D = (label3D, sliceHeight, sliceWidth, numSlices) => { - - let maxLabelFor3D = 0; - - for(let sliceIdx = 0; sliceIdx < numSlices; sliceIdx++ ) { - for(let row = 0; row < sliceHeight; row++) { - for(let col = 0; col < sliceWidth; col++) { - - if( label3D[sliceIdx][row][col] > maxLabelFor3D) { - maxLabelFor3D = label3D[sliceIdx][row][col]; - } - } - } - } - - return maxLabelFor3D; - } - - - getMaxVolumeLabel3D = (label3D, sliceHeight, sliceWidth, numSlices) => { - - // Initiat connected component volumes to zeros - let ccVolume = []; - let maxCCLabel3D = getMaxLabelFor3D(label3D, sliceHeight, sliceWidth, numSlices) - - for( let idx = 0; idx < maxCCLabel3D; idx ++) { - ccVolume[idx] = 0; - } - - for(let sliceIdx = 0; sliceIdx < numSlices; sliceIdx++ ) { - for(let row = 0; row < sliceHeight; row++) { - for(let col = 0; col < sliceWidth; col++) { - ccVolume[label3D[sliceIdx][row][col]] = ccVolume[label3D[sliceIdx][row][col]] +1; - } - } - } - - let maxCcVolume = 0; - let maxCcVolumeLabel = -1; - - for( let idx = 1; idx < maxCCLabel3D; idx ++) { - - if( maxCcVolume < ccVolume[idx] ) { - maxCcVolume = ccVolume[idx]; - maxCcVolumeLabel = idx; - } - } - - return maxCcVolumeLabel; - } - - - - getMaxAreaLabel2D = (label2D, imgHeight, imgWidth) => { - - // Initiat connected component areas to zeros - let ccAreas = []; - let maxCCLabel = getMaxLabelFor2D(label2D, imgHeight, imgWidth) - - for( let idx = 0; idx < maxCCLabel; idx ++) { - ccAreas[idx] = 0; - } - - // Find areas of connected components where ccAreas[0] is for background - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - ccAreas[label2D[row][col]] = ccAreas[label2D[row][col]] +1; - } - } - - let maxCcArea = 0; - let maxCcAreaLabel = -1; - for( let idx = 1; idx < maxCCLabel; idx ++) { - if( maxCcArea < ccAreas[idx] ) { - maxCcArea = ccAreas[idx]; - maxCcAreaLabel = idx; - } - } - - - return maxCcAreaLabel; - } - - - resetEquivalenceTable = () => { - equivalenceTabel = []; - equivalenceTabel[0] = 0; - } - - updateEquivalenceTable = (label, newLabel) => { - equivalenceTabel[label] = newLabel; - } - - - adjustEquivalenceTable = (labelIdx) => { - - if(equivalenceTabel[labelIdx] != labelIdx) { - equivalenceTabel[labelIdx] = adjustEquivalenceTable(equivalenceTabel[labelIdx]); - } - - return equivalenceTabel[labelIdx]; - } - - - checkNeighbors2D = (label, row, col, maxLabel) => { - - if ( label[row][col - 1] && label[row - 1][col]) { - - if(label[row][col - 1] == label[row - 1][col]) { - return label[row ][col - 1]; - - } else { - - let smallerLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row ][col - 1] ) { - return label[row ][col - 1] ; - } else if ( label[row - 1][col] ) { - return label[row - 1][col]; - } else { - updateEquivalenceTable(maxLabel+1, maxLabel+1); - return maxLabel+1 ; - } - - } - - checkNeighbors3D = (label, z_1PixelLabel, row, col, maxLabel) => { //z_1PixelLabel same x,y pixel label of z-1 prev slice - if ( label[row][col - 1] && label[row - 1][col] && z_1PixelLabel) { - - if( (label[row][col - 1] == label[row - 1][col]) && (label[row][col - 1] == z_1PixelLabel) ) { - return z_1PixelLabel; - - } else { - - let smallLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let smallestLabel = ( z_1PixelLabel < smallLabel ) ? z_1PixelLabel : smallLabel; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - updateEquivalenceTable(largerLabel, smallestLabel); - updateEquivalenceTable(smallLabel, smallestLabel); - return smallestLabel; - } - - } else if ( label[row][col - 1] && label[row - 1][col] ) { - - if(label[row][col - 1] == label[row - 1][col]) { - return label[row ][col - 1]; - - } else { - - let smallerLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - - } else if ( label[row - 1][col] && z_1PixelLabel ) { - - if(label[row - 1][col] == z_1PixelLabel) { - return z_1PixelLabel; - - } else { - - let smallerLabel = ( z_1PixelLabel < label[row - 1][col] ) ? z_1PixelLabel : label[row - 1][col]; - let largerLabel = ( z_1PixelLabel > label[row - 1][col] ) ? z_1PixelLabel : label[row - 1][col]; - updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row][col - 1] && z_1PixelLabel ) { - - if( label[row][col - 1] == z_1PixelLabel ) { - return z_1PixelLabel; - - } else { - - let smallerLabel = ( label[row][col - 1] < z_1PixelLabel ) ? label[row][col - 1] : z_1PixelLabel; - let largerLabel = ( label[row][col - 1] > z_1PixelLabel ) ? label[row][col - 1] : z_1PixelLabel; - updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row ][col - 1] ) { - return label[row ][col - 1] ; - } else if ( label[row - 1][col] ) { - return label[row - 1][col]; - } else if ( z_1PixelLabel) { - return z_1PixelLabel; - } else { - updateEquivalenceTable(maxLabel+1, maxLabel+1); - return maxLabel+1 ; - } - } - - getConComponentsFor3DVolume = (outputSlices, sliceHeight, sliceWidth) => { - - let binaryMaskData1D = []; - let binaryMaskData2D = []; - let label3D = []; - - for(let sliceIdx = 0; sliceIdx < outputSlices.length; sliceIdx++) { - - binaryMaskData1D[sliceIdx] = getBinaryMaskData1D(outputSlices[sliceIdx]); // binaryMaskData1D has values 0 or 1 - - binaryMaskData2D[sliceIdx] = convertBinaryDataTo2D(binaryMaskData1D[sliceIdx], sliceHeight, sliceWidth); - - if(sliceIdx == 0) { - label3D[sliceIdx] = getConComponentsFor2D(binaryMaskData2D[sliceIdx], sliceHeight, sliceWidth); - - } else { - label3D[sliceIdx] = getConComponentsFor2Slices(binaryMaskData2D[sliceIdx], label3D[sliceIdx - 1], sliceHeight, sliceWidth); - } - - } - - // 3d cc third pass - for(let sliceIdx = 0; sliceIdx < outputSlices.length; sliceIdx++) { - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - - if( label3D[sliceIdx][row][col] != 0) { - label3D[sliceIdx][row][col] = equivalenceTabel[label3D[sliceIdx][row][col]]; - } - } - } - } - - return label3D; - } - - getConComponentsFor2Slices = (binaryMaskData2D, preSliceLabels, imgHeight, imgWidth) => { - let label1D = []; - - for(let idx = 0; idx < imgHeight * imgWidth; idx++) { - label1D[idx] = 0; - } - - let label2D = convertBinaryDataTo2D(label1D, imgHeight, imgWidth); - - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( binaryMaskData2D[row][col] != 0) { - label2D[row][col] = checkNeighbors3D(label2D, preSliceLabels[row][col], row, col, maxLabel) - if(maxLabel < label2D[row][col]) { - maxLabel = label2D[row][col]; - } - - } - } - } - - for(let labelIdx = equivalenceTabel.length - 1; labelIdx > 0; labelIdx = labelIdx-1 ) { - adjustEquivalenceTable (labelIdx); - } - - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( label2D[row][col] != 0) { - label2D[row][col] = equivalenceTabel[label2D[row][col]]; - } - } - } - - return label2D; - } - - - postProcessSlices3D = (outputSlices) => { - // get nifti dimensions - let sliceWidth = niftiHeader.dims[1]; - let sliceHeight = niftiHeader.dims[2]; - - let bgLabelValue = parseInt(document.getElementById("bgLabelId").value); - - let label3D = []; - - label3D = getConComponentsFor3DVolume(outputSlices, sliceHeight, sliceWidth); - - let maxVolumeLabel = getMaxVolumeLabel3D(label3D, sliceHeight, sliceWidth, outputSlices.length); - - - for(let sliceIdx = 0; sliceIdx < outputSlices.length; sliceIdx++) { - //Get max volume mask - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - if(label3D[sliceIdx][row][col] != maxVolumeLabel) { - label3D[sliceIdx][row][col] = 0; - } else { - label3D[sliceIdx][row][col] = 255; - } - } - } - - let pixelIdx; - - for(row = 0, pixelIdx = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++, pixelIdx++) { - - if(label3D[sliceIdx][row][col] == 0) { - outputSlices[sliceIdx][pixelIdx] = 0; - } - - } - } - } - - return outputSlices; - } - - - - postProcessSlices2D = (outputSlices) => { - // get nifti dimensions - let sliceWidth = niftiHeader.dims[1]; - let sliceHeight = niftiHeader.dims[2]; - - let binaryMaskData1D = []; - let binaryMaskData2D = []; - let maxAreaLabel; - let label2D = []; - - for(let sliceIdx = 0; sliceIdx < outputSlices.length; sliceIdx++) { - - binaryMaskData1D = getBinaryMaskData1D(outputSlices[sliceIdx]); // binaryMaskData1D has values 0 or 1 - - binaryMaskData2D = convertBinaryDataTo2D(binaryMaskData1D, sliceHeight, sliceWidth); - - // labels 2d are starting from 0 and increment by 1 with each new label - label2D = getConComponentsFor2D(binaryMaskData2D, sliceHeight, sliceWidth); - - - maxAreaLabel = getMaxAreaLabel2D(label2D, sliceHeight, sliceWidth); - - - // Get max area mask - // It is fine to set label2D to 255 since each slice labels have no effect on other slices labels. - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - if(label2D[row][col] != maxAreaLabel){ - label2D[row][col] = 0; - } else { - label2D[row][col] = 255; - } - } - } - - - // Remove all areas except largest brain area - let pixelIdx; - for(row = 0, pixelIdx = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++, pixelIdx++) { - if(label2D[row][col] == 0){ - outputSlices[sliceIdx][pixelIdx] = 0; - } - } - } - } - - return outputSlices; - } - - -///////////////******************************************************************//////////////////// - - - //1- Standard Normal variate using Box-Muller transform. - randn_bm = () => { - let u = 0, v = 0; - while(u === 0) u = Math.random(); //Converting [0,1) to (0,1) - while(v === 0) v = Math.random(); - return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ); - } - - - // check whether the proposed subvolumes coords are feasible - checkInside = (DHW, cubeSides, subCubeSides) => { - for (let i = 0; i < 3; i++) { - if ( (Math.sign(DHW[i]) < 0) || ( (DHW[i] + subCubeSides[i]) > cubeSides[i]) ) { - return false; - } - } - - return true; - } - - - - findCoordsOfAddBrainBatches = (numOfSubCubes, mean, sigma, cubeSides, subCubeSides ) => { - - const allCoords = []; - let coord; - - for (let i = 0; i < numOfSubCubes; i++) { - coord = Array(Math.round(mean[0]+randn_bm()*sigma[0]), - Math.round(mean[1]+randn_bm()*sigma[1]), - Math.round(mean[2]+randn_bm()*sigma[2]) ); - if( !checkInside(coord, cubeSides, subCubeSides) ) { - i--; - // console.log(coord); - } else { - allCoords[i] = coord; - } - } - - return allCoords; - } - - // Return Tensor with binary 3D volume data 0 or 1 - binarizeVolumeDataTensor = (volumeDataTensor) => { - - let alpha = 0; - return volumeDataTensor.step(alpha); // element-wise: (x > 0 ? 1 : alpha * x ); e.g. Tenosr [0, 0.9, 0.8, -3] => Tensor [0, 1, 1, 0] - } - - - // Convert tensor to buffer so immutable tensor can be mutable buffer with get() and set() - tensor2Buffer = (tensor) => { - return tf.buffer(tensor.shape, tensor.dtype, tensor.dataSync()); - } - - - cubeMoments = (cube3d, threshold) => { - // mean and variance of a normalized cube data [0, 1] - let cube = tensor2Buffer(cube3d); - let coords = []; - for(let i = 0; i < cube3d.shape[0]; i++) { - for(let j = 0; j < cube3d.shape[1]; j++) { - for(let k = 0; k < cube3d.shape[2]; k++) { - if (cube.get(i,j,k) > threshold) { - coords.push([i, j, k]); - } - } - } - } - let coordsTensor = tf.tensor2d(coords); - let moments = tf.moments(coordsTensor, 0, false); - let meanArray = Array.from(tf.round(moments['mean']).dataSync()); - let varArray = Array.from(moments['variance'].dataSync()); - coordsTensor.dispose(); - - return [meanArray, varArray]; - }; - - - // For all MRI volume values > 0 , find the centroid of those data - findHeadCentroid = (slices_3d, num_of_slices, slice_height, slice_width) => { - // Threshold tensor volume values to 0 or 1 such that if (voxelVal > 0 ? 1 : 0 ) - let binarizeVolumeTensor = binarizeVolumeDataTensor(slices_3d); - let binarizeVolumeBuffer = tensor2Buffer(binarizeVolumeTensor); - - const grid_coords = []; - let counter = 0; - - - // Find coordinates of nonzero voxels as (x_i, y_i, z_i) vectors - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - - let voxelValue = binarizeVolumeBuffer.get(depthIdx, rowIdx, colIdx); - if(voxelValue == 1) { - grid_coords[counter] = Array(depthIdx, rowIdx, colIdx); - counter += 1; - } - } - } - } - - // Create 2D Tesnor with three columns for depth, row, col index - let gridCoordsTensor = tf.tensor2d(grid_coords); - let axis = 0; - - let headCentroidTensor = tf.round(gridCoordsTensor.mean(axis)); - - // Find the Centroid voxel Array [d, h, w] - let headCentroidArray = Array.from(headCentroidTensor.dataSync()); - tf.dispose(gridCoordsTensor); - tf.dispose(headCentroidTensor); - - return headCentroidArray; - - } - - // Try to create batches with the volume of slices each of D,H,W sub_volume and focus on brain area for the additional sub_volumes - sliceVolumeIntoOverlappedBatches = (slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W, headSubCubesCoords ) => { - - let allSlicedBatches = []; - let batch_id = 1; - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += batch_D) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += batch_H) { - for(let colIdx = 0; colIdx < slice_width; colIdx += batch_W) { - // for overlap calculations of last batches - let depthIdxDiff = 0; - let rowIdxDiff = 0; - let colIdxDiff = 0; - - if((depthIdx + batch_D) > num_of_slices) { - depthIdxDiff = (depthIdx + batch_D) - num_of_slices; - } - - if((rowIdx + batch_H) > slice_height) { - rowIdxDiff = (rowIdx + batch_H) - slice_height; - } - - if((colIdx + batch_W) > slice_width) { - colIdxDiff = (colIdx + batch_W) - slice_width; - } - - let startIndex = [depthIdx - depthIdxDiff, rowIdx - rowIdxDiff, colIdx - colIdxDiff]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - } - } - - // Additional sub_volumes or batches focus around the head centroid - for(let cubeIdx = 0; cubeIdx < headSubCubesCoords.length; cubeIdx++) { - - let startIndex = [headSubCubesCoords[cubeIdx][0], headSubCubesCoords[cubeIdx][1], headSubCubesCoords[cubeIdx][2]]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - - - return allSlicedBatches; - } - - // Try to create batches with the volume of slices each of D,H,W sub_volume with minimum overlap option - sliceVolumeIntoBatches = (slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W ) => { - let allSlicedBatches = []; - let batch_id = 1; - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += batch_D) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += batch_H) { - for(let colIdx = 0; colIdx < slice_width; colIdx += batch_W) { - // for overlap calculations of last batches - let depthIdxDiff = 0; - let rowIdxDiff = 0; - let colIdxDiff = 0; - - if((depthIdx + batch_D) > num_of_slices) { - depthIdxDiff = (depthIdx + batch_D) - num_of_slices; - } - - if((rowIdx + batch_H) > slice_height) { - rowIdxDiff = (rowIdx + batch_H) - slice_height; - } - - if((colIdx + batch_W) > slice_width) { - colIdxDiff = (colIdx + batch_W) - slice_width; - } - - let startIndex = [depthIdx - depthIdxDiff, rowIdx - rowIdxDiff, colIdx - colIdxDiff]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - } - } - - return allSlicedBatches; - } - - getAllSlicesData1D = (num_of_slices) => { - let allSlices = []; - for(let sliceIdx = 0; sliceIdx < num_of_slices; sliceIdx++) { - let slice = getSliceData1D(sliceIdx, niftiHeader, niftiImage); - allSlices.push(slice); - } - - return allSlices; - } - - getAllSlices2D = (allSlices, slice_height, slice_width) => { - let allSlices_2D = []; - for(let sliceIdx = 0; sliceIdx < allSlices.length; sliceIdx ++){ - allSlices_2D.push(tf.tensor(allSlices[sliceIdx], [slice_height, slice_width])); - } - - return allSlices_2D; - } - - getSlices3D = (allSlices_2D) => { - - return tf.stack(allSlices_2D); - - } - - normalizeVolumeData = (volumeData) => { - //Normalize the data to the range 0 - 1 using min-max scaling - const volumeData_Max = volumeData.max(); - const volumeData_Min = volumeData.min(); - const normalizedSlices_3d = volumeData.sub(volumeData_Min).div(volumeData_Max.sub(volumeData_Min)); - return normalizedSlices_3d; - } - - load_model = async() => { - let modelUrl = './mnm_tfjs_me_test/model.json'; - // let modelUrl = './meshnet_dropout/mnm_dropout/model2.json'; - const Model = await tf.loadLayersModel(modelUrl); - return Model; - } - - - findPixelIndex = (allPixels, d, h, w) => { - - for( pIndex = 0; pIndex < allPixels.length; pIndex++) { - if( (allPixels[pIndex]["d"] == d) && - (allPixels[pIndex]["h"] == h) && - (allPixels[pIndex]["w"] == w) ) { - - return pIndex; - } - - } - - return null; - } - - - -// Find current voxel value of the related seg class buffer, if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 -generateOutputSlicesV2 = (allPredictions, num_of_slices, numSegClasses, slice_height, slice_width, batch_D, batch_H, batch_W) => { - - console.log("version 2 num of seg classes: ", numSegClasses); - // buffer set ( depth, H, W) in order - let outVolumeBuffer = tf.buffer([num_of_slices, slice_height, slice_width, numSegClasses ], dtype=tf.float32) - let isPostProcessEnable = document.getElementById("postProcessing").checked; - - - for(batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"]; - let pixelValues = allPredictions[batchIdx]["data"]; - let pixelValuesCounter = 0; - - for(depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - // Find current voxel value of the related seg class buffer - // if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 - let voxelValue = outVolumeBuffer.get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer.set(voxelValue + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - - pixelValuesCounter += 1; - } - } - } - } - - // convert output buffer to tensor - let axis = -1; // last axis - // Set for each voxel the value of the index of the buffer that has the max voxel value, e.g. third buffer with index = 2 (cont..) - // has max voxel value = 10 then the related voxel in outVolumeTensor will have value of 2 - let outVolumeTensor = tf.argMax(outVolumeBuffer.toTensor(), axis); - - - let unstackOutVolumeTensor = tf.unstack(outVolumeTensor); - outVolumeTensor.dispose(); - - console.log("Converting unstack tensors to arrays: ") - - - for(sliceTensorIdx = 0; sliceTensorIdx < unstackOutVolumeTensor.length; sliceTensorIdx++ ) { - allOutputSlices[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - allOutputSlices2DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - allOutputSlices3DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - } - - - - if(isPostProcessEnable) { - // console.log("wait postprocessing slices"); - document.getElementById("postProcessHint").innerHTML = "Post processing status => 2D Connected Comp: " + " In progress".fontcolor("red").bold(); - allOutputSlices2DCC = postProcessSlices(allOutputSlices2DCC); // remove noisy regions using 2d CC - document.getElementById("postProcessHint").innerHTML = "postprocessing status => 2D Connected Comp: " + " Ok".fontcolor("green").bold() + " => 3D Connected Comp: " + " In progress".fontcolor("red").bold() - allOutputSlices3DCC = postProcessSlices3D(allOutputSlices3DCC); // remove noisy regions using 3d CC - document.getElementById("postProcessHint").innerHTML = "Post processing status => 2D Connected Comp: " + " Ok".fontcolor("green").bold() + " => 3D Connected Comp : " + " Ok".fontcolor("green").bold() - } - - // draw output canvas - let outCanvas = document.getElementById('outputCanvas'); - let output2dCC = document.getElementById('out2dCC'); - let output3dCC = document.getElementById('out3dCC'); - let slider = document.getElementById('sliceNav'); - drawOutputCanvas(outCanvas, slider.value, niftiHeader, niftiImage, allOutputSlices); - drawOutputCanvas(output2dCC, slider.value, niftiHeader, niftiImage, allOutputSlices2DCC); - drawOutputCanvas(output3dCC, slider.value, niftiHeader, niftiImage, allOutputSlices3DCC); - } - - - generateOutputSlices = (allPredictions, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W) => { - console.log("version 1"); - // buffer set ( depth, H, W) in order - let outVolumeBuffer = tf.buffer([num_of_slices, slice_height, slice_width], dtype=tf.float32) - let isPostProcessEnable = document.getElementById("postProcessing").checked; - - - for(batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"] - let pixelValues = allPredictions[batchIdx]["data"] - let pixelValuesCounter = 0; - - for(depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - outVolumeBuffer.set(pixelValues[pixelValuesCounter], depthIdx, rowIdx, colIdx ); - pixelValuesCounter += 1; - } - } - } - } - - // convert output buffer to tensor - let outVolumeTensor = outVolumeBuffer.toTensor(); - - let unstackOutVolumeTensor = tf.unstack(outVolumeTensor) - - console.log("Converting unstack tensors to arrays: ") - - - for(sliceTensorIdx = 0; sliceTensorIdx < unstackOutVolumeTensor.length; sliceTensorIdx++ ) { - allOutputSlices[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - allOutputSlices2DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - allOutputSlices3DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()) - } - - - if(isPostProcessEnable) { - // console.log("wait postprocessing slices"); - document.getElementById("postProcessHint").innerHTML = "Post processing status => 2D Connected Comp: " + " In progress".fontcolor("red").bold(); - allOutputSlices2DCC = postProcessSlices(allOutputSlices2DCC); // remove noisy regions using 2d CC - document.getElementById("postProcessHint").innerHTML = "postprocessing status => 2D Connected Comp: " + " Ok".fontcolor("green").bold() + " => 3D Connected Comp: " + " In progress".fontcolor("red").bold() - allOutputSlices3DCC = postProcessSlices3D(allOutputSlices3DCC); // remove noisy regions using 3d CC - document.getElementById("postProcessHint").innerHTML = "Post processing status => 2D Connected Comp: " + " Ok".fontcolor("green").bold() + " => 3D Connected Comp : " + " Ok".fontcolor("green").bold() - } - - // draw output canvas - let outCanvas = document.getElementById('outputCanvas'); - let output2dCC = document.getElementById('out2dCC'); - let output3dCC = document.getElementById('out3dCC'); - let slider = document.getElementById('sliceNav'); - drawOutputCanvas(outCanvas, slider.value, niftiHeader, niftiImage, allOutputSlices); - drawOutputCanvas(output2dCC, slider.value, niftiHeader, niftiImage, allOutputSlices2DCC); - drawOutputCanvas(output3dCC, slider.value, niftiHeader, niftiImage, allOutputSlices3DCC); - } - - - inputVolumeChange = (val) => { - document.getElementById("inputVolumeId").innerHTML = "Input Volume Dim :" + " [" + document.getElementById("batchSizeId").value + ", 38, 38, 38, " + - document.getElementById("numOfChanId").value + "]" - } - - // For future use - download = (content, fileName, contentType) => { - var a = document.createElement("a"); - var file = new Blob([content], {type: contentType}); - a.href = URL.createObjectURL(file); - a.download = fileName; - a.click(); - } - - checkWebGl1 = () => { - - const gl = document.createElement('canvas').getContext('webgl'); - if (!gl) { - if (typeof WebGLRenderingContext !== 'undefined') { - console.log('WebGL1 may be disabled. Please try updating video card drivers'); - document.getElementById("results").innerHTML += '
WebGL1 status: ' + "Disabled".fontcolor("red").bold() + '
Try updating video card driver'; - } else { - console.log('WebGL1 is not supported'); - document.getElementById("results").innerHTML += '
WebGL1 status: ' + "Red".fontcolor("red").bold() + '
Not supported'; - } - } else { - console.log('WebGl1 is enabled'); - document.getElementById("results").innerHTML += '
WebGL1 status: ' + "Green".fontcolor("green").bold(); - } - - } - - checkWebGl2 = () => { - - const gl = document.createElement('canvas').getContext('webgl2'); - if (!gl) { - if (typeof WebGL2RenderingContext !== 'undefined') { - console.log('WebGL2 may be disabled. Please try updating video card drivers'); - document.getElementById("results").innerHTML = 'WebGL2 status: ' + "Disabled".fontcolor("red").bold() + '
Try updating video card driver'; - } else { - console.log('WebGL2 is not supported'); - document.getElementById("results").innerHTML = 'WebGL2 status: ' + "Red".fontcolor("red").bold() + '
Not supported'; - } - - checkWebGl1(); - } else { - console.log('WebGl2 is enabled'); - document.getElementById("results").innerHTML = 'WebGL2 status: ' + "Green".fontcolor("green").bold(); - } - - - } - - runInference = () => { - - let processingFlag = true; - - let batchSize = parseInt(document.getElementById("batchSizeId").value); - let numOfChan = parseInt(document.getElementById("numOfChanId").value); - - if (document.getElementById("file").value == "") { - document.getElementById("results").innerHTML = "No NIfTI file is selected".fontcolor("red"); - processingFlag = false; - } - - if (isNaN(batchSize) || batchSize < 1 || batchSize > 1) { - document.getElementById("results").innerHTML = "The batch Size must be 1 for this demo".fontcolor("red"); - processingFlag = false; - } - - if (isNaN(numOfChan) || (numOfChan !=1)) { - document.getElementById("results").innerHTML = "The number of channels must be a number of 1 for this demo".fontcolor("red"); - processingFlag = false; - } - - if(processingFlag) { - - tf.engine().startScope() - - console.log("Batch size: ", batchSize); - console.log("Num of Channels: ", numOfChan); - - // Propose subvolume size as needed by inference model input e.g. 38x38x38 - let batch_D = 38; - let batch_H = 38; - let batch_W = 38; - - let slice_width = niftiHeader.dims[1]; - let slice_height = niftiHeader.dims[2]; - let num_of_slices = niftiHeader.dims[3]; - - let isBatchOverlapEnable = document.getElementById("batchOverlapId").checked; - - - - // let input_shape = [batchSize, 38, 38, 38, numOfChan]; - let input_shape = [batchSize, batch_D, batch_H, batch_W, numOfChan]; - - - let allSlices = getAllSlicesData1D(num_of_slices); - - let allSlices_2D = getAllSlices2D(allSlices, slice_height, slice_width); - // get slices_3d tensor - let slices_3d = getSlices3D(allSlices_2D); - tf.dispose(allSlices_2D); - // nomalize MRI data to be from 0 to 1 - slices_3d = normalizeVolumeData(slices_3d); - - let allBatches = []; - let headSubCubesCoords = []; - - - if(isBatchOverlapEnable) { - // number of additional batches focus on the brain/head volume - let num_of_Overlap_batches = parseInt(document.getElementById("numOverlapBatchesId").value); - console.log(" num of overlapped batches: ", num_of_Overlap_batches); - - // Find the centroid of 3D head volume - // const headCentroid = findHeadCentroid(slices_3d, num_of_slices, slice_height, slice_width); - - // Find the centroid of 3D head volume and the variance - let cent_var = cubeMoments(slices_3d, 0.5); - // Mean or centroid - const headCentroid = cent_var[0]; - console.log(" Head 3D Centroid : ", headCentroid); - // Variance - const sigma = cent_var[1]; - console.log(" Head 3D Variance : ", sigma); - - - headSubCubesCoords = findCoordsOfAddBrainBatches(num_of_Overlap_batches, - new Array(headCentroid[0], headCentroid[1], headCentroid[2]), - new Array(sigma[0], sigma[1], sigma[2]), - new Array(num_of_slices, slice_height, slice_width), - new Array(batch_D, batch_H, batch_W)); - - allBatches = sliceVolumeIntoOverlappedBatches(slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W, headSubCubesCoords); - - } else { - // This option will cover all slices, some slices that are not enough to create a batch will need overlap with prevous batch slices - // e.g. slice volume = 3*5*5 DHW , and batch is 2*2*2 , 2*3*3 =18 batches will be considered - let num_of_batches = Math.ceil(slice_width/batch_W) * Math.ceil(slice_height/batch_H) * Math.ceil(num_of_slices/batch_D); - console.log("Num of Batches for inference: ", num_of_batches); - - allBatches = sliceVolumeIntoBatches(slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W); - } - - tf.dispose(slices_3d); - console.log(" sample of a batch for inference : ", Array.from(allBatches[0].data.dataSync())) - - console.log(tf.getBackend()); - checkWebGl2(); - - - fetch('./mnm_tfjs_me_test/model.json') - .then(response => { - return response.json(); - }) - .then(data => console.log("fetch results: ", data)); - - - let allPredictions = []; - console.log("predictOnBatch enabled"); - - model.then(function (res) { - - try { - let startTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - - let j = 0; - let timer = window.setInterval(function() { - - let curTensor = tf.tensor(allBatches[j].data.dataSync(), input_shape); - // let prediction = res.predict( curTensor ); - let prediction = res.predictOnBatch( curTensor ); - tf.dispose(curTensor); - let axis = -1; //4; - let prediction_argmax = tf.argMax(prediction, axis); - tf.dispose(prediction); - allPredictions.push({"id": allBatches[j].id, "coordinates": allBatches[j].coordinates, "data": Array.from(prediction_argmax.dataSync()) }) - let curBatchMaxLabel = Math.max(...Array.from(prediction_argmax.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - tf.dispose(prediction_argmax); - - - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons.fontcolor("red").bold() : ""; - document.getElementById("completed").innerHTML = "Batches completed: " + (j+1) + " / " + allBatches.length + - // https://js.tensorflow.org/api/latest/#memory - "

" +"TF Memory Status: " + memStatus.fontcolor(tf.memory().unreliable ? "red" : "green").bold() + - // numBytes: Number of bytes allocated (undisposed) at this time - "
" + "numBytes : " + Math.round(tf.memory().numBytes/(1024*1024)) + " MB" + - //numBytesInGPU : Number of bytes allocated (undisposed) in the GPU only at this time - "
" + "numBytesInGPU : " + Math.round(tf.memory().numBytesInGPU/(1024*1024)) + " MB" + - "
" + "numBytesInGPUAllocated : " + Math.round(tf.memory().numBytesInGPUAllocated/(1024*1024)) + " MB" + - "
" + "numBytesInGPUFree : " + Math.round(tf.memory().numBytesInGPUFree/(1024*1024)) + " MB" + - // numDataBuffers : Number of unique data buffers allocated (undisposed) at this time, which is ≤ the number of tensors - "
" + "numDataBuffers : " + tf.memory().numDataBuffers + - "
" + "numTensors : " + tf.memory().numTensors + - "
" + unreliableReasons ; - - - if( j == allBatches.length-1 ){ - window.clearInterval( timer ); - - let numSegClasses = maxLabelPredicted + 1; - // Generate output volume or slices - generateOutputSlicesV2(allPredictions, num_of_slices, numSegClasses, slice_height, slice_width, batch_D, batch_H, batch_W); - - tf.engine().endScope(); - - let stopTime = performance.now(); - document.getElementById("results").innerHTML ="Processing the whole brain volume in tfjs tooks for multi-class output mask : " + - ((stopTime -startTime)/1000).toFixed(4).fontcolor("green").bold() + - " Seconds.
Press " + " F12 ".fontcolor("red").bold() + - "to see the inference results in Console" - //download(JSON.stringify(allPredictions), 'prediction.json', 'text/plain'); - } - - j++; - - }, 0); - - } - catch(err) { - document.getElementById("results").innerHTML = err.message.fontcolor("red").bold() + - " Try to decrease batch size or increase H/W resources".fontcolor("red").bold() + - "
" +"If webgl context is lost, try to restore webgl context by visit the link ".fontcolor("red") + - 'here' - } - }); - } - }// end of runInference - - - // Load tfjs json model - const model = load_model(); - - -})(); diff --git a/v3/experimental_mode/CanvasVersion/js/mainNiftiReadingFunctions.js b/v3/experimental_mode/CanvasVersion/js/mainNiftiReadingFunctions.js deleted file mode 100644 index 52a059b..0000000 --- a/v3/experimental_mode/CanvasVersion/js/mainNiftiReadingFunctions.js +++ /dev/null @@ -1,312 +0,0 @@ - // Mohamed Masoud 2021 (Sergey Plis Lab) - // For [1, 38, 38, 38, 1] input shape, MeshNet model - - // Main parameters: - niftiHeader = []; - niftiImage = []; - labelNiftiHeader = []; - labelNiftiImage = []; - gtLabelLoaded = false; - - // This part inspired from https://github.com/rii-mango/NIFTI-Reader-JS - readNIFTI = (data) => { - - let inputCanvas = document.getElementById('inputCanvas'); - let gtCanvas = document.getElementById('gtCanvas'); - let outCanvas = document.getElementById('outputCanvas'); - let outCC2D = document.getElementById('out2dCC'); - let outCC3D = document.getElementById('out3dCC'); - let slider = document.getElementById('sliceNav'); - - // parse nifti - if (nifti.isCompressed(data)) { - data = nifti.decompress(data); - } - - if (nifti.isNIFTI(data)) { - niftiHeader = nifti.readHeader(data); - niftiImage = nifti.readImage(niftiHeader, data); - } - - - // set up slider - let slices = niftiHeader.dims[3]; - - slider.max = slices - 1; - // slider.min = 0; - slider.value = Math.round(slices / 2); - slider.oninput = function() { - document.getElementById('sliceNumId').innerHTML = slider.value; - drawInputCanvas(inputCanvas, slider.value, niftiHeader, niftiImage); - if(gtLabelLoaded) { - drawGtCanvas(gtCanvas, slider.value, labelNiftiHeader, labelNiftiImage); - } - - if(allOutputSlices.length) { - drawOutputCanvas(outCanvas, slider.value, niftiHeader, niftiImage, allOutputSlices); - drawOutputCanvas(outCC2D, slider.value, niftiHeader, niftiImage, allOutputSlices2DCC); - drawOutputCanvas(outCC3D, slider.value, niftiHeader, niftiImage, allOutputSlices3DCC); - } - }; - - // draw slice - drawInputCanvas(inputCanvas, slider.value, niftiHeader, niftiImage); - } - - - readNIFTILabels = (data) => { - - - let gtCanvas = document.getElementById('gtCanvas'); - let slider = document.getElementById('sliceNav'); - - // parse nifti - if (nifti.isCompressed(data)) { - data = nifti.decompress(data); - } - - gtLabelLoaded = true; - - if (nifti.isNIFTI(data)) { - labelNiftiHeader = nifti.readHeader(data); - labelNiftiImage = nifti.readImage(labelNiftiHeader, data); - } - - - // draw slice - drawGtCanvas(gtCanvas, slider.value, labelNiftiHeader, labelNiftiImage); - } - - drawInputCanvas = (canvas, sliceIdx, niftiHeader, niftiImage) => { - // get nifti dimensions - let cols = niftiHeader.dims[1]; - let rows = niftiHeader.dims[2]; - - // set canvas dimensions to nifti slice dimensions - canvas.width = cols; - canvas.height = rows; - - - // make canvas image data - let ctx = canvas.getContext("2d"); - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - // convert raw data to typed array based on nifti datatype - let typedData; - - if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedData = new Uint8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedData = new Int16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedData = new Int32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedData = new Float32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedData = new Float64Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedData = new Int8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedData = new Uint16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedData = new Uint32Array(niftiImage); - } else { - return; - } - - // offset to specified slice - let sliceSize = cols * rows; - let sliceOffset = sliceSize * sliceIdx; - - // draw pixels - for (let row = 0; row < rows; row++) { - let rowOffset = row * cols; - - for (let col = 0; col < cols; col++) { - let offset = sliceOffset + rowOffset + col; - let value = typedData[offset]; - - /* - Assumes data is 8-bit, otherwise you would need to first convert - to 0-255 range based on datatype range, data range (iterate through - data to find), or display range (cal_min/max). - - Other things to take into consideration: - - data scale: scl_slope and scl_inter, apply to raw value before - applying display range - - orientation: displays in raw orientation, see nifti orientation - info for how to orient data - - assumes voxel shape (pixDims) is isometric, if not, you'll need - to apply transform to the canvas - - byte order: see littleEndian flag - */ - canvasImageData.data[(rowOffset + col) * 4] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 1] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 2] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 3] = 0xFF; - - } - } - - ctx.putImageData(canvasImageData, 0, 0); - - } - -function labelMax(arr){ - let max = arr[0]; - for(let i=1; i max){ - max = arr[i]; - } - } - return max; -} - -drawGtCanvas = (canvas, sliceIdx, labelNiftiHeader, labelNiftiImage) => { - // get nifti dimensions - let cols = labelNiftiHeader.dims[1]; - let rows = labelNiftiHeader.dims[2]; - - // set canvas dimensions to nifti slice dimensions - canvas.width = cols; - canvas.height = rows; - - - // make canvas image data - let ctx = canvas.getContext("2d"); - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - // convert raw data to typed array based on nifti datatype - let typedData; - - if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedData = new Uint8Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedData = new Int16Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedData = new Int32Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedData = new Float32Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedData = new Float64Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedData = new Int8Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedData = new Uint16Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedData = new Uint32Array(labelNiftiImage); - } else { - return; - } - - document.getElementById("numOfClassesId").value = labelMax(typedData) + 1; - let n_classes = labelMax(typedData) + 1; - document.getElementById("results").innerHTML = "Found " + n_classes.toString().fontcolor("green").bold() + " classes in the ground truth"; - - // offset to specified slice - let sliceSize = cols * rows; - let sliceOffset = sliceSize * sliceIdx; - - // draw pixels - for (let row = 0; row < rows; row++) { - let rowOffset = row * cols; - - for (let col = 0; col < cols; col++) { - let offset = sliceOffset + rowOffset + col; - let value = typedData[offset]; - - /* - Assumes data is 8-bit, otherwise you would need to first convert - to 0-255 range based on datatype range, data range (iterate through - data to find), or display range (cal_min/max). - - Other things to take into consideration: - - data scale: scl_slope and scl_inter, apply to raw value before - applying display range - - orientation: displays in raw orientation, see nifti orientation - info for how to orient data - - assumes voxel shape (pixDims) is isometric, if not, you'll need - to apply transform to the canvas - - byte order: see littleEndian flag - */ - value = Math.ceil(value*255/(n_classes - 1)) - - canvasImageData.data[(rowOffset + col) * 4] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 1] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 2] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 3] = 0xFF; - - } - } - - ctx.putImageData(canvasImageData, 0, 0); - - } - - - - makeSlice = (file, start, length) => { - var fileType = (typeof File); - - if (fileType === 'undefined') { - return function () {}; - } - - if (File.prototype.slice) { - return file.slice(start, start + length); - } - - if (File.prototype.mozSlice) { - return file.mozSlice(start, length); - } - - if (File.prototype.webkitSlice) { - return file.webkitSlice(start, length); - } - - return null; - } - - readFile = (file, sourceId) => { - console.log("file is :", file) - - var blob = makeSlice(file, 0, file.size); - - var reader = new FileReader(); - - reader.onloadend = function (evt) { - if (evt.target.readyState === FileReader.DONE) { - - document.getElementById("results").innerHTML = ""; - - //evt.target.result is : ArrayBuffer { byteLength: 763810 } - if(sourceId == "file") { - readNIFTI(evt.target.result); - document.getElementById("mriTitle").innerHTML = "MRI"; - document.getElementById("groundTruthFile").disabled = false; - document.getElementById("file").disabled = true; - document.getElementById("runInferenceId").disabled = false; - } - - if(sourceId == "groundTruthFile") { - readNIFTILabels(evt.target.result); - document.getElementById("gtTitle").innerHTML = "Ground Truth"; - document.getElementById("groundTruthFile").disabled = true; - } - - allOutputSlices = []; - allOutputSlices2DCC = []; - allOutputSlices3DCC = []; - } - }; - - reader.readAsArrayBuffer(blob); - } - - handleFileSelect = (evt) => { - let files = evt.target.files; - let source = evt.target || evt.srcElement; - readFile(files[0],source.id); - } - - diff --git a/v3/experimental_mode/CanvasVersion/js/nifti-reader-min.js b/v3/experimental_mode/CanvasVersion/js/nifti-reader-min.js deleted file mode 100644 index 80c64a3..0000000 --- a/v3/experimental_mode/CanvasVersion/js/nifti-reader-min.js +++ /dev/null @@ -1,4 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.nifti=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0){opt.windowBits=-opt.windowBits}else if(opt.gzip&&opt.windowBits>0&&opt.windowBits<16){opt.windowBits+=16}this.err=0;this.msg="";this.ended=false;this.chunks=[];this.strm=new zstream;this.strm.avail_out=0;var status=zlib_deflate.deflateInit2(this.strm,opt.level,opt.method,opt.windowBits,opt.memLevel,opt.strategy);if(status!==Z_OK){throw new Error(msg[status])}if(opt.header){zlib_deflate.deflateSetHeader(this.strm,opt.header)}};Deflate.prototype.push=function(data,mode){var strm=this.strm;var chunkSize=this.options.chunkSize;var status,_mode;if(this.ended){return false}_mode=mode===~~mode?mode:mode===true?Z_FINISH:Z_NO_FLUSH;if(typeof data==="string"){strm.input=strings.string2buf(data)}else if(toString.call(data)==="[object ArrayBuffer]"){strm.input=new Uint8Array(data)}else{strm.input=data}strm.next_in=0;strm.avail_in=strm.input.length;do{if(strm.avail_out===0){strm.output=new utils.Buf8(chunkSize);strm.next_out=0;strm.avail_out=chunkSize}status=zlib_deflate.deflate(strm,_mode);if(status!==Z_STREAM_END&&status!==Z_OK){this.onEnd(status);this.ended=true;return false}if(strm.avail_out===0||strm.avail_in===0&&(_mode===Z_FINISH||_mode===Z_SYNC_FLUSH)){if(this.options.to==="string"){this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output,strm.next_out)))}else{this.onData(utils.shrinkBuf(strm.output,strm.next_out))}}}while((strm.avail_in>0||strm.avail_out===0)&&status!==Z_STREAM_END);if(_mode===Z_FINISH){status=zlib_deflate.deflateEnd(this.strm);this.onEnd(status);this.ended=true;return status===Z_OK}if(_mode===Z_SYNC_FLUSH){this.onEnd(Z_OK);strm.avail_out=0;return true}return true};Deflate.prototype.onData=function(chunk){this.chunks.push(chunk)};Deflate.prototype.onEnd=function(status){if(status===Z_OK){if(this.options.to==="string"){this.result=this.chunks.join("")}else{this.result=utils.flattenChunks(this.chunks)}}this.chunks=[];this.err=status;this.msg=this.strm.msg};function deflate(input,options){var deflator=new Deflate(options);deflator.push(input,true);if(deflator.err){throw deflator.msg}return deflator.result}function deflateRaw(input,options){options=options||{};options.raw=true;return deflate(input,options)}function gzip(input,options){options=options||{};options.gzip=true;return deflate(input,options)}exports.Deflate=Deflate;exports.deflate=deflate;exports.deflateRaw=deflateRaw;exports.gzip=gzip},{"./utils/common":4,"./utils/strings":5,"./zlib/deflate.js":9,"./zlib/messages":14,"./zlib/zstream":16}],3:[function(require,module,exports){"use strict";var zlib_inflate=require("./zlib/inflate.js");var utils=require("./utils/common");var strings=require("./utils/strings");var c=require("./zlib/constants");var msg=require("./zlib/messages");var zstream=require("./zlib/zstream");var gzheader=require("./zlib/gzheader");var toString=Object.prototype.toString;var Inflate=function(options){this.options=utils.assign({chunkSize:16384,windowBits:0,to:""},options||{});var opt=this.options;if(opt.raw&&opt.windowBits>=0&&opt.windowBits<16){opt.windowBits=-opt.windowBits;if(opt.windowBits===0){opt.windowBits=-15}}if(opt.windowBits>=0&&opt.windowBits<16&&!(options&&options.windowBits)){opt.windowBits+=32}if(opt.windowBits>15&&opt.windowBits<48){if((opt.windowBits&15)===0){opt.windowBits|=15}}this.err=0;this.msg="";this.ended=false;this.chunks=[];this.strm=new zstream;this.strm.avail_out=0;var status=zlib_inflate.inflateInit2(this.strm,opt.windowBits);if(status!==c.Z_OK){throw new Error(msg[status])}this.header=new gzheader;zlib_inflate.inflateGetHeader(this.strm,this.header)};Inflate.prototype.push=function(data,mode){var strm=this.strm;var chunkSize=this.options.chunkSize;var status,_mode;var next_out_utf8,tail,utf8str;var allowBufError=false;if(this.ended){return false}_mode=mode===~~mode?mode:mode===true?c.Z_FINISH:c.Z_NO_FLUSH;if(typeof data==="string"){strm.input=strings.binstring2buf(data)}else if(toString.call(data)==="[object ArrayBuffer]"){strm.input=new Uint8Array(data)}else{strm.input=data}strm.next_in=0;strm.avail_in=strm.input.length;do{if(strm.avail_out===0){strm.output=new utils.Buf8(chunkSize);strm.next_out=0;strm.avail_out=chunkSize}status=zlib_inflate.inflate(strm,c.Z_NO_FLUSH);if(status===c.Z_BUF_ERROR&&allowBufError===true){status=c.Z_OK;allowBufError=false}if(status!==c.Z_STREAM_END&&status!==c.Z_OK){this.onEnd(status);this.ended=true;return false}if(strm.next_out){if(strm.avail_out===0||status===c.Z_STREAM_END||strm.avail_in===0&&(_mode===c.Z_FINISH||_mode===c.Z_SYNC_FLUSH)){if(this.options.to==="string"){next_out_utf8=strings.utf8border(strm.output,strm.next_out);tail=strm.next_out-next_out_utf8;utf8str=strings.buf2string(strm.output,next_out_utf8);strm.next_out=tail;strm.avail_out=chunkSize-tail;if(tail){utils.arraySet(strm.output,strm.output,next_out_utf8,tail,0)}this.onData(utf8str)}else{this.onData(utils.shrinkBuf(strm.output,strm.next_out))}}}if(strm.avail_in===0&&strm.avail_out===0){allowBufError=true}}while((strm.avail_in>0||strm.avail_out===0)&&status!==c.Z_STREAM_END);if(status===c.Z_STREAM_END){_mode=c.Z_FINISH}if(_mode===c.Z_FINISH){status=zlib_inflate.inflateEnd(this.strm);this.onEnd(status);this.ended=true;return status===c.Z_OK}if(_mode===c.Z_SYNC_FLUSH){this.onEnd(c.Z_OK);strm.avail_out=0;return true}return true};Inflate.prototype.onData=function(chunk){this.chunks.push(chunk)};Inflate.prototype.onEnd=function(status){if(status===c.Z_OK){if(this.options.to==="string"){this.result=this.chunks.join("")}else{this.result=utils.flattenChunks(this.chunks)}}this.chunks=[];this.err=status;this.msg=this.strm.msg};function inflate(input,options){var inflator=new Inflate(options);inflator.push(input,true);if(inflator.err){throw inflator.msg}return inflator.result}function inflateRaw(input,options){options=options||{};options.raw=true;return inflate(input,options)}exports.Inflate=Inflate;exports.inflate=inflate;exports.inflateRaw=inflateRaw;exports.ungzip=inflate},{"./utils/common":4,"./utils/strings":5,"./zlib/constants":7,"./zlib/gzheader":10,"./zlib/inflate.js":12,"./zlib/messages":14,"./zlib/zstream":16}],4:[function(require,module,exports){"use strict";var TYPED_OK=typeof Uint8Array!=="undefined"&&typeof Uint16Array!=="undefined"&&typeof Int32Array!=="undefined";exports.assign=function(obj){var sources=Array.prototype.slice.call(arguments,1);while(sources.length){var source=sources.shift();if(!source){continue}if(typeof source!=="object"){throw new TypeError(source+"must be non-object")}for(var p in source){if(source.hasOwnProperty(p)){obj[p]=source[p]}}}return obj};exports.shrinkBuf=function(buf,size){if(buf.length===size){return buf}if(buf.subarray){return buf.subarray(0,size)}buf.length=size;return buf};var fnTyped={arraySet:function(dest,src,src_offs,len,dest_offs){if(src.subarray&&dest.subarray){dest.set(src.subarray(src_offs,src_offs+len),dest_offs);return}for(var i=0;i=252?6:q>=248?5:q>=240?4:q>=224?3:q>=192?2:1}_utf8len[254]=_utf8len[254]=1;exports.string2buf=function(str){var buf,c,c2,m_pos,i,str_len=str.length,buf_len=0;for(m_pos=0;m_pos>>6;buf[i++]=128|c&63}else if(c<65536){buf[i++]=224|c>>>12;buf[i++]=128|c>>>6&63;buf[i++]=128|c&63}else{buf[i++]=240|c>>>18;buf[i++]=128|c>>>12&63;buf[i++]=128|c>>>6&63;buf[i++]=128|c&63}}return buf};function buf2binstring(buf,len){if(len<65537){if(buf.subarray&&STR_APPLY_UIA_OK||!buf.subarray&&STR_APPLY_OK){return String.fromCharCode.apply(null,utils.shrinkBuf(buf,len))}}var result="";for(var i=0;i4){utf16buf[out++]=65533;i+=c_len-1;continue}c&=c_len===2?31:c_len===3?15:7;while(c_len>1&&i1){utf16buf[out++]=65533;continue}if(c<65536){utf16buf[out++]=c}else{c-=65536;utf16buf[out++]=55296|c>>10&1023;utf16buf[out++]=56320|c&1023}}return buf2binstring(utf16buf,out)};exports.utf8border=function(buf,max){var pos;max=max||buf.length;if(max>buf.length){max=buf.length}pos=max-1;while(pos>=0&&(buf[pos]&192)===128){pos--}if(pos<0){return max}if(pos===0){return max}return pos+_utf8len[buf[pos]]>max?pos:max}},{"./common":4}],6:[function(require,module,exports){"use strict";function adler32(adler,buf,len,pos){var s1=adler&65535|0,s2=adler>>>16&65535|0,n=0;while(len!==0){n=len>2e3?2e3:len;len-=n;do{s1=s1+buf[pos++]|0;s2=s2+s1|0}while(--n);s1%=65521;s2%=65521}return s1|s2<<16|0}module.exports=adler32},{}],7:[function(require,module,exports){module.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],8:[function(require,module,exports){"use strict";function makeTable(){var c,table=[];for(var n=0;n<256;n++){c=n;for(var k=0;k<8;k++){c=c&1?3988292384^c>>>1:c>>>1}table[n]=c}return table}var crcTable=makeTable();function crc32(crc,buf,len,pos){var t=crcTable,end=pos+len;crc=crc^-1;for(var i=pos;i>>8^t[(crc^buf[i])&255]}return crc^-1}module.exports=crc32},{}],9:[function(require,module,exports){"use strict";var utils=require("../utils/common");var trees=require("./trees");var adler32=require("./adler32");var crc32=require("./crc32");var msg=require("./messages");var Z_NO_FLUSH=0;var Z_PARTIAL_FLUSH=1;var Z_FULL_FLUSH=3;var Z_FINISH=4;var Z_BLOCK=5;var Z_OK=0;var Z_STREAM_END=1;var Z_STREAM_ERROR=-2;var Z_DATA_ERROR=-3;var Z_BUF_ERROR=-5;var Z_DEFAULT_COMPRESSION=-1;var Z_FILTERED=1;var Z_HUFFMAN_ONLY=2;var Z_RLE=3;var Z_FIXED=4;var Z_DEFAULT_STRATEGY=0;var Z_UNKNOWN=2;var Z_DEFLATED=8;var MAX_MEM_LEVEL=9;var MAX_WBITS=15;var DEF_MEM_LEVEL=8;var LENGTH_CODES=29;var LITERALS=256;var L_CODES=LITERALS+1+LENGTH_CODES;var D_CODES=30;var BL_CODES=19;var HEAP_SIZE=2*L_CODES+1;var MAX_BITS=15;var MIN_MATCH=3;var MAX_MATCH=258;var MIN_LOOKAHEAD=MAX_MATCH+MIN_MATCH+1;var PRESET_DICT=32;var INIT_STATE=42;var EXTRA_STATE=69;var NAME_STATE=73;var COMMENT_STATE=91;var HCRC_STATE=103;var BUSY_STATE=113;var FINISH_STATE=666;var BS_NEED_MORE=1;var BS_BLOCK_DONE=2;var BS_FINISH_STARTED=3;var BS_FINISH_DONE=4;var OS_CODE=3;function err(strm,errorCode){strm.msg=msg[errorCode];return errorCode}function rank(f){return(f<<1)-(f>4?9:0)}function zero(buf){var len=buf.length;while(--len>=0){buf[len]=0}}function flush_pending(strm){var s=strm.state;var len=s.pending;if(len>strm.avail_out){len=strm.avail_out}if(len===0){return}utils.arraySet(strm.output,s.pending_buf,s.pending_out,len,strm.next_out);strm.next_out+=len;s.pending_out+=len;strm.total_out+=len;strm.avail_out-=len;s.pending-=len;if(s.pending===0){s.pending_out=0}}function flush_block_only(s,last){trees._tr_flush_block(s,s.block_start>=0?s.block_start:-1,s.strstart-s.block_start,last);s.block_start=s.strstart;flush_pending(s.strm)}function put_byte(s,b){s.pending_buf[s.pending++]=b}function putShortMSB(s,b){s.pending_buf[s.pending++]=b>>>8&255;s.pending_buf[s.pending++]=b&255}function read_buf(strm,buf,start,size){var len=strm.avail_in;if(len>size){len=size}if(len===0){return 0}strm.avail_in-=len;utils.arraySet(buf,strm.input,strm.next_in,len,start);if(strm.state.wrap===1){strm.adler=adler32(strm.adler,buf,len,start)}else if(strm.state.wrap===2){strm.adler=crc32(strm.adler,buf,len,start)}strm.next_in+=len;strm.total_in+=len;return len}function longest_match(s,cur_match){var chain_length=s.max_chain_length;var scan=s.strstart;var match;var len;var best_len=s.prev_length;var nice_match=s.nice_match;var limit=s.strstart>s.w_size-MIN_LOOKAHEAD?s.strstart-(s.w_size-MIN_LOOKAHEAD):0;var _win=s.window;var wmask=s.w_mask;var prev=s.prev;var strend=s.strstart+MAX_MATCH;var scan_end1=_win[scan+best_len-1];var scan_end=_win[scan+best_len];if(s.prev_length>=s.good_match){chain_length>>=2}if(nice_match>s.lookahead){nice_match=s.lookahead}do{match=cur_match;if(_win[match+best_len]!==scan_end||_win[match+best_len-1]!==scan_end1||_win[match]!==_win[scan]||_win[++match]!==_win[scan+1]){continue}scan+=2;match++;do{}while(_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&_win[++scan]===_win[++match]&&scanbest_len){s.match_start=cur_match;best_len=len;if(len>=nice_match){break}scan_end1=_win[scan+best_len-1];scan_end=_win[scan+best_len]}}while((cur_match=prev[cur_match&wmask])>limit&&--chain_length!==0);if(best_len<=s.lookahead){return best_len}return s.lookahead}function fill_window(s){var _w_size=s.w_size;var p,n,m,more,str;do{more=s.window_size-s.lookahead-s.strstart;if(s.strstart>=_w_size+(_w_size-MIN_LOOKAHEAD)){utils.arraySet(s.window,s.window,_w_size,_w_size,0);s.match_start-=_w_size;s.strstart-=_w_size;s.block_start-=_w_size;n=s.hash_size;p=n;do{m=s.head[--p];s.head[p]=m>=_w_size?m-_w_size:0}while(--n);n=_w_size;p=n;do{m=s.prev[--p];s.prev[p]=m>=_w_size?m-_w_size:0}while(--n);more+=_w_size}if(s.strm.avail_in===0){break}n=read_buf(s.strm,s.window,s.strstart+s.lookahead,more);s.lookahead+=n;if(s.lookahead+s.insert>=MIN_MATCH){str=s.strstart-s.insert;s.ins_h=s.window[str];s.ins_h=(s.ins_h<s.pending_buf_size-5){max_block_size=s.pending_buf_size-5}for(;;){if(s.lookahead<=1){fill_window(s);if(s.lookahead===0&&flush===Z_NO_FLUSH){return BS_NEED_MORE}if(s.lookahead===0){break}}s.strstart+=s.lookahead;s.lookahead=0;var max_start=s.block_start+max_block_size;if(s.strstart===0||s.strstart>=max_start){s.lookahead=s.strstart-max_start;s.strstart=max_start;flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}if(s.strstart-s.block_start>=s.w_size-MIN_LOOKAHEAD){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}}s.insert=0;if(flush===Z_FINISH){flush_block_only(s,true);if(s.strm.avail_out===0){return BS_FINISH_STARTED}return BS_FINISH_DONE}if(s.strstart>s.block_start){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}return BS_NEED_MORE}function deflate_fast(s,flush){var hash_head;var bflush;for(;;){if(s.lookahead=MIN_MATCH){s.ins_h=(s.ins_h<=MIN_MATCH){bflush=trees._tr_tally(s,s.strstart-s.match_start,s.match_length-MIN_MATCH);s.lookahead-=s.match_length;if(s.match_length<=s.max_lazy_match&&s.lookahead>=MIN_MATCH){s.match_length--;do{s.strstart++;s.ins_h=(s.ins_h<=MIN_MATCH){s.ins_h=(s.ins_h<4096)){s.match_length=MIN_MATCH-1}}if(s.prev_length>=MIN_MATCH&&s.match_length<=s.prev_length){max_insert=s.strstart+s.lookahead-MIN_MATCH;bflush=trees._tr_tally(s,s.strstart-1-s.prev_match,s.prev_length-MIN_MATCH);s.lookahead-=s.prev_length-1;s.prev_length-=2;do{if(++s.strstart<=max_insert){s.ins_h=(s.ins_h<=MIN_MATCH&&s.strstart>0){scan=s.strstart-1;prev=_win[scan];if(prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]){strend=s.strstart+MAX_MATCH;do{}while(prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&prev===_win[++scan]&&scans.lookahead){s.match_length=s.lookahead}}}if(s.match_length>=MIN_MATCH){bflush=trees._tr_tally(s,1,s.match_length-MIN_MATCH);s.lookahead-=s.match_length;s.strstart+=s.match_length;s.match_length=0}else{bflush=trees._tr_tally(s,0,s.window[s.strstart]);s.lookahead--;s.strstart++}if(bflush){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}}s.insert=0;if(flush===Z_FINISH){flush_block_only(s,true);if(s.strm.avail_out===0){return BS_FINISH_STARTED}return BS_FINISH_DONE}if(s.last_lit){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}return BS_BLOCK_DONE}function deflate_huff(s,flush){var bflush;for(;;){if(s.lookahead===0){fill_window(s);if(s.lookahead===0){if(flush===Z_NO_FLUSH){return BS_NEED_MORE}break}}s.match_length=0;bflush=trees._tr_tally(s,0,s.window[s.strstart]);s.lookahead--;s.strstart++;if(bflush){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}}s.insert=0;if(flush===Z_FINISH){flush_block_only(s,true);if(s.strm.avail_out===0){return BS_FINISH_STARTED}return BS_FINISH_DONE}if(s.last_lit){flush_block_only(s,false);if(s.strm.avail_out===0){return BS_NEED_MORE}}return BS_BLOCK_DONE}var Config=function(good_length,max_lazy,nice_length,max_chain,func){this.good_length=good_length;this.max_lazy=max_lazy;this.nice_length=nice_length;this.max_chain=max_chain;this.func=func};var configuration_table;configuration_table=[new Config(0,0,0,0,deflate_stored),new Config(4,4,8,4,deflate_fast),new Config(4,5,16,8,deflate_fast),new Config(4,6,32,32,deflate_fast),new Config(4,4,16,16,deflate_slow),new Config(8,16,32,32,deflate_slow),new Config(8,16,128,128,deflate_slow),new Config(8,32,128,256,deflate_slow),new Config(32,128,258,1024,deflate_slow),new Config(32,258,258,4096,deflate_slow)];function lm_init(s){s.window_size=2*s.w_size;zero(s.head);s.max_lazy_match=configuration_table[s.level].max_lazy;s.good_match=configuration_table[s.level].good_length;s.nice_match=configuration_table[s.level].nice_length;s.max_chain_length=configuration_table[s.level].max_chain;s.strstart=0;s.block_start=0;s.lookahead=0;s.insert=0;s.match_length=s.prev_length=MIN_MATCH-1;s.match_available=0;s.ins_h=0}function DeflateState(){this.strm=null;this.status=0;this.pending_buf=null;this.pending_buf_size=0;this.pending_out=0;this.pending=0;this.wrap=0;this.gzhead=null;this.gzindex=0;this.method=Z_DEFLATED;this.last_flush=-1;this.w_size=0;this.w_bits=0;this.w_mask=0;this.window=null;this.window_size=0;this.prev=null;this.head=null;this.ins_h=0;this.hash_size=0;this.hash_bits=0;this.hash_mask=0;this.hash_shift=0;this.block_start=0;this.match_length=0;this.prev_match=0;this.match_available=0;this.strstart=0;this.match_start=0;this.lookahead=0;this.prev_length=0;this.max_chain_length=0;this.max_lazy_match=0;this.level=0;this.strategy=0;this.good_match=0;this.nice_match=0;this.dyn_ltree=new utils.Buf16(HEAP_SIZE*2);this.dyn_dtree=new utils.Buf16((2*D_CODES+1)*2);this.bl_tree=new utils.Buf16((2*BL_CODES+1)*2);zero(this.dyn_ltree);zero(this.dyn_dtree);zero(this.bl_tree);this.l_desc=null;this.d_desc=null;this.bl_desc=null;this.bl_count=new utils.Buf16(MAX_BITS+1);this.heap=new utils.Buf16(2*L_CODES+1);zero(this.heap);this.heap_len=0;this.heap_max=0;this.depth=new utils.Buf16(2*L_CODES+1);zero(this.depth);this.l_buf=0;this.lit_bufsize=0;this.last_lit=0;this.d_buf=0;this.opt_len=0;this.static_len=0;this.matches=0;this.insert=0;this.bi_buf=0;this.bi_valid=0}function deflateResetKeep(strm){var s;if(!strm||!strm.state){return err(strm,Z_STREAM_ERROR)}strm.total_in=strm.total_out=0;strm.data_type=Z_UNKNOWN;s=strm.state;s.pending=0;s.pending_out=0;if(s.wrap<0){s.wrap=-s.wrap}s.status=s.wrap?INIT_STATE:BUSY_STATE;strm.adler=s.wrap===2?0:1;s.last_flush=Z_NO_FLUSH;trees._tr_init(s);return Z_OK}function deflateReset(strm){var ret=deflateResetKeep(strm);if(ret===Z_OK){lm_init(strm.state)}return ret}function deflateSetHeader(strm,head){if(!strm||!strm.state){return Z_STREAM_ERROR}if(strm.state.wrap!==2){return Z_STREAM_ERROR}strm.state.gzhead=head;return Z_OK}function deflateInit2(strm,level,method,windowBits,memLevel,strategy){if(!strm){return Z_STREAM_ERROR}var wrap=1;if(level===Z_DEFAULT_COMPRESSION){level=6}if(windowBits<0){wrap=0;windowBits=-windowBits}else if(windowBits>15){wrap=2;windowBits-=16}if(memLevel<1||memLevel>MAX_MEM_LEVEL||method!==Z_DEFLATED||windowBits<8||windowBits>15||level<0||level>9||strategy<0||strategy>Z_FIXED){return err(strm,Z_STREAM_ERROR)}if(windowBits===8){windowBits=9}var s=new DeflateState;strm.state=s;s.strm=strm;s.wrap=wrap;s.gzhead=null;s.w_bits=windowBits;s.w_size=1<>1;s.l_buf=(1+2)*s.lit_bufsize;s.level=level;s.strategy=strategy;s.method=method;return deflateReset(strm)}function deflateInit(strm,level){return deflateInit2(strm,level,Z_DEFLATED,MAX_WBITS,DEF_MEM_LEVEL,Z_DEFAULT_STRATEGY)}function deflate(strm,flush){var old_flush,s;var beg,val;if(!strm||!strm.state||flush>Z_BLOCK||flush<0){return strm?err(strm,Z_STREAM_ERROR):Z_STREAM_ERROR}s=strm.state;if(!strm.output||!strm.input&&strm.avail_in!==0||s.status===FINISH_STATE&&flush!==Z_FINISH){return err(strm,strm.avail_out===0?Z_BUF_ERROR:Z_STREAM_ERROR)}s.strm=strm;old_flush=s.last_flush;s.last_flush=flush;if(s.status===INIT_STATE){if(s.wrap===2){strm.adler=0;put_byte(s,31);put_byte(s,139);put_byte(s,8);if(!s.gzhead){put_byte(s,0);put_byte(s,0);put_byte(s,0);put_byte(s,0);put_byte(s,0);put_byte(s,s.level===9?2:s.strategy>=Z_HUFFMAN_ONLY||s.level<2?4:0);put_byte(s,OS_CODE);s.status=BUSY_STATE}else{put_byte(s,(s.gzhead.text?1:0)+(s.gzhead.hcrc?2:0)+(!s.gzhead.extra?0:4)+(!s.gzhead.name?0:8)+(!s.gzhead.comment?0:16));put_byte(s,s.gzhead.time&255);put_byte(s,s.gzhead.time>>8&255);put_byte(s,s.gzhead.time>>16&255);put_byte(s,s.gzhead.time>>24&255);put_byte(s,s.level===9?2:s.strategy>=Z_HUFFMAN_ONLY||s.level<2?4:0);put_byte(s,s.gzhead.os&255);if(s.gzhead.extra&&s.gzhead.extra.length){put_byte(s,s.gzhead.extra.length&255);put_byte(s,s.gzhead.extra.length>>8&255)}if(s.gzhead.hcrc){strm.adler=crc32(strm.adler,s.pending_buf,s.pending,0)}s.gzindex=0;s.status=EXTRA_STATE}}else{var header=Z_DEFLATED+(s.w_bits-8<<4)<<8;var level_flags=-1;if(s.strategy>=Z_HUFFMAN_ONLY||s.level<2){level_flags=0}else if(s.level<6){level_flags=1}else if(s.level===6){level_flags=2}else{level_flags=3}header|=level_flags<<6;if(s.strstart!==0){header|=PRESET_DICT}header+=31-header%31;s.status=BUSY_STATE;putShortMSB(s,header);if(s.strstart!==0){putShortMSB(s,strm.adler>>>16);putShortMSB(s,strm.adler&65535)}strm.adler=1}}if(s.status===EXTRA_STATE){if(s.gzhead.extra){beg=s.pending;while(s.gzindex<(s.gzhead.extra.length&65535)){if(s.pending===s.pending_buf_size){if(s.gzhead.hcrc&&s.pending>beg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}flush_pending(strm);beg=s.pending;if(s.pending===s.pending_buf_size){break}}put_byte(s,s.gzhead.extra[s.gzindex]&255);s.gzindex++}if(s.gzhead.hcrc&&s.pending>beg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}if(s.gzindex===s.gzhead.extra.length){s.gzindex=0;s.status=NAME_STATE}}else{s.status=NAME_STATE}}if(s.status===NAME_STATE){if(s.gzhead.name){beg=s.pending;do{if(s.pending===s.pending_buf_size){if(s.gzhead.hcrc&&s.pending>beg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}flush_pending(strm);beg=s.pending;if(s.pending===s.pending_buf_size){val=1;break}}if(s.gzindexbeg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}if(val===0){s.gzindex=0;s.status=COMMENT_STATE}}else{s.status=COMMENT_STATE}}if(s.status===COMMENT_STATE){if(s.gzhead.comment){beg=s.pending;do{if(s.pending===s.pending_buf_size){if(s.gzhead.hcrc&&s.pending>beg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}flush_pending(strm);beg=s.pending;if(s.pending===s.pending_buf_size){val=1;break}}if(s.gzindexbeg){strm.adler=crc32(strm.adler,s.pending_buf,s.pending-beg,beg)}if(val===0){s.status=HCRC_STATE}}else{s.status=HCRC_STATE}}if(s.status===HCRC_STATE){if(s.gzhead.hcrc){if(s.pending+2>s.pending_buf_size){flush_pending(strm)}if(s.pending+2<=s.pending_buf_size){put_byte(s,strm.adler&255);put_byte(s,strm.adler>>8&255);strm.adler=0;s.status=BUSY_STATE}}else{s.status=BUSY_STATE}}if(s.pending!==0){flush_pending(strm);if(strm.avail_out===0){s.last_flush=-1;return Z_OK}}else if(strm.avail_in===0&&rank(flush)<=rank(old_flush)&&flush!==Z_FINISH){return err(strm,Z_BUF_ERROR)}if(s.status===FINISH_STATE&&strm.avail_in!==0){return err(strm,Z_BUF_ERROR)}if(strm.avail_in!==0||s.lookahead!==0||flush!==Z_NO_FLUSH&&s.status!==FINISH_STATE){var bstate=s.strategy===Z_HUFFMAN_ONLY?deflate_huff(s,flush):s.strategy===Z_RLE?deflate_rle(s,flush):configuration_table[s.level].func(s,flush);if(bstate===BS_FINISH_STARTED||bstate===BS_FINISH_DONE){s.status=FINISH_STATE}if(bstate===BS_NEED_MORE||bstate===BS_FINISH_STARTED){if(strm.avail_out===0){ -s.last_flush=-1}return Z_OK}if(bstate===BS_BLOCK_DONE){if(flush===Z_PARTIAL_FLUSH){trees._tr_align(s)}else if(flush!==Z_BLOCK){trees._tr_stored_block(s,0,0,false);if(flush===Z_FULL_FLUSH){zero(s.head);if(s.lookahead===0){s.strstart=0;s.block_start=0;s.insert=0}}}flush_pending(strm);if(strm.avail_out===0){s.last_flush=-1;return Z_OK}}}if(flush!==Z_FINISH){return Z_OK}if(s.wrap<=0){return Z_STREAM_END}if(s.wrap===2){put_byte(s,strm.adler&255);put_byte(s,strm.adler>>8&255);put_byte(s,strm.adler>>16&255);put_byte(s,strm.adler>>24&255);put_byte(s,strm.total_in&255);put_byte(s,strm.total_in>>8&255);put_byte(s,strm.total_in>>16&255);put_byte(s,strm.total_in>>24&255)}else{putShortMSB(s,strm.adler>>>16);putShortMSB(s,strm.adler&65535)}flush_pending(strm);if(s.wrap>0){s.wrap=-s.wrap}return s.pending!==0?Z_OK:Z_STREAM_END}function deflateEnd(strm){var status;if(!strm||!strm.state){return Z_STREAM_ERROR}status=strm.state.status;if(status!==INIT_STATE&&status!==EXTRA_STATE&&status!==NAME_STATE&&status!==COMMENT_STATE&&status!==HCRC_STATE&&status!==BUSY_STATE&&status!==FINISH_STATE){return err(strm,Z_STREAM_ERROR)}strm.state=null;return status===BUSY_STATE?err(strm,Z_DATA_ERROR):Z_OK}exports.deflateInit=deflateInit;exports.deflateInit2=deflateInit2;exports.deflateReset=deflateReset;exports.deflateResetKeep=deflateResetKeep;exports.deflateSetHeader=deflateSetHeader;exports.deflate=deflate;exports.deflateEnd=deflateEnd;exports.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":4,"./adler32":6,"./crc32":8,"./messages":14,"./trees":15}],10:[function(require,module,exports){"use strict";function GZheader(){this.text=0;this.time=0;this.xflags=0;this.os=0;this.extra=null;this.extra_len=0;this.name="";this.comment="";this.hcrc=0;this.done=false}module.exports=GZheader},{}],11:[function(require,module,exports){"use strict";var BAD=30;var TYPE=12;module.exports=function inflate_fast(strm,start){var state;var _in;var last;var _out;var beg;var end;var dmax;var wsize;var whave;var wnext;var s_window;var hold;var bits;var lcode;var dcode;var lmask;var dmask;var here;var op;var len;var dist;var from;var from_source;var input,output;state=strm.state;_in=strm.next_in;input=strm.input;last=_in+(strm.avail_in-5);_out=strm.next_out;output=strm.output;beg=_out-(start-strm.avail_out);end=_out+(strm.avail_out-257);dmax=state.dmax;wsize=state.wsize;whave=state.whave;wnext=state.wnext;s_window=state.window;hold=state.hold;bits=state.bits;lcode=state.lencode;dcode=state.distcode;lmask=(1<>>24;hold>>>=op;bits-=op;op=here>>>16&255;if(op===0){output[_out++]=here&65535}else if(op&16){len=here&65535;op&=15;if(op){if(bits>>=op;bits-=op}if(bits<15){hold+=input[_in++]<>>24;hold>>>=op;bits-=op;op=here>>>16&255;if(op&16){dist=here&65535;op&=15;if(bitsdmax){strm.msg="invalid distance too far back";state.mode=BAD;break top}hold>>>=op;bits-=op;op=_out-beg;if(dist>op){op=dist-op;if(op>whave){if(state.sane){strm.msg="invalid distance too far back";state.mode=BAD;break top}}from=0;from_source=s_window;if(wnext===0){from+=wsize-op;if(op2){output[_out++]=from_source[from++];output[_out++]=from_source[from++];output[_out++]=from_source[from++];len-=3}if(len){output[_out++]=from_source[from++];if(len>1){output[_out++]=from_source[from++]}}}else{from=_out-dist;do{output[_out++]=output[from++];output[_out++]=output[from++];output[_out++]=output[from++];len-=3}while(len>2);if(len){output[_out++]=output[from++];if(len>1){output[_out++]=output[from++]}}}}else if((op&64)===0){here=dcode[(here&65535)+(hold&(1<>3;_in-=len;bits-=len<<3;hold&=(1<>>24&255)+(q>>>8&65280)+((q&65280)<<8)+((q&255)<<24)}function InflateState(){this.mode=0;this.last=false;this.wrap=0;this.havedict=false;this.flags=0;this.dmax=0;this.check=0;this.total=0;this.head=null;this.wbits=0;this.wsize=0;this.whave=0;this.wnext=0;this.window=null;this.hold=0;this.bits=0;this.length=0;this.offset=0;this.extra=0;this.lencode=null;this.distcode=null;this.lenbits=0;this.distbits=0;this.ncode=0;this.nlen=0;this.ndist=0;this.have=0;this.next=null;this.lens=new utils.Buf16(320);this.work=new utils.Buf16(288);this.lendyn=null;this.distdyn=null;this.sane=0;this.back=0;this.was=0}function inflateResetKeep(strm){var state;if(!strm||!strm.state){return Z_STREAM_ERROR}state=strm.state;strm.total_in=strm.total_out=state.total=0;strm.msg="";if(state.wrap){strm.adler=state.wrap&1}state.mode=HEAD;state.last=0;state.havedict=0;state.dmax=32768;state.head=null;state.hold=0;state.bits=0;state.lencode=state.lendyn=new utils.Buf32(ENOUGH_LENS);state.distcode=state.distdyn=new utils.Buf32(ENOUGH_DISTS);state.sane=1;state.back=-1;return Z_OK}function inflateReset(strm){var state;if(!strm||!strm.state){return Z_STREAM_ERROR}state=strm.state;state.wsize=0;state.whave=0;state.wnext=0;return inflateResetKeep(strm)}function inflateReset2(strm,windowBits){var wrap;var state;if(!strm||!strm.state){return Z_STREAM_ERROR}state=strm.state;if(windowBits<0){wrap=0;windowBits=-windowBits}else{wrap=(windowBits>>4)+1;if(windowBits<48){windowBits&=15}}if(windowBits&&(windowBits<8||windowBits>15)){return Z_STREAM_ERROR}if(state.window!==null&&state.wbits!==windowBits){state.window=null}state.wrap=wrap;state.wbits=windowBits;return inflateReset(strm)}function inflateInit2(strm,windowBits){var ret;var state;if(!strm){return Z_STREAM_ERROR}state=new InflateState;strm.state=state;state.window=null;ret=inflateReset2(strm,windowBits);if(ret!==Z_OK){strm.state=null}return ret}function inflateInit(strm){return inflateInit2(strm,DEF_WBITS)}var virgin=true;var lenfix,distfix;function fixedtables(state){if(virgin){var sym;lenfix=new utils.Buf32(512);distfix=new utils.Buf32(32);sym=0;while(sym<144){state.lens[sym++]=8}while(sym<256){state.lens[sym++]=9}while(sym<280){state.lens[sym++]=7}while(sym<288){state.lens[sym++]=8}inflate_table(LENS,state.lens,0,288,lenfix,0,state.work,{bits:9});sym=0;while(sym<32){state.lens[sym++]=5}inflate_table(DISTS,state.lens,0,32,distfix,0,state.work,{bits:5});virgin=false}state.lencode=lenfix;state.lenbits=9;state.distcode=distfix;state.distbits=5}function updatewindow(strm,src,end,copy){var dist;var state=strm.state;if(state.window===null){state.wsize=1<=state.wsize){utils.arraySet(state.window,src,end-state.wsize,state.wsize,0);state.wnext=0;state.whave=state.wsize}else{dist=state.wsize-state.wnext;if(dist>copy){dist=copy}utils.arraySet(state.window,src,end-copy,dist,state.wnext);copy-=dist;if(copy){utils.arraySet(state.window,src,end-copy,copy,0);state.wnext=copy;state.whave=state.wsize}else{state.wnext+=dist;if(state.wnext===state.wsize){state.wnext=0}if(state.whave>>8&255;state.check=crc32(state.check,hbuf,2,0);hold=0;bits=0;state.mode=FLAGS;break}state.flags=0;if(state.head){state.head.done=false}if(!(state.wrap&1)||(((hold&255)<<8)+(hold>>8))%31){strm.msg="incorrect header check";state.mode=BAD;break}if((hold&15)!==Z_DEFLATED){strm.msg="unknown compression method";state.mode=BAD;break}hold>>>=4;bits-=4;len=(hold&15)+8;if(state.wbits===0){state.wbits=len}else if(len>state.wbits){strm.msg="invalid window size";state.mode=BAD;break}state.dmax=1<>8&1}if(state.flags&512){hbuf[0]=hold&255;hbuf[1]=hold>>>8&255;state.check=crc32(state.check,hbuf,2,0)}hold=0;bits=0;state.mode=TIME;case TIME:while(bits<32){if(have===0){break inf_leave}have--;hold+=input[next++]<>>8&255;hbuf[2]=hold>>>16&255;hbuf[3]=hold>>>24&255;state.check=crc32(state.check,hbuf,4,0)}hold=0;bits=0;state.mode=OS;case OS:while(bits<16){if(have===0){break inf_leave}have--;hold+=input[next++]<>8}if(state.flags&512){hbuf[0]=hold&255;hbuf[1]=hold>>>8&255;state.check=crc32(state.check,hbuf,2,0)}hold=0;bits=0;state.mode=EXLEN;case EXLEN:if(state.flags&1024){while(bits<16){if(have===0){break inf_leave}have--;hold+=input[next++]<>>8&255;state.check=crc32(state.check,hbuf,2,0)}hold=0;bits=0}else if(state.head){state.head.extra=null}state.mode=EXTRA;case EXTRA:if(state.flags&1024){copy=state.length;if(copy>have){copy=have}if(copy){if(state.head){len=state.head.extra_len-state.length;if(!state.head.extra){state.head.extra=new Array(state.head.extra_len)}utils.arraySet(state.head.extra,input,next,copy,len)}if(state.flags&512){state.check=crc32(state.check,input,copy,next)}have-=copy;next+=copy;state.length-=copy}if(state.length){break inf_leave}}state.length=0;state.mode=NAME;case NAME:if(state.flags&2048){if(have===0){break inf_leave}copy=0;do{len=input[next+copy++];if(state.head&&len&&state.length<65536){state.head.name+=String.fromCharCode(len)}}while(len&©>9&1;state.head.done=true}strm.adler=state.check=0;state.mode=TYPE;break;case DICTID:while(bits<32){if(have===0){break inf_leave}have--;hold+=input[next++]<>>=bits&7;bits-=bits&7;state.mode=CHECK;break}while(bits<3){if(have===0){break inf_leave}have--;hold+=input[next++]<>>=1;bits-=1;switch(hold&3){case 0:state.mode=STORED;break;case 1:fixedtables(state);state.mode=LEN_;if(flush===Z_TREES){hold>>>=2;bits-=2;break inf_leave}break;case 2:state.mode=TABLE;break;case 3:strm.msg="invalid block type";state.mode=BAD}hold>>>=2;bits-=2;break;case STORED:hold>>>=bits&7;bits-=bits&7;while(bits<32){if(have===0){break inf_leave}have--;hold+=input[next++]<>>16^65535)){strm.msg="invalid stored block lengths";state.mode=BAD;break}state.length=hold&65535;hold=0;bits=0;state.mode=COPY_;if(flush===Z_TREES){break inf_leave}case COPY_:state.mode=COPY;case COPY:copy=state.length;if(copy){if(copy>have){copy=have}if(copy>left){copy=left}if(copy===0){break inf_leave}utils.arraySet(output,input,next,copy,put);have-=copy;next+=copy;left-=copy;put+=copy;state.length-=copy;break}state.mode=TYPE;break;case TABLE:while(bits<14){if(have===0){break inf_leave}have--;hold+=input[next++]<>>=5;bits-=5;state.ndist=(hold&31)+1;hold>>>=5;bits-=5;state.ncode=(hold&15)+4;hold>>>=4;bits-=4;if(state.nlen>286||state.ndist>30){strm.msg="too many length or distance symbols";state.mode=BAD;break}state.have=0;state.mode=LENLENS;case LENLENS:while(state.have>>=3;bits-=3}while(state.have<19){state.lens[order[state.have++]]=0}state.lencode=state.lendyn;state.lenbits=7;opts={bits:state.lenbits};ret=inflate_table(CODES,state.lens,0,19,state.lencode,0,state.work,opts);state.lenbits=opts.bits;if(ret){strm.msg="invalid code lengths set";state.mode=BAD;break}state.have=0;state.mode=CODELENS;case CODELENS:while(state.have>>24;here_op=here>>>16&255;here_val=here&65535;if(here_bits<=bits){break}if(have===0){break inf_leave}have--;hold+=input[next++]<>>=here_bits;bits-=here_bits;state.lens[state.have++]=here_val}else{if(here_val===16){n=here_bits+2;while(bits>>=here_bits;bits-=here_bits;if(state.have===0){strm.msg="invalid bit length repeat";state.mode=BAD;break}len=state.lens[state.have-1];copy=3+(hold&3);hold>>>=2;bits-=2}else if(here_val===17){n=here_bits+3;while(bits>>=here_bits;bits-=here_bits;len=0;copy=3+(hold&7);hold>>>=3;bits-=3}else{n=here_bits+7;while(bits>>=here_bits;bits-=here_bits;len=0;copy=11+(hold&127);hold>>>=7;bits-=7}if(state.have+copy>state.nlen+state.ndist){strm.msg="invalid bit length repeat";state.mode=BAD;break}while(copy--){state.lens[state.have++]=len}}}if(state.mode===BAD){break}if(state.lens[256]===0){strm.msg="invalid code -- missing end-of-block";state.mode=BAD;break}state.lenbits=9;opts={bits:state.lenbits};ret=inflate_table(LENS,state.lens,0,state.nlen,state.lencode,0,state.work,opts);state.lenbits=opts.bits;if(ret){strm.msg="invalid literal/lengths set";state.mode=BAD;break}state.distbits=6;state.distcode=state.distdyn;opts={bits:state.distbits};ret=inflate_table(DISTS,state.lens,state.nlen,state.ndist,state.distcode,0,state.work,opts);state.distbits=opts.bits;if(ret){strm.msg="invalid distances set";state.mode=BAD;break}state.mode=LEN_;if(flush===Z_TREES){break inf_leave}case LEN_:state.mode=LEN;case LEN:if(have>=6&&left>=258){strm.next_out=put;strm.avail_out=left;strm.next_in=next;strm.avail_in=have;state.hold=hold;state.bits=bits;inflate_fast(strm,_out);put=strm.next_out;output=strm.output;left=strm.avail_out;next=strm.next_in;input=strm.input;have=strm.avail_in;hold=state.hold;bits=state.bits;if(state.mode===TYPE){state.back=-1}break}state.back=0;for(;;){here=state.lencode[hold&(1<>>24;here_op=here>>>16&255;here_val=here&65535;if(here_bits<=bits){break}if(have===0){break inf_leave}have--;hold+=input[next++]<>last_bits)];here_bits=here>>>24;here_op=here>>>16&255;here_val=here&65535;if(last_bits+here_bits<=bits){break}if(have===0){break inf_leave}have--;hold+=input[next++]<>>=last_bits;bits-=last_bits;state.back+=last_bits}hold>>>=here_bits;bits-=here_bits;state.back+=here_bits;state.length=here_val;if(here_op===0){state.mode=LIT;break}if(here_op&32){state.back=-1;state.mode=TYPE;break}if(here_op&64){strm.msg="invalid literal/length code";state.mode=BAD;break}state.extra=here_op&15;state.mode=LENEXT;case LENEXT:if(state.extra){n=state.extra;while(bits>>=state.extra;bits-=state.extra;state.back+=state.extra}state.was=state.length;state.mode=DIST;case DIST:for(;;){here=state.distcode[hold&(1<>>24;here_op=here>>>16&255;here_val=here&65535;if(here_bits<=bits){break}if(have===0){break inf_leave}have--;hold+=input[next++]<>last_bits)];here_bits=here>>>24;here_op=here>>>16&255;here_val=here&65535;if(last_bits+here_bits<=bits){break}if(have===0){break inf_leave}have--;hold+=input[next++]<>>=last_bits;bits-=last_bits;state.back+=last_bits}hold>>>=here_bits;bits-=here_bits;state.back+=here_bits;if(here_op&64){strm.msg="invalid distance code";state.mode=BAD;break}state.offset=here_val;state.extra=here_op&15;state.mode=DISTEXT;case DISTEXT:if(state.extra){n=state.extra;while(bits>>=state.extra;bits-=state.extra;state.back+=state.extra}if(state.offset>state.dmax){strm.msg="invalid distance too far back";state.mode=BAD;break}state.mode=MATCH;case MATCH:if(left===0){break inf_leave}copy=_out-left;if(state.offset>copy){copy=state.offset-copy;if(copy>state.whave){if(state.sane){strm.msg="invalid distance too far back";state.mode=BAD;break}}if(copy>state.wnext){copy-=state.wnext;from=state.wsize-copy}else{from=state.wnext-copy}if(copy>state.length){copy=state.length}from_source=state.window}else{from_source=output;from=put-state.offset;copy=state.length}if(copy>left){copy=left}left-=copy;state.length-=copy;do{output[put++]=from_source[from++]}while(--copy);if(state.length===0){state.mode=LEN}break;case LIT:if(left===0){break inf_leave}output[put++]=state.length;left--;state.mode=LEN;break;case CHECK:if(state.wrap){while(bits<32){if(have===0){break inf_leave}have--;hold|=input[next++]<=1;max--){if(count[max]!==0){break}}if(root>max){root=max}if(max===0){table[table_index++]=1<<24|64<<16|0;table[table_index++]=1<<24|64<<16|0;opts.bits=1;return 0}for(min=1;min0&&(type===CODES||max!==1)){return-1}offs[1]=0;for(len=1;lenENOUGH_LENS||type===DISTS&&used>ENOUGH_DISTS){return 1}var i=0;for(;;){i++;here_bits=len-drop;if(work[sym]end){here_op=extra[extra_index+work[sym]];here_val=base[base_index+work[sym]]}else{here_op=32+64;here_val=0}incr=1<>drop)+fill]=here_bits<<24|here_op<<16|here_val|0}while(fill!==0);incr=1<>=1}if(incr!==0){huff&=incr-1;huff+=incr}else{huff=0}sym++;if(--count[len]===0){if(len===max){break}len=lens[lens_index+work[sym]]}if(len>root&&(huff&mask)!==low){if(drop===0){drop=root}next+=min;curr=len-drop;left=1<ENOUGH_LENS||type===DISTS&&used>ENOUGH_DISTS){return 1}low=huff&mask;table[low]=root<<24|curr<<16|next-table_index|0}}if(huff!==0){table[next+huff]=len-drop<<24|64<<16|0}opts.bits=root;return 0}},{"../utils/common":4}],14:[function(require,module,exports){"use strict";module.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],15:[function(require,module,exports){"use strict";var utils=require("../utils/common");var Z_FIXED=4;var Z_BINARY=0;var Z_TEXT=1;var Z_UNKNOWN=2;function zero(buf){var len=buf.length;while(--len>=0){buf[len]=0}}var STORED_BLOCK=0;var STATIC_TREES=1;var DYN_TREES=2;var MIN_MATCH=3;var MAX_MATCH=258;var LENGTH_CODES=29;var LITERALS=256;var L_CODES=LITERALS+1+LENGTH_CODES;var D_CODES=30;var BL_CODES=19;var HEAP_SIZE=2*L_CODES+1;var MAX_BITS=15;var Buf_size=16;var MAX_BL_BITS=7;var END_BLOCK=256;var REP_3_6=16;var REPZ_3_10=17;var REPZ_11_138=18;var extra_lbits=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];var extra_dbits=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];var extra_blbits=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];var bl_order=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];var DIST_CODE_LEN=512;var static_ltree=new Array((L_CODES+2)*2);zero(static_ltree);var static_dtree=new Array(D_CODES*2);zero(static_dtree);var _dist_code=new Array(DIST_CODE_LEN);zero(_dist_code);var _length_code=new Array(MAX_MATCH-MIN_MATCH+1);zero(_length_code);var base_length=new Array(LENGTH_CODES);zero(base_length);var base_dist=new Array(D_CODES);zero(base_dist);var StaticTreeDesc=function(static_tree,extra_bits,extra_base,elems,max_length){this.static_tree=static_tree;this.extra_bits=extra_bits;this.extra_base=extra_base;this.elems=elems;this.max_length=max_length;this.has_stree=static_tree&&static_tree.length};var static_l_desc;var static_d_desc;var static_bl_desc;var TreeDesc=function(dyn_tree,stat_desc){this.dyn_tree=dyn_tree;this.max_code=0;this.stat_desc=stat_desc};function d_code(dist){return dist<256?_dist_code[dist]:_dist_code[256+(dist>>>7)]}function put_short(s,w){s.pending_buf[s.pending++]=w&255;s.pending_buf[s.pending++]=w>>>8&255}function send_bits(s,value,length){if(s.bi_valid>Buf_size-length){s.bi_buf|=value<>Buf_size-s.bi_valid;s.bi_valid+=length-Buf_size}else{s.bi_buf|=value<>>=1;res<<=1}while(--len>0);return res>>>1}function bi_flush(s){if(s.bi_valid===16){put_short(s,s.bi_buf);s.bi_buf=0;s.bi_valid=0}else if(s.bi_valid>=8){s.pending_buf[s.pending++]=s.bi_buf&255;s.bi_buf>>=8;s.bi_valid-=8}}function gen_bitlen(s,desc){var tree=desc.dyn_tree;var max_code=desc.max_code;var stree=desc.stat_desc.static_tree;var has_stree=desc.stat_desc.has_stree;var extra=desc.stat_desc.extra_bits;var base=desc.stat_desc.extra_base;var max_length=desc.stat_desc.max_length;var h;var n,m;var bits;var xbits;var f;var overflow=0;for(bits=0;bits<=MAX_BITS;bits++){s.bl_count[bits]=0}tree[s.heap[s.heap_max]*2+1]=0;for(h=s.heap_max+1;hmax_length){bits=max_length;overflow++}tree[n*2+1]=bits;if(n>max_code){continue}s.bl_count[bits]++;xbits=0;if(n>=base){xbits=extra[n-base]}f=tree[n*2];s.opt_len+=f*(bits+xbits);if(has_stree){s.static_len+=f*(stree[n*2+1]+xbits)}}if(overflow===0){return}do{bits=max_length-1;while(s.bl_count[bits]===0){bits--}s.bl_count[bits]--;s.bl_count[bits+1]+=2;s.bl_count[max_length]--;overflow-=2}while(overflow>0);for(bits=max_length;bits!==0;bits--){n=s.bl_count[bits];while(n!==0){m=s.heap[--h];if(m>max_code){continue}if(tree[m*2+1]!==bits){s.opt_len+=(bits-tree[m*2+1])*tree[m*2];tree[m*2+1]=bits}n--}}}function gen_codes(tree,max_code,bl_count){var next_code=new Array(MAX_BITS+1);var code=0;var bits;var n;for(bits=1;bits<=MAX_BITS;bits++){next_code[bits]=code=code+bl_count[bits-1]<<1}for(n=0;n<=max_code;n++){var len=tree[n*2+1];if(len===0){continue}tree[n*2]=bi_reverse(next_code[len]++,len)}}function tr_static_init(){var n;var bits;var length;var code;var dist;var bl_count=new Array(MAX_BITS+1);length=0;for(code=0;code>=7;for(;code8){put_short(s,s.bi_buf)}else if(s.bi_valid>0){s.pending_buf[s.pending++]=s.bi_buf}s.bi_buf=0;s.bi_valid=0}function copy_block(s,buf,len,header){bi_windup(s);if(header){put_short(s,len);put_short(s,~len)}utils.arraySet(s.pending_buf,s.window,buf,len,s.pending);s.pending+=len}function smaller(tree,n,m,depth){ -var _n2=n*2;var _m2=m*2;return tree[_n2]>1;n>=1;n--){pqdownheap(s,tree,n)}node=elems;do{n=s.heap[1];s.heap[1]=s.heap[s.heap_len--];pqdownheap(s,tree,1);m=s.heap[1];s.heap[--s.heap_max]=n;s.heap[--s.heap_max]=m;tree[node*2]=tree[n*2]+tree[m*2];s.depth[node]=(s.depth[n]>=s.depth[m]?s.depth[n]:s.depth[m])+1;tree[n*2+1]=tree[m*2+1]=node;s.heap[1]=node++;pqdownheap(s,tree,1)}while(s.heap_len>=2);s.heap[--s.heap_max]=s.heap[1];gen_bitlen(s,desc);gen_codes(tree,max_code,s.bl_count)}function scan_tree(s,tree,max_code){var n;var prevlen=-1;var curlen;var nextlen=tree[0*2+1];var count=0;var max_count=7;var min_count=4;if(nextlen===0){max_count=138;min_count=3}tree[(max_code+1)*2+1]=65535;for(n=0;n<=max_code;n++){curlen=nextlen;nextlen=tree[(n+1)*2+1];if(++count=3;max_blindex--){if(s.bl_tree[bl_order[max_blindex]*2+1]!==0){break}}s.opt_len+=3*(max_blindex+1)+5+5+4;return max_blindex}function send_all_trees(s,lcodes,dcodes,blcodes){var rank;send_bits(s,lcodes-257,5);send_bits(s,dcodes-1,5);send_bits(s,blcodes-4,4);for(rank=0;rank>>=1){if(black_mask&1&&s.dyn_ltree[n*2]!==0){return Z_BINARY}}if(s.dyn_ltree[9*2]!==0||s.dyn_ltree[10*2]!==0||s.dyn_ltree[13*2]!==0){return Z_TEXT}for(n=32;n0){if(s.strm.data_type===Z_UNKNOWN){s.strm.data_type=detect_data_type(s)}build_tree(s,s.l_desc);build_tree(s,s.d_desc);max_blindex=build_bl_tree(s);opt_lenb=s.opt_len+3+7>>>3;static_lenb=s.static_len+3+7>>>3;if(static_lenb<=opt_lenb){opt_lenb=static_lenb}}else{opt_lenb=static_lenb=stored_len+5}if(stored_len+4<=opt_lenb&&buf!==-1){_tr_stored_block(s,buf,stored_len,last)}else if(s.strategy===Z_FIXED||static_lenb===opt_lenb){send_bits(s,(STATIC_TREES<<1)+(last?1:0),3);compress_block(s,static_ltree,static_dtree)}else{send_bits(s,(DYN_TREES<<1)+(last?1:0),3);send_all_trees(s,s.l_desc.max_code+1,s.d_desc.max_code+1,max_blindex+1);compress_block(s,s.dyn_ltree,s.dyn_dtree)}init_block(s);if(last){bi_windup(s)}}function _tr_tally(s,dist,lc){s.pending_buf[s.d_buf+s.last_lit*2]=dist>>>8&255;s.pending_buf[s.d_buf+s.last_lit*2+1]=dist&255;s.pending_buf[s.l_buf+s.last_lit]=lc&255;s.last_lit++;if(dist===0){s.dyn_ltree[lc*2]++}else{s.matches++;dist--;s.dyn_ltree[(_length_code[lc]+LITERALS+1)*2]++;s.dyn_dtree[d_code(dist)*2]++}return s.last_lit===s.lit_bufsize-1}exports._tr_init=_tr_init;exports._tr_stored_block=_tr_stored_block;exports._tr_flush_block=_tr_flush_block;exports._tr_tally=_tr_tally;exports._tr_align=_tr_align},{"../utils/common":4}],16:[function(require,module,exports){"use strict";function ZStream(){this.input=null;this.next_in=0;this.avail_in=0;this.total_in=0;this.output=null;this.next_out=0;this.avail_out=0;this.total_out=0;this.msg="";this.state=null;this.data_type=2;this.adler=0}module.exports=ZStream},{}],17:[function(require,module,exports){"use strict";var nifti=nifti||{};nifti.NIFTI1=nifti.NIFTI1||(typeof require!=="undefined"?require("./nifti1.js"):null);nifti.NIFTI2=nifti.NIFTI2||(typeof require!=="undefined"?require("./nifti2.js"):null);nifti.Utils=nifti.Utils||(typeof require!=="undefined"?require("./utilities.js"):null);var pako=pako||(typeof require!=="undefined"?require("pako"):null);nifti.isNIFTI1=function(data){var buf,mag1,mag2,mag3;if(data.byteLengthnifti.NIFTI1.MAGIC_COOKIE){this.extensionFlag[0]=nifti.Utils.getByteAt(rawData,348);this.extensionFlag[1]=nifti.Utils.getByteAt(rawData,348+1);this.extensionFlag[2]=nifti.Utils.getByteAt(rawData,348+2);this.extensionFlag[3]=nifti.Utils.getByteAt(rawData,348+3);if(this.extensionFlag[0]){this.extensionSize=this.getExtensionSize(rawData);this.extensionCode=this.getExtensionCode(rawData)}}};nifti.NIFTI1.prototype.toFormattedString=function(){var fmt=nifti.Utils.formatNumber,string="";string+="Dim Info = "+this.dim_info+"\n";string+="Image Dimensions (1-8): "+this.dims[0]+", "+this.dims[1]+", "+this.dims[2]+", "+this.dims[3]+", "+this.dims[4]+", "+this.dims[5]+", "+this.dims[6]+", "+this.dims[7]+"\n";string+="Intent Parameters (1-3): "+this.intent_p1+", "+this.intent_p2+", "+this.intent_p3+"\n";string+="Intent Code = "+this.intent_code+"\n";string+="Datatype = "+this.datatypeCode+" ("+this.getDatatypeCodeString(this.datatypeCode)+")\n";string+="Bits Per Voxel = "+this.numBitsPerVoxel+"\n";string+="Slice Start = "+this.slice_start+"\n";string+="Voxel Dimensions (1-8): "+fmt(this.pixDims[0])+", "+fmt(this.pixDims[1])+", "+fmt(this.pixDims[2])+", "+fmt(this.pixDims[3])+", "+fmt(this.pixDims[4])+", "+fmt(this.pixDims[5])+", "+fmt(this.pixDims[6])+", "+fmt(this.pixDims[7])+"\n";string+="Image Offset = "+this.vox_offset+"\n";string+="Data Scale: Slope = "+fmt(this.scl_slope)+" Intercept = "+fmt(this.scl_inter)+"\n";string+="Slice End = "+this.slice_end+"\n";string+="Slice Code = "+this.slice_code+"\n";string+="Units Code = "+this.xyzt_units+" ("+this.getUnitsCodeString(nifti.NIFTI1.SPATIAL_UNITS_MASK&this.xyzt_units)+", "+this.getUnitsCodeString(nifti.NIFTI1.TEMPORAL_UNITS_MASK&this.xyzt_units)+")\n";string+="Display Range: Max = "+fmt(this.cal_max)+" Min = "+fmt(this.cal_min)+"\n";string+="Slice Duration = "+this.slice_duration+"\n";string+="Time Axis Shift = "+this.toffset+"\n";string+='Description: "'+this.description+'"\n';string+='Auxiliary File: "'+this.aux_file+'"\n';string+="Q-Form Code = "+this.qform_code+" ("+this.getTransformCodeString(this.qform_code)+")\n";string+="S-Form Code = "+this.sform_code+" ("+this.getTransformCodeString(this.sform_code)+")\n";string+="Quaternion Parameters: "+"b = "+fmt(this.quatern_b)+" "+"c = "+fmt(this.quatern_c)+" "+"d = "+fmt(this.quatern_d)+"\n";string+="Quaternion Offsets: "+"x = "+this.qoffset_x+" "+"y = "+this.qoffset_y+" "+"z = "+this.qoffset_z+"\n";string+="S-Form Parameters X: "+fmt(this.affine[0][0])+", "+fmt(this.affine[0][1])+", "+fmt(this.affine[0][2])+", "+fmt(this.affine[0][3])+"\n";string+="S-Form Parameters Y: "+fmt(this.affine[1][0])+", "+fmt(this.affine[1][1])+", "+fmt(this.affine[1][2])+", "+fmt(this.affine[1][3])+"\n";string+="S-Form Parameters Z: "+fmt(this.affine[2][0])+", "+fmt(this.affine[2][1])+", "+fmt(this.affine[2][2])+", "+fmt(this.affine[2][3])+"\n";string+='Intent Name: "'+this.intent_name+'"\n';if(this.extensionFlag[0]){string+="Extension: Size = "+this.extensionSize+" Code = "+this.extensionCode+"\n"}return string};nifti.NIFTI1.prototype.getDatatypeCodeString=function(code){if(code===nifti.NIFTI1.TYPE_UINT8){return"1-Byte Unsigned Integer"}else if(code===nifti.NIFTI1.TYPE_INT16){return"2-Byte Signed Integer"}else if(code===nifti.NIFTI1.TYPE_INT32){return"4-Byte Signed Integer"}else if(code===nifti.NIFTI1.TYPE_FLOAT32){return"4-Byte Float"}else if(code===nifti.NIFTI1.TYPE_FLOAT64){return"8-Byte Float"}else if(code===nifti.NIFTI1.TYPE_RGB24){return"RGB"}else if(code===nifti.NIFTI1.TYPE_INT8){return"1-Byte Signed Integer"}else if(code===nifti.NIFTI1.TYPE_UINT16){return"2-Byte Unsigned Integer"}else if(code===nifti.NIFTI1.TYPE_UINT32){return"4-Byte Unsigned Integer"}else if(code===nifti.NIFTI1.TYPE_INT64){return"8-Byte Signed Integer"}else if(code===nifti.NIFTI1.TYPE_UINT64){return"8-Byte Unsigned Integer"}else{return"Unknown"}};nifti.NIFTI1.prototype.getTransformCodeString=function(code){if(code===nifti.NIFTI1.XFORM_SCANNER_ANAT){return"Scanner"}else if(code===nifti.NIFTI1.XFORM_ALIGNED_ANAT){return"Aligned"}else if(code===nifti.NIFTI1.XFORM_TALAIRACH){return"Talairach"}else if(code===nifti.NIFTI1.XFORM_MNI_152){return"MNI"}else{return"Unknown"}};nifti.NIFTI1.prototype.getUnitsCodeString=function(code){if(code===nifti.NIFTI1.UNITS_METER){return"Meters"}else if(code===nifti.NIFTI1.UNITS_MM){return"Millimeters"}else if(code===nifti.NIFTI1.UNITS_MICRON){return"Microns"}else if(code===nifti.NIFTI1.UNITS_SEC){return"Seconds"}else if(code===nifti.NIFTI1.UNITS_MSEC){return"Milliseconds"}else if(code===nifti.NIFTI1.UNITS_USEC){return"Microseconds"}else if(code===nifti.NIFTI1.UNITS_HZ){return"Hz"}else if(code===nifti.NIFTI1.UNITS_PPM){return"PPM"}else if(code===nifti.NIFTI1.UNITS_RADS){return"Rads"}else{return"Unknown"}};nifti.NIFTI1.prototype.getQformMat=function(){return this.convertNiftiQFormToNiftiSForm(this.quatern_b,this.quatern_c,this.quatern_d,this.qoffset_x,this.qoffset_y,this.qoffset_z,this.pixDims[1],this.pixDims[2],this.pixDims[3],this.pixDims[0])};nifti.NIFTI1.prototype.convertNiftiQFormToNiftiSForm=function(qb,qc,qd,qx,qy,qz,dx,dy,dz,qfac){var R=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],a,b=qb,c=qc,d=qd,xd,yd,zd;R[3][0]=R[3][1]=R[3][2]=0;R[3][3]=1;a=1-(b*b+c*c+d*d);if(a<1e-7){a=1/Math.sqrt(b*b+c*c+d*d);b*=a;c*=a;d*=a;a=0}else{a=Math.sqrt(a)}xd=dx>0?dx:1;yd=dy>0?dy:1;zd=dz>0?dz:1;if(qfac<0){zd=-zd}R[0][0]=(a*a+b*b-c*c-d*d)*xd;R[0][1]=2*(b*c-a*d)*yd;R[0][2]=2*(b*d+a*c)*zd;R[1][0]=2*(b*c+a*d)*xd;R[1][1]=(a*a+c*c-b*b-d*d)*yd;R[1][2]=2*(c*d-a*b)*zd;R[2][0]=2*(b*d-a*c)*xd;R[2][1]=2*(c*d+a*b)*yd;R[2][2]=(a*a+d*d-c*c-b*b)*zd;R[0][3]=qx;R[1][3]=qy;R[2][3]=qz;return R};nifti.NIFTI1.prototype.convertNiftiSFormToNEMA=function(R){var xi,xj,xk,yi,yj,yk,zi,zj,zk,val,detQ,detP,i,j,k,p,q,r,ibest,jbest,kbest,pbest,qbest,rbest,M,vbest,Q,P,iChar,jChar,kChar,iSense,jSense,kSense;k=0;Q=[[0,0,0],[0,0,0],[0,0,0]];P=[[0,0,0],[0,0,0],[0,0,0]];xi=R[0][0];xj=R[0][1];xk=R[0][2];yi=R[1][0];yj=R[1][1];yk=R[1][2];zi=R[2][0];zj=R[2][1];zk=R[2][2];val=Math.sqrt(xi*xi+yi*yi+zi*zi);if(val===0){return null}xi/=val;yi/=val;zi/=val;val=Math.sqrt(xj*xj+yj*yj+zj*zj);if(val===0){return null}xj/=val;yj/=val;zj/=val;val=xi*xj+yi*yj+zi*zj;if(Math.abs(val)>1e-4){xj-=val*xi;yj-=val*yi;zj-=val*zi;val=Math.sqrt(xj*xj+yj*yj+zj*zj);if(val===0){return null}xj/=val;yj/=val;zj/=val}val=Math.sqrt(xk*xk+yk*yk+zk*zk);if(val===0){xk=yi*zj-zi*yj;yk=zi*xj-zj*xi;zk=xi*yj-yi*xj}else{xk/=val;yk/=val;zk/=val}val=xi*xk+yi*yk+zi*zk;if(Math.abs(val)>1e-4){xk-=val*xi;yk-=val*yi;zk-=val*zi;val=Math.sqrt(xk*xk+yk*yk+zk*zk);if(val===0){return null}xk/=val;yk/=val;zk/=val}val=xj*xk+yj*yk+zj*zk;if(Math.abs(val)>1e-4){xk-=val*xj;yk-=val*yj;zk-=val*zj;val=Math.sqrt(xk*xk+yk*yk+zk*zk);if(val===0){return null}xk/=val;yk/=val;zk/=val}Q[0][0]=xi;Q[0][1]=xj;Q[0][2]=xk;Q[1][0]=yi;Q[1][1]=yj;Q[1][2]=yk;Q[2][0]=zi;Q[2][1]=zj;Q[2][2]=zk;detQ=this.nifti_mat33_determ(Q);if(detQ===0){return null}vbest=-666;ibest=pbest=qbest=rbest=1;jbest=2;kbest=3;for(i=1;i<=3;i+=1){for(j=1;j<=3;j+=1){if(i!==j){for(k=1;k<=3;k+=1){if(!(i===k||j===k)){P[0][0]=P[0][1]=P[0][2]=P[1][0]=P[1][1]=P[1][2]=P[2][0]=P[2][1]=P[2][2]=0;for(p=-1;p<=1;p+=2){for(q=-1;q<=1;q+=2){for(r=-1;r<=1;r+=2){P[0][i-1]=p;P[1][j-1]=q;P[2][k-1]=r;detP=this.nifti_mat33_determ(P);if(detP*detQ>0){M=this.nifti_mat33_mul(P,Q);val=M[0][0]+M[1][1]+M[2][2];if(val>vbest){vbest=val;ibest=i;jbest=j;kbest=k;pbest=p;qbest=q;rbest=r}}}}}}}}}}iChar=jChar=kChar=iSense=jSense=kSense=0;switch(ibest*pbest){case 1:iChar="X";iSense="+";break;case-1:iChar="X";iSense="-";break;case 2:iChar="Y";iSense="+";break;case-2:iChar="Y";iSense="-";break;case 3:iChar="Z";iSense="+";break;case-3:iChar="Z";iSense="-";break}switch(jbest*qbest){case 1:jChar="X";jSense="+";break;case-1:jChar="X";jSense="-";break;case 2:jChar="Y";jSense="+";break;case-2:jChar="Y";jSense="-";break;case 3:jChar="Z";jSense="+";break;case-3:jChar="Z";jSense="-";break}switch(kbest*rbest){case 1:kChar="X";kSense="+";break;case-1:kChar="X";kSense="-";break;case 2:kChar="Y";kSense="+";break;case-2:kChar="Y";kSense="-";break;case 3:kChar="Z";kSense="+";break;case-3:kChar="Z";kSense="-";break}return iChar+jChar+kChar+iSense+jSense+kSense};nifti.NIFTI1.prototype.nifti_mat33_mul=function(A,B){var C=[[0,0,0],[0,0,0],[0,0,0]],i,j;for(i=0;i<3;i+=1){for(j=0;j<3;j+=1){C[i][j]=A[i][0]*B[0][j]+A[i][1]*B[1][j]+A[i][2]*B[2][j]}}return C};nifti.NIFTI1.prototype.nifti_mat33_determ=function(R){var r11,r12,r13,r21,r22,r23,r31,r32,r33;r11=R[0][0];r12=R[0][1];r13=R[0][2];r21=R[1][0];r22=R[1][1];r23=R[1][2];r31=R[2][0];r32=R[2][1];r33=R[2][2];return r11*r22*r33-r11*r32*r23-r21*r12*r33+r21*r32*r13+r31*r12*r23-r31*r22*r13};nifti.NIFTI1.prototype.getExtensionLocation=function(){return nifti.NIFTI1.MAGIC_COOKIE+4};nifti.NIFTI1.prototype.getExtensionSize=function(data){return nifti.Utils.getIntAt(data,this.getExtensionLocation(),this.littleEndian)};nifti.NIFTI1.prototype.getExtensionCode=function(data){return nifti.Utils.getIntAt(data,this.getExtensionLocation()+4,this.littleEndian)};var moduleType=typeof module;if(moduleType!=="undefined"&&module.exports){module.exports=nifti.NIFTI1}},{"./utilities.js":20}],19:[function(require,module,exports){"use strict";var nifti=nifti||{};nifti.Utils=nifti.Utils||(typeof require!=="undefined"?require("./utilities.js"):null);nifti.NIFTI1=nifti.NIFTI1||(typeof require!=="undefined"?require("./nifti1.js"):null);nifti.NIFTI2=nifti.NIFTI2||function(){this.littleEndian=false;this.dim_info=0;this.dims=[];this.intent_p1=0;this.intent_p2=0;this.intent_p3=0;this.intent_code=0;this.datatypeCode=0;this.numBitsPerVoxel=0;this.slice_start=0;this.slice_end=0;this.slice_code=0;this.pixDims=[];this.vox_offset=0;this.scl_slope=1;this.scl_inter=0;this.xyzt_units=0;this.cal_max=0;this.cal_min=0;this.slice_duration=0;this.toffset=0;this.description="";this.aux_file="";this.intent_name="";this.qform_code=0;this.sform_code=0;this.quatern_b=0;this.quatern_c=0;this.quatern_d=0;this.qoffset_x=0;this.qoffset_y=0;this.qoffset_z=0;this.affine=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];this.magic=0;this.extensionFlag=[0,0,0,0]};nifti.NIFTI2.MAGIC_COOKIE=540;nifti.NIFTI2.MAGIC_NUMBER_LOCATION=4;nifti.NIFTI2.MAGIC_NUMBER=[110,43,50,0,13,10,26,10];nifti.NIFTI2.prototype.readHeader=function(data){var rawData=new DataView(data),magicCookieVal=nifti.Utils.getIntAt(rawData,0,this.littleEndian),ctr,ctrOut,ctrIn,index,array;if(magicCookieVal!==nifti.NIFTI2.MAGIC_COOKIE){this.littleEndian=true;magicCookieVal=nifti.Utils.getIntAt(rawData,0,this.littleEndian)}if(magicCookieVal!==nifti.NIFTI2.MAGIC_COOKIE){throw new Error("This does not appear to be a NIFTI file!")}this.datatypeCode=nifti.Utils.getShortAt(rawData,12,this.littleEndian);this.numBitsPerVoxel=nifti.Utils.getShortAt(rawData,14,this.littleEndian);for(ctr=0;ctr<8;ctr+=1){index=16+ctr*8;this.dims[ctr]=nifti.Utils.getLongAt(rawData,index,this.littleEndian)}this.intent_p1=nifti.Utils.getDoubleAt(rawData,80,this.littleEndian);this.intent_p2=nifti.Utils.getDoubleAt(rawData,88,this.littleEndian);this.intent_p3=nifti.Utils.getDoubleAt(rawData,96,this.littleEndian);for(ctr=0;ctr<8;ctr+=1){index=104+ctr*8;this.pixDims[ctr]=nifti.Utils.getDoubleAt(rawData,index,this.littleEndian)}this.vox_offset=nifti.Utils.getLongAt(rawData,168,this.littleEndian);this.scl_slope=nifti.Utils.getDoubleAt(rawData,176,this.littleEndian);this.scl_inter=nifti.Utils.getDoubleAt(rawData,184,this.littleEndian);this.cal_max=nifti.Utils.getDoubleAt(rawData,192,this.littleEndian);this.cal_min=nifti.Utils.getDoubleAt(rawData,200,this.littleEndian);this.slice_duration=nifti.Utils.getDoubleAt(rawData,208,this.littleEndian);this.toffset=nifti.Utils.getDoubleAt(rawData,216,this.littleEndian);this.slice_start=nifti.Utils.getLongAt(rawData,224,this.littleEndian);this.slice_end=nifti.Utils.getLongAt(rawData,232,this.littleEndian);this.description=nifti.Utils.getStringAt(rawData,240,240+80);this.aux_file=nifti.Utils.getStringAt(rawData,320,320+24);this.qform_code=nifti.Utils.getIntAt(rawData,344,this.littleEndian);this.sform_code=nifti.Utils.getIntAt(rawData,348,this.littleEndian);this.quatern_b=nifti.Utils.getDoubleAt(rawData,352,this.littleEndian);this.quatern_c=nifti.Utils.getDoubleAt(rawData,360,this.littleEndian);this.quatern_d=nifti.Utils.getDoubleAt(rawData,368,this.littleEndian);this.qoffset_x=nifti.Utils.getDoubleAt(rawData,376,this.littleEndian);this.qoffset_y=nifti.Utils.getDoubleAt(rawData,384,this.littleEndian);this.qoffset_z=nifti.Utils.getDoubleAt(rawData,392,this.littleEndian);for(ctrOut=0;ctrOut<3;ctrOut+=1){for(ctrIn=0;ctrIn<4;ctrIn+=1){index=400+(ctrOut*4+ctrIn)*8;this.affine[ctrOut][ctrIn]=nifti.Utils.getDoubleAt(rawData,index,this.littleEndian)}}this.affine[3][0]=0;this.affine[3][1]=0;this.affine[3][2]=0;this.affine[3][3]=1;this.slice_code=nifti.Utils.getIntAt(rawData,496,this.littleEndian);this.xyzt_units=nifti.Utils.getIntAt(rawData,500,this.littleEndian);this.intent_code=nifti.Utils.getIntAt(rawData,504,this.littleEndian);this.intent_name=nifti.Utils.getStringAt(rawData,508,508+16);this.dim_info=nifti.Utils.getByteAt(rawData,524);if(rawData.byteLength>nifti.NIFTI2.MAGIC_COOKIE){this.extensionFlag[0]=nifti.Utils.getByteAt(rawData,540);this.extensionFlag[1]=nifti.Utils.getByteAt(rawData,540+1);this.extensionFlag[2]=nifti.Utils.getByteAt(rawData,540+2);this.extensionFlag[3]=nifti.Utils.getByteAt(rawData,540+3);if(this.extensionFlag[0]){this.extensionSize=this.getExtensionSize(rawData);this.extensionCode=this.getExtensionCode(rawData)}}};nifti.NIFTI2.prototype.toFormattedString=function(){var fmt=nifti.Utils.formatNumber,string="";string+="Datatype = "+ +this.datatypeCode+" ("+this.getDatatypeCodeString(this.datatypeCode)+")\n";string+="Bits Per Voxel = "+" = "+this.numBitsPerVoxel+"\n";string+="Image Dimensions"+" (1-8): "+this.dims[0]+", "+this.dims[1]+", "+this.dims[2]+", "+this.dims[3]+", "+this.dims[4]+", "+this.dims[5]+", "+this.dims[6]+", "+this.dims[7]+"\n";string+="Intent Parameters (1-3): "+this.intent_p1+", "+this.intent_p2+", "+this.intent_p3+"\n";string+="Voxel Dimensions (1-8): "+fmt(this.pixDims[0])+", "+fmt(this.pixDims[1])+", "+fmt(this.pixDims[2])+", "+fmt(this.pixDims[3])+", "+fmt(this.pixDims[4])+", "+fmt(this.pixDims[5])+", "+fmt(this.pixDims[6])+", "+fmt(this.pixDims[7])+"\n";string+="Image Offset = "+this.vox_offset+"\n";string+="Data Scale: Slope = "+fmt(this.scl_slope)+" Intercept = "+fmt(this.scl_inter)+"\n";string+="Display Range: Max = "+fmt(this.cal_max)+" Min = "+fmt(this.cal_min)+"\n";string+="Slice Duration = "+this.slice_duration+"\n";string+="Time Axis Shift = "+this.toffset+"\n";string+="Slice Start = "+this.slice_start+"\n";string+="Slice End = "+this.slice_end+"\n";string+='Description: "'+this.description+'"\n';string+='Auxiliary File: "'+this.aux_file+'"\n';string+="Q-Form Code = "+this.qform_code+" ("+this.getTransformCodeString(this.qform_code)+")\n";string+="S-Form Code = "+this.sform_code+" ("+this.getTransformCodeString(this.sform_code)+")\n";string+="Quaternion Parameters: "+"b = "+fmt(this.quatern_b)+" "+"c = "+fmt(this.quatern_c)+" "+"d = "+fmt(this.quatern_d)+"\n";string+="Quaternion Offsets: "+"x = "+this.qoffset_x+" "+"y = "+this.qoffset_y+" "+"z = "+this.qoffset_z+"\n";string+="S-Form Parameters X: "+fmt(this.affine[0][0])+", "+fmt(this.affine[0][1])+", "+fmt(this.affine[0][2])+", "+fmt(this.affine[0][3])+"\n";string+="S-Form Parameters Y: "+fmt(this.affine[1][0])+", "+fmt(this.affine[1][1])+", "+fmt(this.affine[1][2])+", "+fmt(this.affine[1][3])+"\n";string+="S-Form Parameters Z: "+fmt(this.affine[2][0])+", "+fmt(this.affine[2][1])+", "+fmt(this.affine[2][2])+", "+fmt(this.affine[2][3])+"\n";string+="Slice Code = "+this.slice_code+"\n";string+="Units Code = "+this.xyzt_units+" ("+this.getUnitsCodeString(nifti.NIFTI1.SPATIAL_UNITS_MASK&this.xyzt_units)+", "+this.getUnitsCodeString(nifti.NIFTI1.TEMPORAL_UNITS_MASK&this.xyzt_units)+")\n";string+="Intent Code = "+this.intent_code+"\n";string+='Intent Name: "'+this.intent_name+'"\n';string+="Dim Info = "+this.dim_info+"\n";return string};nifti.NIFTI2.prototype.getExtensionLocation=function(){return nifti.NIFTI2.MAGIC_COOKIE+4};nifti.NIFTI2.prototype.getExtensionSize=nifti.NIFTI1.prototype.getExtensionSize;nifti.NIFTI2.prototype.getExtensionCode=nifti.NIFTI1.prototype.getExtensionCode;nifti.NIFTI2.prototype.getDatatypeCodeString=nifti.NIFTI1.prototype.getDatatypeCodeString;nifti.NIFTI2.prototype.getTransformCodeString=nifti.NIFTI1.prototype.getTransformCodeString;nifti.NIFTI2.prototype.getUnitsCodeString=nifti.NIFTI1.prototype.getUnitsCodeString;nifti.NIFTI2.prototype.getQformMat=nifti.NIFTI1.prototype.getQformMat;nifti.NIFTI2.prototype.convertNiftiQFormToNiftiSForm=nifti.NIFTI1.prototype.convertNiftiQFormToNiftiSForm;nifti.NIFTI2.prototype.convertNiftiSFormToNEMA=nifti.NIFTI1.prototype.convertNiftiSFormToNEMA;nifti.NIFTI2.prototype.nifti_mat33_mul=nifti.NIFTI1.prototype.nifti_mat33_mul;nifti.NIFTI2.prototype.nifti_mat33_determ=nifti.NIFTI1.prototype.nifti_mat33_determ;var moduleType=typeof module;if(moduleType!=="undefined"&&module.exports){module.exports=nifti.NIFTI2}},{"./nifti1.js":18,"./utilities.js":20}],20:[function(require,module,exports){"use strict";var nifti=nifti||{};nifti.Utils=nifti.Utils||{};nifti.Utils.crcTable=null;nifti.Utils.GUNZIP_MAGIC_COOKIE1=31;nifti.Utils.GUNZIP_MAGIC_COOKIE2=139;nifti.Utils.getStringAt=function(data,start,end){var str="",ctr,ch;for(ctr=start;ctr=0;ctr--){value=value*256+array[ctr]}return value};nifti.Utils.toArrayBuffer=function(buffer){var ab,view,i;ab=new ArrayBuffer(buffer.length);view=new Uint8Array(ab);for(i=0;i>>1:c>>>1}crcTable[n]=c}return crcTable};nifti.Utils.crc32=function(dataView){var crcTable=nifti.Utils.crcTable||(nifti.Utils.crcTable=nifti.Utils.makeCRCTable());var crc=0^-1;for(var i=0;i>>8^crcTable[(crc^dataView.getUint8(i))&255]}return(crc^-1)>>>0};var moduleType=typeof module;if(moduleType!=="undefined"&&module.exports){module.exports=nifti.Utils}},{}]},{},[17])(17)}); \ No newline at end of file diff --git a/v3/experimental_mode/CanvasVersion/js/nifti-reader.js b/v3/experimental_mode/CanvasVersion/js/nifti-reader.js deleted file mode 100644 index c03eb1d..0000000 --- a/v3/experimental_mode/CanvasVersion/js/nifti-reader.js +++ /dev/null @@ -1,8082 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.nifti = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Array - * - * Chunks of output data, if [[Deflate#onData]] not overriden. - **/ - -/** - * Deflate.result -> Uint8Array|Array - * - * Compressed result, generated by default [[Deflate#onData]] - * and [[Deflate#onEnd]] handlers. Filled after you push last chunk - * (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you - * push a chunk with explicit flush (call [[Deflate#push]] with - * `Z_SYNC_FLUSH` param). - **/ - -/** - * Deflate.err -> Number - * - * Error code after deflate finished. 0 (Z_OK) on success. - * You will not need it in real life, because deflate errors - * are possible only on wrong options or bad `onData` / `onEnd` - * custom handlers. - **/ - -/** - * Deflate.msg -> String - * - * Error message, if [[Deflate.err]] != 0 - **/ - - -/** - * new Deflate(options) - * - options (Object): zlib deflate options. - * - * Creates new deflator instance with specified params. Throws exception - * on bad params. Supported options: - * - * - `level` - * - `windowBits` - * - `memLevel` - * - `strategy` - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Additional options, for internal needs: - * - * - `chunkSize` - size of generated data chunks (16K by default) - * - `raw` (Boolean) - do raw deflate - * - `gzip` (Boolean) - create gzip wrapper - * - `to` (String) - if equal to 'string', then result will be "binary string" - * (each char code [0..255]) - * - `header` (Object) - custom header for gzip - * - `text` (Boolean) - true if compressed data believed to be text - * - `time` (Number) - modification time, unix timestamp - * - `os` (Number) - operation system code - * - `extra` (Array) - array of bytes with extra data (max 65536) - * - `name` (String) - file name (binary string) - * - `comment` (String) - comment (binary string) - * - `hcrc` (Boolean) - true if header crc should be added - * - * ##### Example: - * - * ```javascript - * var pako = require('pako') - * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) - * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); - * - * var deflate = new pako.Deflate({ level: 3}); - * - * deflate.push(chunk1, false); - * deflate.push(chunk2, true); // true -> last chunk - * - * if (deflate.err) { throw new Error(deflate.err); } - * - * console.log(deflate.result); - * ``` - **/ -var Deflate = function(options) { - - this.options = utils.assign({ - level: Z_DEFAULT_COMPRESSION, - method: Z_DEFLATED, - chunkSize: 16384, - windowBits: 15, - memLevel: 8, - strategy: Z_DEFAULT_STRATEGY, - to: '' - }, options || {}); - - var opt = this.options; - - if (opt.raw && (opt.windowBits > 0)) { - opt.windowBits = -opt.windowBits; - } - - else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { - opt.windowBits += 16; - } - - this.err = 0; // error code, if happens (0 = Z_OK) - this.msg = ''; // error message - this.ended = false; // used to avoid multiple onEnd() calls - this.chunks = []; // chunks of compressed data - - this.strm = new zstream(); - this.strm.avail_out = 0; - - var status = zlib_deflate.deflateInit2( - this.strm, - opt.level, - opt.method, - opt.windowBits, - opt.memLevel, - opt.strategy - ); - - if (status !== Z_OK) { - throw new Error(msg[status]); - } - - if (opt.header) { - zlib_deflate.deflateSetHeader(this.strm, opt.header); - } -}; - -/** - * Deflate#push(data[, mode]) -> Boolean - * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be - * converted to utf8 byte sequence. - * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. - * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH. - * - * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with - * new compressed chunks. Returns `true` on success. The last data block must have - * mode Z_FINISH (or `true`). That will flush internal pending buffers and call - * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you - * can use mode Z_SYNC_FLUSH, keeping the compression context. - * - * On fail call [[Deflate#onEnd]] with error code and return false. - * - * We strongly recommend to use `Uint8Array` on input for best speed (output - * array format is detected automatically). Also, don't skip last param and always - * use the same type in your code (boolean or number). That will improve JS speed. - * - * For regular `Array`-s make sure all elements are [0..255]. - * - * ##### Example - * - * ```javascript - * push(chunk, false); // push one of data chunks - * ... - * push(chunk, true); // push last chunk - * ``` - **/ -Deflate.prototype.push = function(data, mode) { - var strm = this.strm; - var chunkSize = this.options.chunkSize; - var status, _mode; - - if (this.ended) { return false; } - - _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH); - - // Convert data if needed - if (typeof data === 'string') { - // If we need to compress text, change encoding to utf8. - strm.input = strings.string2buf(data); - } else if (toString.call(data) === '[object ArrayBuffer]') { - strm.input = new Uint8Array(data); - } else { - strm.input = data; - } - - strm.next_in = 0; - strm.avail_in = strm.input.length; - - do { - if (strm.avail_out === 0) { - strm.output = new utils.Buf8(chunkSize); - strm.next_out = 0; - strm.avail_out = chunkSize; - } - status = zlib_deflate.deflate(strm, _mode); /* no bad return value */ - - if (status !== Z_STREAM_END && status !== Z_OK) { - this.onEnd(status); - this.ended = true; - return false; - } - if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) { - if (this.options.to === 'string') { - this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out))); - } else { - this.onData(utils.shrinkBuf(strm.output, strm.next_out)); - } - } - } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END); - - // Finalize on the last chunk. - if (_mode === Z_FINISH) { - status = zlib_deflate.deflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return status === Z_OK; - } - - // callback interim results if Z_SYNC_FLUSH. - if (_mode === Z_SYNC_FLUSH) { - this.onEnd(Z_OK); - strm.avail_out = 0; - return true; - } - - return true; -}; - - -/** - * Deflate#onData(chunk) -> Void - * - chunk (Uint8Array|Array|String): ouput data. Type of array depends - * on js engine support. When string output requested, each chunk - * will be string. - * - * By default, stores data blocks in `chunks[]` property and glue - * those in `onEnd`. Override this handler, if you need another behaviour. - **/ -Deflate.prototype.onData = function(chunk) { - this.chunks.push(chunk); -}; - - -/** - * Deflate#onEnd(status) -> Void - * - status (Number): deflate status. 0 (Z_OK) on success, - * other if not. - * - * Called once after you tell deflate that the input stream is - * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) - * or if an error happened. By default - join collected chunks, - * free memory and fill `results` / `err` properties. - **/ -Deflate.prototype.onEnd = function(status) { - // On success - join - if (status === Z_OK) { - if (this.options.to === 'string') { - this.result = this.chunks.join(''); - } else { - this.result = utils.flattenChunks(this.chunks); - } - } - this.chunks = []; - this.err = status; - this.msg = this.strm.msg; -}; - - -/** - * deflate(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * Compress `data` with deflate alrorythm and `options`. - * - * Supported options are: - * - * - level - * - windowBits - * - memLevel - * - strategy - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Sugar (options): - * - * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify - * negative windowBits implicitly. - * - `to` (String) - if equal to 'string', then result will be "binary string" - * (each char code [0..255]) - * - * ##### Example: - * - * ```javascript - * var pako = require('pako') - * , data = Uint8Array([1,2,3,4,5,6,7,8,9]); - * - * console.log(pako.deflate(data)); - * ``` - **/ -function deflate(input, options) { - var deflator = new Deflate(options); - - deflator.push(input, true); - - // That will never happens, if you don't cheat with options :) - if (deflator.err) { throw deflator.msg; } - - return deflator.result; -} - - -/** - * deflateRaw(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * The same as [[deflate]], but creates raw data, without wrapper - * (header and adler32 crc). - **/ -function deflateRaw(input, options) { - options = options || {}; - options.raw = true; - return deflate(input, options); -} - - -/** - * gzip(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * The same as [[deflate]], but create gzip wrapper instead of - * deflate one. - **/ -function gzip(input, options) { - options = options || {}; - options.gzip = true; - return deflate(input, options); -} - - -exports.Deflate = Deflate; -exports.deflate = deflate; -exports.deflateRaw = deflateRaw; -exports.gzip = gzip; - -},{"./utils/common":4,"./utils/strings":5,"./zlib/deflate.js":9,"./zlib/messages":14,"./zlib/zstream":16}],3:[function(require,module,exports){ -'use strict'; - - -var zlib_inflate = require('./zlib/inflate.js'); -var utils = require('./utils/common'); -var strings = require('./utils/strings'); -var c = require('./zlib/constants'); -var msg = require('./zlib/messages'); -var zstream = require('./zlib/zstream'); -var gzheader = require('./zlib/gzheader'); - -var toString = Object.prototype.toString; - -/** - * class Inflate - * - * Generic JS-style wrapper for zlib calls. If you don't need - * streaming behaviour - use more simple functions: [[inflate]] - * and [[inflateRaw]]. - **/ - -/* internal - * inflate.chunks -> Array - * - * Chunks of output data, if [[Inflate#onData]] not overriden. - **/ - -/** - * Inflate.result -> Uint8Array|Array|String - * - * Uncompressed result, generated by default [[Inflate#onData]] - * and [[Inflate#onEnd]] handlers. Filled after you push last chunk - * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you - * push a chunk with explicit flush (call [[Inflate#push]] with - * `Z_SYNC_FLUSH` param). - **/ - -/** - * Inflate.err -> Number - * - * Error code after inflate finished. 0 (Z_OK) on success. - * Should be checked if broken data possible. - **/ - -/** - * Inflate.msg -> String - * - * Error message, if [[Inflate.err]] != 0 - **/ - - -/** - * new Inflate(options) - * - options (Object): zlib inflate options. - * - * Creates new inflator instance with specified params. Throws exception - * on bad params. Supported options: - * - * - `windowBits` - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Additional options, for internal needs: - * - * - `chunkSize` - size of generated data chunks (16K by default) - * - `raw` (Boolean) - do raw inflate - * - `to` (String) - if equal to 'string', then result will be converted - * from utf8 to utf16 (javascript) string. When string output requested, - * chunk length can differ from `chunkSize`, depending on content. - * - * By default, when no options set, autodetect deflate/gzip data format via - * wrapper header. - * - * ##### Example: - * - * ```javascript - * var pako = require('pako') - * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) - * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); - * - * var inflate = new pako.Inflate({ level: 3}); - * - * inflate.push(chunk1, false); - * inflate.push(chunk2, true); // true -> last chunk - * - * if (inflate.err) { throw new Error(inflate.err); } - * - * console.log(inflate.result); - * ``` - **/ -var Inflate = function(options) { - - this.options = utils.assign({ - chunkSize: 16384, - windowBits: 0, - to: '' - }, options || {}); - - var opt = this.options; - - // Force window size for `raw` data, if not set directly, - // because we have no header for autodetect. - if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { - opt.windowBits = -opt.windowBits; - if (opt.windowBits === 0) { opt.windowBits = -15; } - } - - // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate - if ((opt.windowBits >= 0) && (opt.windowBits < 16) && - !(options && options.windowBits)) { - opt.windowBits += 32; - } - - // Gzip header has no info about windows size, we can do autodetect only - // for deflate. So, if window size not set, force it to max when gzip possible - if ((opt.windowBits > 15) && (opt.windowBits < 48)) { - // bit 3 (16) -> gzipped data - // bit 4 (32) -> autodetect gzip/deflate - if ((opt.windowBits & 15) === 0) { - opt.windowBits |= 15; - } - } - - this.err = 0; // error code, if happens (0 = Z_OK) - this.msg = ''; // error message - this.ended = false; // used to avoid multiple onEnd() calls - this.chunks = []; // chunks of compressed data - - this.strm = new zstream(); - this.strm.avail_out = 0; - - var status = zlib_inflate.inflateInit2( - this.strm, - opt.windowBits - ); - - if (status !== c.Z_OK) { - throw new Error(msg[status]); - } - - this.header = new gzheader(); - - zlib_inflate.inflateGetHeader(this.strm, this.header); -}; - -/** - * Inflate#push(data[, mode]) -> Boolean - * - data (Uint8Array|Array|ArrayBuffer|String): input data - * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. - * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH. - * - * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with - * new output chunks. Returns `true` on success. The last data block must have - * mode Z_FINISH (or `true`). That will flush internal pending buffers and call - * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you - * can use mode Z_SYNC_FLUSH, keeping the decompression context. - * - * On fail call [[Inflate#onEnd]] with error code and return false. - * - * We strongly recommend to use `Uint8Array` on input for best speed (output - * format is detected automatically). Also, don't skip last param and always - * use the same type in your code (boolean or number). That will improve JS speed. - * - * For regular `Array`-s make sure all elements are [0..255]. - * - * ##### Example - * - * ```javascript - * push(chunk, false); // push one of data chunks - * ... - * push(chunk, true); // push last chunk - * ``` - **/ -Inflate.prototype.push = function(data, mode) { - var strm = this.strm; - var chunkSize = this.options.chunkSize; - var status, _mode; - var next_out_utf8, tail, utf8str; - - // Flag to properly process Z_BUF_ERROR on testing inflate call - // when we check that all output data was flushed. - var allowBufError = false; - - if (this.ended) { return false; } - _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH); - - // Convert data if needed - if (typeof data === 'string') { - // Only binary strings can be decompressed on practice - strm.input = strings.binstring2buf(data); - } else if (toString.call(data) === '[object ArrayBuffer]') { - strm.input = new Uint8Array(data); - } else { - strm.input = data; - } - - strm.next_in = 0; - strm.avail_in = strm.input.length; - - do { - if (strm.avail_out === 0) { - strm.output = new utils.Buf8(chunkSize); - strm.next_out = 0; - strm.avail_out = chunkSize; - } - - status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */ - - if (status === c.Z_BUF_ERROR && allowBufError === true) { - status = c.Z_OK; - allowBufError = false; - } - - if (status !== c.Z_STREAM_END && status !== c.Z_OK) { - this.onEnd(status); - this.ended = true; - return false; - } - - if (strm.next_out) { - if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) { - - if (this.options.to === 'string') { - - next_out_utf8 = strings.utf8border(strm.output, strm.next_out); - - tail = strm.next_out - next_out_utf8; - utf8str = strings.buf2string(strm.output, next_out_utf8); - - // move tail - strm.next_out = tail; - strm.avail_out = chunkSize - tail; - if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); } - - this.onData(utf8str); - - } else { - this.onData(utils.shrinkBuf(strm.output, strm.next_out)); - } - } - } - - // When no more input data, we should check that internal inflate buffers - // are flushed. The only way to do it when avail_out = 0 - run one more - // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR. - // Here we set flag to process this error properly. - // - // NOTE. Deflate does not return error in this case and does not needs such - // logic. - if (strm.avail_in === 0 && strm.avail_out === 0) { - allowBufError = true; - } - - } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END); - - if (status === c.Z_STREAM_END) { - _mode = c.Z_FINISH; - } - - // Finalize on the last chunk. - if (_mode === c.Z_FINISH) { - status = zlib_inflate.inflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return status === c.Z_OK; - } - - // callback interim results if Z_SYNC_FLUSH. - if (_mode === c.Z_SYNC_FLUSH) { - this.onEnd(c.Z_OK); - strm.avail_out = 0; - return true; - } - - return true; -}; - - -/** - * Inflate#onData(chunk) -> Void - * - chunk (Uint8Array|Array|String): ouput data. Type of array depends - * on js engine support. When string output requested, each chunk - * will be string. - * - * By default, stores data blocks in `chunks[]` property and glue - * those in `onEnd`. Override this handler, if you need another behaviour. - **/ -Inflate.prototype.onData = function(chunk) { - this.chunks.push(chunk); -}; - - -/** - * Inflate#onEnd(status) -> Void - * - status (Number): inflate status. 0 (Z_OK) on success, - * other if not. - * - * Called either after you tell inflate that the input stream is - * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) - * or if an error happened. By default - join collected chunks, - * free memory and fill `results` / `err` properties. - **/ -Inflate.prototype.onEnd = function(status) { - // On success - join - if (status === c.Z_OK) { - if (this.options.to === 'string') { - // Glue & convert here, until we teach pako to send - // utf8 alligned strings to onData - this.result = this.chunks.join(''); - } else { - this.result = utils.flattenChunks(this.chunks); - } - } - this.chunks = []; - this.err = status; - this.msg = this.strm.msg; -}; - - -/** - * inflate(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to decompress. - * - options (Object): zlib inflate options. - * - * Decompress `data` with inflate/ungzip and `options`. Autodetect - * format via wrapper header by default. That's why we don't provide - * separate `ungzip` method. - * - * Supported options are: - * - * - windowBits - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information. - * - * Sugar (options): - * - * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify - * negative windowBits implicitly. - * - `to` (String) - if equal to 'string', then result will be converted - * from utf8 to utf16 (javascript) string. When string output requested, - * chunk length can differ from `chunkSize`, depending on content. - * - * - * ##### Example: - * - * ```javascript - * var pako = require('pako') - * , input = pako.deflate([1,2,3,4,5,6,7,8,9]) - * , output; - * - * try { - * output = pako.inflate(input); - * } catch (err) - * console.log(err); - * } - * ``` - **/ -function inflate(input, options) { - var inflator = new Inflate(options); - - inflator.push(input, true); - - // That will never happens, if you don't cheat with options :) - if (inflator.err) { throw inflator.msg; } - - return inflator.result; -} - - -/** - * inflateRaw(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to decompress. - * - options (Object): zlib inflate options. - * - * The same as [[inflate]], but creates raw data, without wrapper - * (header and adler32 crc). - **/ -function inflateRaw(input, options) { - options = options || {}; - options.raw = true; - return inflate(input, options); -} - - -/** - * ungzip(data[, options]) -> Uint8Array|Array|String - * - data (Uint8Array|Array|String): input data to decompress. - * - options (Object): zlib inflate options. - * - * Just shortcut to [[inflate]], because it autodetects format - * by header.content. Done for convenience. - **/ - - -exports.Inflate = Inflate; -exports.inflate = inflate; -exports.inflateRaw = inflateRaw; -exports.ungzip = inflate; - -},{"./utils/common":4,"./utils/strings":5,"./zlib/constants":7,"./zlib/gzheader":10,"./zlib/inflate.js":12,"./zlib/messages":14,"./zlib/zstream":16}],4:[function(require,module,exports){ -'use strict'; - - -var TYPED_OK = (typeof Uint8Array !== 'undefined') && - (typeof Uint16Array !== 'undefined') && - (typeof Int32Array !== 'undefined'); - - -exports.assign = function (obj /*from1, from2, from3, ...*/) { - var sources = Array.prototype.slice.call(arguments, 1); - while (sources.length) { - var source = sources.shift(); - if (!source) { continue; } - - if (typeof source !== 'object') { - throw new TypeError(source + 'must be non-object'); - } - - for (var p in source) { - if (source.hasOwnProperty(p)) { - obj[p] = source[p]; - } - } - } - - return obj; -}; - - -// reduce buffer size, avoiding mem copy -exports.shrinkBuf = function (buf, size) { - if (buf.length === size) { return buf; } - if (buf.subarray) { return buf.subarray(0, size); } - buf.length = size; - return buf; -}; - - -var fnTyped = { - arraySet: function (dest, src, src_offs, len, dest_offs) { - if (src.subarray && dest.subarray) { - dest.set(src.subarray(src_offs, src_offs+len), dest_offs); - return; - } - // Fallback to ordinary array - for (var i=0; i= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); -} -_utf8len[254]=_utf8len[254]=1; // Invalid sequence start - - -// convert string to array (typed, when possible) -exports.string2buf = function (str) { - var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; - - // count binary size - for (m_pos = 0; m_pos < str_len; m_pos++) { - c = str.charCodeAt(m_pos); - if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { - c2 = str.charCodeAt(m_pos+1); - if ((c2 & 0xfc00) === 0xdc00) { - c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); - m_pos++; - } - } - buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; - } - - // allocate buffer - buf = new utils.Buf8(buf_len); - - // convert - for (i=0, m_pos = 0; i < buf_len; m_pos++) { - c = str.charCodeAt(m_pos); - if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) { - c2 = str.charCodeAt(m_pos+1); - if ((c2 & 0xfc00) === 0xdc00) { - c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); - m_pos++; - } - } - if (c < 0x80) { - /* one byte */ - buf[i++] = c; - } else if (c < 0x800) { - /* two bytes */ - buf[i++] = 0xC0 | (c >>> 6); - buf[i++] = 0x80 | (c & 0x3f); - } else if (c < 0x10000) { - /* three bytes */ - buf[i++] = 0xE0 | (c >>> 12); - buf[i++] = 0x80 | (c >>> 6 & 0x3f); - buf[i++] = 0x80 | (c & 0x3f); - } else { - /* four bytes */ - buf[i++] = 0xf0 | (c >>> 18); - buf[i++] = 0x80 | (c >>> 12 & 0x3f); - buf[i++] = 0x80 | (c >>> 6 & 0x3f); - buf[i++] = 0x80 | (c & 0x3f); - } - } - - return buf; -}; - -// Helper (used in 2 places) -function buf2binstring(buf, len) { - // use fallback for big arrays to avoid stack overflow - if (len < 65537) { - if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) { - return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len)); - } - } - - var result = ''; - for (var i=0; i < len; i++) { - result += String.fromCharCode(buf[i]); - } - return result; -} - - -// Convert byte array to binary string -exports.buf2binstring = function(buf) { - return buf2binstring(buf, buf.length); -}; - - -// Convert binary string (typed, when possible) -exports.binstring2buf = function(str) { - var buf = new utils.Buf8(str.length); - for (var i=0, len=buf.length; i < len; i++) { - buf[i] = str.charCodeAt(i); - } - return buf; -}; - - -// convert array to string -exports.buf2string = function (buf, max) { - var i, out, c, c_len; - var len = max || buf.length; - - // Reserve max possible length (2 words per char) - // NB: by unknown reasons, Array is significantly faster for - // String.fromCharCode.apply than Uint16Array. - var utf16buf = new Array(len*2); - - for (out=0, i=0; i 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; } - - // apply mask on first byte - c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; - // join the rest - while (c_len > 1 && i < len) { - c = (c << 6) | (buf[i++] & 0x3f); - c_len--; - } - - // terminated by end of string? - if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } - - if (c < 0x10000) { - utf16buf[out++] = c; - } else { - c -= 0x10000; - utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); - utf16buf[out++] = 0xdc00 | (c & 0x3ff); - } - } - - return buf2binstring(utf16buf, out); -}; - - -// Calculate max possible position in utf8 buffer, -// that will not break sequence. If that's not possible -// - (very small limits) return max size as is. -// -// buf[] - utf8 bytes array -// max - length limit (mandatory); -exports.utf8border = function(buf, max) { - var pos; - - max = max || buf.length; - if (max > buf.length) { max = buf.length; } - - // go back from last position, until start of sequence found - pos = max-1; - while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } - - // Fuckup - very small and broken sequence, - // return max, because we should return something anyway. - if (pos < 0) { return max; } - - // If we came to start of buffer - that means vuffer is too small, - // return max too. - if (pos === 0) { return max; } - - return (pos + _utf8len[buf[pos]] > max) ? pos : max; -}; - -},{"./common":4}],6:[function(require,module,exports){ -'use strict'; - -// Note: adler32 takes 12% for level 0 and 2% for level 6. -// It doesn't worth to make additional optimizationa as in original. -// Small size is preferable. - -function adler32(adler, buf, len, pos) { - var s1 = (adler & 0xffff) |0, - s2 = ((adler >>> 16) & 0xffff) |0, - n = 0; - - while (len !== 0) { - // Set limit ~ twice less than 5552, to keep - // s2 in 31-bits, because we force signed ints. - // in other case %= will fail. - n = len > 2000 ? 2000 : len; - len -= n; - - do { - s1 = (s1 + buf[pos++]) |0; - s2 = (s2 + s1) |0; - } while (--n); - - s1 %= 65521; - s2 %= 65521; - } - - return (s1 | (s2 << 16)) |0; -} - - -module.exports = adler32; - -},{}],7:[function(require,module,exports){ -module.exports = { - - /* Allowed flush values; see deflate() and inflate() below for details */ - Z_NO_FLUSH: 0, - Z_PARTIAL_FLUSH: 1, - Z_SYNC_FLUSH: 2, - Z_FULL_FLUSH: 3, - Z_FINISH: 4, - Z_BLOCK: 5, - Z_TREES: 6, - - /* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ - Z_OK: 0, - Z_STREAM_END: 1, - Z_NEED_DICT: 2, - Z_ERRNO: -1, - Z_STREAM_ERROR: -2, - Z_DATA_ERROR: -3, - //Z_MEM_ERROR: -4, - Z_BUF_ERROR: -5, - //Z_VERSION_ERROR: -6, - - /* compression levels */ - Z_NO_COMPRESSION: 0, - Z_BEST_SPEED: 1, - Z_BEST_COMPRESSION: 9, - Z_DEFAULT_COMPRESSION: -1, - - - Z_FILTERED: 1, - Z_HUFFMAN_ONLY: 2, - Z_RLE: 3, - Z_FIXED: 4, - Z_DEFAULT_STRATEGY: 0, - - /* Possible values of the data_type field (though see inflate()) */ - Z_BINARY: 0, - Z_TEXT: 1, - //Z_ASCII: 1, // = Z_TEXT (deprecated) - Z_UNKNOWN: 2, - - /* The deflate compression method */ - Z_DEFLATED: 8 - //Z_NULL: null // Use -1 or null inline, depending on var type -}; - -},{}],8:[function(require,module,exports){ -'use strict'; - -// Note: we can't get significant speed boost here. -// So write code to minimize size - no pregenerated tables -// and array tools dependencies. - - -// Use ordinary array, since untyped makes no boost here -function makeTable() { - var c, table = []; - - for (var n =0; n < 256; n++) { - c = n; - for (var k =0; k < 8; k++) { - c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - table[n] = c; - } - - return table; -} - -// Create table on load. Just 255 signed longs. Not a problem. -var crcTable = makeTable(); - - -function crc32(crc, buf, len, pos) { - var t = crcTable, - end = pos + len; - - crc = crc ^ (-1); - - for (var i = pos; i < end; i++) { - crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; - } - - return (crc ^ (-1)); // >>> 0; -} - - -module.exports = crc32; - -},{}],9:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils/common'); -var trees = require('./trees'); -var adler32 = require('./adler32'); -var crc32 = require('./crc32'); -var msg = require('./messages'); - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -/* Allowed flush values; see deflate() and inflate() below for details */ -var Z_NO_FLUSH = 0; -var Z_PARTIAL_FLUSH = 1; -//var Z_SYNC_FLUSH = 2; -var Z_FULL_FLUSH = 3; -var Z_FINISH = 4; -var Z_BLOCK = 5; -//var Z_TREES = 6; - - -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ -var Z_OK = 0; -var Z_STREAM_END = 1; -//var Z_NEED_DICT = 2; -//var Z_ERRNO = -1; -var Z_STREAM_ERROR = -2; -var Z_DATA_ERROR = -3; -//var Z_MEM_ERROR = -4; -var Z_BUF_ERROR = -5; -//var Z_VERSION_ERROR = -6; - - -/* compression levels */ -//var Z_NO_COMPRESSION = 0; -//var Z_BEST_SPEED = 1; -//var Z_BEST_COMPRESSION = 9; -var Z_DEFAULT_COMPRESSION = -1; - - -var Z_FILTERED = 1; -var Z_HUFFMAN_ONLY = 2; -var Z_RLE = 3; -var Z_FIXED = 4; -var Z_DEFAULT_STRATEGY = 0; - -/* Possible values of the data_type field (though see inflate()) */ -//var Z_BINARY = 0; -//var Z_TEXT = 1; -//var Z_ASCII = 1; // = Z_TEXT -var Z_UNKNOWN = 2; - - -/* The deflate compression method */ -var Z_DEFLATED = 8; - -/*============================================================================*/ - - -var MAX_MEM_LEVEL = 9; -/* Maximum value for memLevel in deflateInit2 */ -var MAX_WBITS = 15; -/* 32K LZ77 window */ -var DEF_MEM_LEVEL = 8; - - -var LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ -var LITERALS = 256; -/* number of literal bytes 0..255 */ -var L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ -var D_CODES = 30; -/* number of distance codes */ -var BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ -var HEAP_SIZE = 2*L_CODES + 1; -/* maximum heap size */ -var MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -var MIN_MATCH = 3; -var MAX_MATCH = 258; -var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - -var PRESET_DICT = 0x20; - -var INIT_STATE = 42; -var EXTRA_STATE = 69; -var NAME_STATE = 73; -var COMMENT_STATE = 91; -var HCRC_STATE = 103; -var BUSY_STATE = 113; -var FINISH_STATE = 666; - -var BS_NEED_MORE = 1; /* block not completed, need more input or more output */ -var BS_BLOCK_DONE = 2; /* block flush performed */ -var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ -var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ - -var OS_CODE = 0x03; // Unix :) . Don't detect, use this default. - -function err(strm, errorCode) { - strm.msg = msg[errorCode]; - return errorCode; -} - -function rank(f) { - return ((f) << 1) - ((f) > 4 ? 9 : 0); -} - -function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } - - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->output buffer and copying into it. - * (See also read_buf()). - */ -function flush_pending(strm) { - var s = strm.state; - - //_tr_flush_bits(s); - var len = s.pending; - if (len > strm.avail_out) { - len = strm.avail_out; - } - if (len === 0) { return; } - - utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out); - strm.next_out += len; - s.pending_out += len; - strm.total_out += len; - strm.avail_out -= len; - s.pending -= len; - if (s.pending === 0) { - s.pending_out = 0; - } -} - - -function flush_block_only (s, last) { - trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); - s.block_start = s.strstart; - flush_pending(s.strm); -} - - -function put_byte(s, b) { - s.pending_buf[s.pending++] = b; -} - - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -function putShortMSB(s, b) { -// put_byte(s, (Byte)(b >> 8)); -// put_byte(s, (Byte)(b & 0xff)); - s.pending_buf[s.pending++] = (b >>> 8) & 0xff; - s.pending_buf[s.pending++] = b & 0xff; -} - - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->input buffer and copying from it. - * (See also flush_pending()). - */ -function read_buf(strm, buf, start, size) { - var len = strm.avail_in; - - if (len > size) { len = size; } - if (len === 0) { return 0; } - - strm.avail_in -= len; - - utils.arraySet(buf, strm.input, strm.next_in, len, start); - if (strm.state.wrap === 1) { - strm.adler = adler32(strm.adler, buf, len, start); - } - - else if (strm.state.wrap === 2) { - strm.adler = crc32(strm.adler, buf, len, start); - } - - strm.next_in += len; - strm.total_in += len; - - return len; -} - - -/* =========================================================================== - * Set match_start to the longest match starting at the given string and - * return its length. Matches shorter or equal to prev_length are discarded, - * in which case the result is equal to prev_length and match_start is - * garbage. - * IN assertions: cur_match is the head of the hash chain for the current - * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 - * OUT assertion: the match length is not greater than s->lookahead. - */ -function longest_match(s, cur_match) { - var chain_length = s.max_chain_length; /* max hash chain length */ - var scan = s.strstart; /* current string */ - var match; /* matched string */ - var len; /* length of current match */ - var best_len = s.prev_length; /* best match length so far */ - var nice_match = s.nice_match; /* stop if match long enough */ - var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? - s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; - - var _win = s.window; // shortcut - - var wmask = s.w_mask; - var prev = s.prev; - - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - - var strend = s.strstart + MAX_MATCH; - var scan_end1 = _win[scan + best_len - 1]; - var scan_end = _win[scan + best_len]; - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s.prev_length >= s.good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if (nice_match > s.lookahead) { nice_match = s.lookahead; } - - // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - do { - // Assert(cur_match < s->strstart, "no future"); - match = cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ - - if (_win[match + best_len] !== scan_end || - _win[match + best_len - 1] !== scan_end1 || - _win[match] !== _win[scan] || - _win[++match] !== _win[scan + 1]) { - continue; - } - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2; - match++; - // Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - /*jshint noempty:false*/ - } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - scan < strend); - - // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - s.match_start = cur_match; - best_len = len; - if (len >= nice_match) { - break; - } - scan_end1 = _win[scan + best_len - 1]; - scan_end = _win[scan + best_len]; - } - } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); - - if (best_len <= s.lookahead) { - return best_len; - } - return s.lookahead; -} - - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -function fill_window(s) { - var _w_size = s.w_size; - var p, n, m, more, str; - - //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = s.window_size - s.lookahead - s.strstart; - - // JS ints have 32 bit, block below not needed - /* Deal with !@#$% 64K limit: */ - //if (sizeof(int) <= 2) { - // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - // more = wsize; - // - // } else if (more == (unsigned)(-1)) { - // /* Very unlikely, but possible on 16 bit machine if - // * strstart == 0 && lookahead == 1 (input done a byte at time) - // */ - // more--; - // } - //} - - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { - - utils.arraySet(s.window, s.window, _w_size, _w_size, 0); - s.match_start -= _w_size; - s.strstart -= _w_size; - /* we now have strstart >= MAX_DIST */ - s.block_start -= _w_size; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - - n = s.hash_size; - p = n; - do { - m = s.head[--p]; - s.head[p] = (m >= _w_size ? m - _w_size : 0); - } while (--n); - - n = _w_size; - p = n; - do { - m = s.prev[--p]; - s.prev[p] = (m >= _w_size ? m - _w_size : 0); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); - - more += _w_size; - } - if (s.strm.avail_in === 0) { - break; - } - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - //Assert(more >= 2, "more < 2"); - n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); - s.lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s.lookahead + s.insert >= MIN_MATCH) { - str = s.strstart - s.insert; - s.ins_h = s.window[str]; - - /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; -//#if MIN_MATCH != 3 -// Call update_hash() MIN_MATCH-3 more times -//#endif - while (s.insert) { - /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH-1]) & s.hash_mask; - - s.prev[str & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = str; - str++; - s.insert--; - if (s.lookahead + s.insert < MIN_MATCH) { - break; - } - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ -// if (s.high_water < s.window_size) { -// var curr = s.strstart + s.lookahead; -// var init = 0; -// -// if (s.high_water < curr) { -// /* Previous high water mark below current data -- zero WIN_INIT -// * bytes or up to end of window, whichever is less. -// */ -// init = s.window_size - curr; -// if (init > WIN_INIT) -// init = WIN_INIT; -// zmemzero(s->window + curr, (unsigned)init); -// s->high_water = curr + init; -// } -// else if (s->high_water < (ulg)curr + WIN_INIT) { -// /* High water mark at or above current data, but below current data -// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up -// * to end of window, whichever is less. -// */ -// init = (ulg)curr + WIN_INIT - s->high_water; -// if (init > s->window_size - s->high_water) -// init = s->window_size - s->high_water; -// zmemzero(s->window + s->high_water, (unsigned)init); -// s->high_water += init; -// } -// } -// -// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, -// "not enough room for search"); -} - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. - */ -function deflate_stored(s, flush) { - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - var max_block_size = 0xffff; - - if (max_block_size > s.pending_buf_size - 5) { - max_block_size = s.pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s.lookahead <= 1) { - - //Assert(s->strstart < s->w_size+MAX_DIST(s) || - // s->block_start >= (long)s->w_size, "slide too late"); -// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || -// s.block_start >= s.w_size)) { -// throw new Error("slide too late"); -// } - - fill_window(s); - if (s.lookahead === 0 && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - - if (s.lookahead === 0) { - break; - } - /* flush the current block */ - } - //Assert(s->block_start >= 0L, "block gone"); -// if (s.block_start < 0) throw new Error("block gone"); - - s.strstart += s.lookahead; - s.lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - var max_start = s.block_start + max_block_size; - - if (s.strstart === 0 || s.strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s.lookahead = s.strstart - max_start; - s.strstart = max_start; - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - - - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - - s.insert = 0; - - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - - if (s.strstart > s.block_start) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_NEED_MORE; -} - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -function deflate_fast(s, flush) { - var hash_head; /* head of the hash chain */ - var bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; /* flush the current block */ - } - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < MIN_MATCH - */ - if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - } - if (s.match_length >= MIN_MATCH) { - // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only - - /*** _tr_tally_dist(s, s.strstart - s.match_start, - s.match_length - MIN_MATCH, bflush); ***/ - bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ - if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { - s.match_length--; /* string at strstart already in table */ - do { - s.strstart++; - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. - */ - } while (--s.match_length !== 0); - s.strstart++; - } else - { - s.strstart += s.match_length; - s.match_length = 0; - s.ins_h = s.window[s.strstart]; - /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; - -//#if MIN_MATCH != 3 -// Call UPDATE_HASH() MIN_MATCH-3 more times -//#endif - /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s.window[s.strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = ((s.strstart < (MIN_MATCH-1)) ? s.strstart : MIN_MATCH-1); - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -function deflate_slow(s, flush) { - var hash_head; /* head of hash chain */ - var bflush; /* set if current block must be flushed */ - - var max_insert; - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - */ - s.prev_length = s.match_length; - s.prev_match = s.match_start; - s.match_length = MIN_MATCH-1; - - if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && - s.strstart - hash_head <= (s.w_size-MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - - if (s.match_length <= 5 && - (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { - - /* If prev_match is also MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s.match_length = MIN_MATCH-1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { - max_insert = s.strstart + s.lookahead - MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - //check_match(s, s.strstart-1, s.prev_match, s.prev_length); - - /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, - s.prev_length - MIN_MATCH, bflush);***/ - bflush = trees._tr_tally(s, s.strstart - 1- s.prev_match, s.prev_length - MIN_MATCH); - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s.lookahead -= s.prev_length-1; - s.prev_length -= 2; - do { - if (++s.strstart <= max_insert) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - } while (--s.prev_length !== 0); - s.match_available = 0; - s.match_length = MIN_MATCH-1; - s.strstart++; - - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - } else if (s.match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart-1]); - - if (bflush) { - /*** FLUSH_BLOCK_ONLY(s, 0) ***/ - flush_block_only(s, false); - /***/ - } - s.strstart++; - s.lookahead--; - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s.match_available = 1; - s.strstart++; - s.lookahead--; - } - } - //Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s.match_available) { - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart-1]); - - s.match_available = 0; - } - s.insert = s.strstart < MIN_MATCH-1 ? s.strstart : MIN_MATCH-1; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_BLOCK_DONE; -} - - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -function deflate_rle(s, flush) { - var bflush; /* set if current block must be flushed */ - var prev; /* byte at distance one to match */ - var scan, strend; /* scan goes up to strend for length of run */ - - var _win = s.window; - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s.lookahead <= MAX_MATCH) { - fill_window(s); - if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s.match_length = 0; - if (s.lookahead >= MIN_MATCH && s.strstart > 0) { - scan = s.strstart - 1; - prev = _win[scan]; - if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { - strend = s.strstart + MAX_MATCH; - do { - /*jshint noempty:false*/ - } while (prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - scan < strend); - s.match_length = MAX_MATCH - (strend - scan); - if (s.match_length > s.lookahead) { - s.match_length = s.lookahead; - } - } - //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); - } - - /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s.match_length >= MIN_MATCH) { - //check_match(s, s.strstart, s.strstart - 1, s.match_length); - - /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ - bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - s.strstart += s.match_length; - s.match_length = 0; - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -function deflate_huff(s, flush) { - var bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s.lookahead === 0) { - fill_window(s); - if (s.lookahead === 0) { - if (flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s.match_length = 0; - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -var Config = function (good_length, max_lazy, nice_length, max_chain, func) { - this.good_length = good_length; - this.max_lazy = max_lazy; - this.nice_length = nice_length; - this.max_chain = max_chain; - this.func = func; -}; - -var configuration_table; - -configuration_table = [ - /* good lazy nice chain */ - new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ - new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ - new Config(4, 5, 16, 8, deflate_fast), /* 2 */ - new Config(4, 6, 32, 32, deflate_fast), /* 3 */ - - new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ - new Config(8, 16, 32, 32, deflate_slow), /* 5 */ - new Config(8, 16, 128, 128, deflate_slow), /* 6 */ - new Config(8, 32, 128, 256, deflate_slow), /* 7 */ - new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ - new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ -]; - - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -function lm_init(s) { - s.window_size = 2 * s.w_size; - - /*** CLEAR_HASH(s); ***/ - zero(s.head); // Fill with NIL (= 0); - - /* Set the default configuration parameters: - */ - s.max_lazy_match = configuration_table[s.level].max_lazy; - s.good_match = configuration_table[s.level].good_length; - s.nice_match = configuration_table[s.level].nice_length; - s.max_chain_length = configuration_table[s.level].max_chain; - - s.strstart = 0; - s.block_start = 0; - s.lookahead = 0; - s.insert = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - s.ins_h = 0; -} - - -function DeflateState() { - this.strm = null; /* pointer back to this zlib stream */ - this.status = 0; /* as the name implies */ - this.pending_buf = null; /* output still pending */ - this.pending_buf_size = 0; /* size of pending_buf */ - this.pending_out = 0; /* next pending byte to output to the stream */ - this.pending = 0; /* nb of bytes in the pending buffer */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.gzhead = null; /* gzip header information to write */ - this.gzindex = 0; /* where in extra, name, or comment */ - this.method = Z_DEFLATED; /* can only be DEFLATED */ - this.last_flush = -1; /* value of flush param for previous deflate call */ - - this.w_size = 0; /* LZ77 window size (32K by default) */ - this.w_bits = 0; /* log2(w_size) (8..16) */ - this.w_mask = 0; /* w_size - 1 */ - - this.window = null; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. - */ - - this.window_size = 0; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - this.prev = null; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - this.head = null; /* Heads of the hash chains or NIL. */ - - this.ins_h = 0; /* hash index of string to be inserted */ - this.hash_size = 0; /* number of elements in hash table */ - this.hash_bits = 0; /* log2(hash_size) */ - this.hash_mask = 0; /* hash_size-1 */ - - this.hash_shift = 0; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - this.block_start = 0; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - this.match_length = 0; /* length of best match */ - this.prev_match = 0; /* previous match */ - this.match_available = 0; /* set if previous match exists */ - this.strstart = 0; /* start of string to insert */ - this.match_start = 0; /* start of matching string */ - this.lookahead = 0; /* number of valid bytes ahead in window */ - - this.prev_length = 0; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - this.max_chain_length = 0; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - this.max_lazy_match = 0; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ - // That's alias to max_lazy_match, don't use directly - //this.max_insert_length = 0; - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - this.level = 0; /* compression level (1..9) */ - this.strategy = 0; /* favor or force Huffman coding*/ - - this.good_match = 0; - /* Use a faster search when the previous match is longer than this */ - - this.nice_match = 0; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - - /* Didn't use ct_data typedef below to suppress compiler warning */ - - // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - // Use flat array of DOUBLE size, with interleaved fata, - // because JS does not support effective - this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2); - this.dyn_dtree = new utils.Buf16((2*D_CODES+1) * 2); - this.bl_tree = new utils.Buf16((2*BL_CODES+1) * 2); - zero(this.dyn_ltree); - zero(this.dyn_dtree); - zero(this.bl_tree); - - this.l_desc = null; /* desc. for literal tree */ - this.d_desc = null; /* desc. for distance tree */ - this.bl_desc = null; /* desc. for bit length tree */ - - //ush bl_count[MAX_BITS+1]; - this.bl_count = new utils.Buf16(MAX_BITS+1); - /* number of codes at each bit length for an optimal tree */ - - //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - this.heap = new utils.Buf16(2*L_CODES+1); /* heap used to build the Huffman trees */ - zero(this.heap); - - this.heap_len = 0; /* number of elements in the heap */ - this.heap_max = 0; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - this.depth = new utils.Buf16(2*L_CODES+1); //uch depth[2*L_CODES+1]; - zero(this.depth); - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - - this.l_buf = 0; /* buffer index for literals or lengths */ - - this.lit_bufsize = 0; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - this.last_lit = 0; /* running index in l_buf */ - - this.d_buf = 0; - /* Buffer index for distances. To simplify the code, d_buf and l_buf have - * the same number of elements. To use different lengths, an extra flag - * array would be necessary. - */ - - this.opt_len = 0; /* bit length of current block with optimal trees */ - this.static_len = 0; /* bit length of current block with static trees */ - this.matches = 0; /* number of string matches in current block */ - this.insert = 0; /* bytes at end of window left to insert */ - - - this.bi_buf = 0; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - this.bi_valid = 0; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - // Used for window memory init. We safely ignore it for JS. That makes - // sense only for pointers and memory check tools. - //this.high_water = 0; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ -} - - -function deflateResetKeep(strm) { - var s; - - if (!strm || !strm.state) { - return err(strm, Z_STREAM_ERROR); - } - - strm.total_in = strm.total_out = 0; - strm.data_type = Z_UNKNOWN; - - s = strm.state; - s.pending = 0; - s.pending_out = 0; - - if (s.wrap < 0) { - s.wrap = -s.wrap; - /* was made negative by deflate(..., Z_FINISH); */ - } - s.status = (s.wrap ? INIT_STATE : BUSY_STATE); - strm.adler = (s.wrap === 2) ? - 0 // crc32(0, Z_NULL, 0) - : - 1; // adler32(0, Z_NULL, 0) - s.last_flush = Z_NO_FLUSH; - trees._tr_init(s); - return Z_OK; -} - - -function deflateReset(strm) { - var ret = deflateResetKeep(strm); - if (ret === Z_OK) { - lm_init(strm.state); - } - return ret; -} - - -function deflateSetHeader(strm, head) { - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } - strm.state.gzhead = head; - return Z_OK; -} - - -function deflateInit2(strm, level, method, windowBits, memLevel, strategy) { - if (!strm) { // === Z_NULL - return Z_STREAM_ERROR; - } - var wrap = 1; - - if (level === Z_DEFAULT_COMPRESSION) { - level = 6; - } - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - windowBits = -windowBits; - } - - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } - - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { - return err(strm, Z_STREAM_ERROR); - } - - - if (windowBits === 8) { - windowBits = 9; - } - /* until 256-byte window bug fixed */ - - var s = new DeflateState(); - - strm.state = s; - s.strm = strm; - - s.wrap = wrap; - s.gzhead = null; - s.w_bits = windowBits; - s.w_size = 1 << s.w_bits; - s.w_mask = s.w_size - 1; - - s.hash_bits = memLevel + 7; - s.hash_size = 1 << s.hash_bits; - s.hash_mask = s.hash_size - 1; - s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - s.window = new utils.Buf8(s.w_size * 2); - s.head = new utils.Buf16(s.hash_size); - s.prev = new utils.Buf16(s.w_size); - - // Don't need mem init magic for JS. - //s.high_water = 0; /* nothing written to s->window yet */ - - s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - s.pending_buf_size = s.lit_bufsize * 4; - s.pending_buf = new utils.Buf8(s.pending_buf_size); - - s.d_buf = s.lit_bufsize >> 1; - s.l_buf = (1 + 2) * s.lit_bufsize; - - s.level = level; - s.strategy = strategy; - s.method = method; - - return deflateReset(strm); -} - -function deflateInit(strm, level) { - return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); -} - - -function deflate(strm, flush) { - var old_flush, s; - var beg, val; // for gzip header write only - - if (!strm || !strm.state || - flush > Z_BLOCK || flush < 0) { - return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; - } - - s = strm.state; - - if (!strm.output || - (!strm.input && strm.avail_in !== 0) || - (s.status === FINISH_STATE && flush !== Z_FINISH)) { - return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); - } - - s.strm = strm; /* just in case */ - old_flush = s.last_flush; - s.last_flush = flush; - - /* Write the header */ - if (s.status === INIT_STATE) { - - if (s.wrap === 2) { // GZIP header - strm.adler = 0; //crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (!s.gzhead) { // s->gzhead == Z_NULL - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s.status = BUSY_STATE; - } - else { - put_byte(s, (s.gzhead.text ? 1 : 0) + - (s.gzhead.hcrc ? 2 : 0) + - (!s.gzhead.extra ? 0 : 4) + - (!s.gzhead.name ? 0 : 8) + - (!s.gzhead.comment ? 0 : 16) - ); - put_byte(s, s.gzhead.time & 0xff); - put_byte(s, (s.gzhead.time >> 8) & 0xff); - put_byte(s, (s.gzhead.time >> 16) & 0xff); - put_byte(s, (s.gzhead.time >> 24) & 0xff); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, s.gzhead.os & 0xff); - if (s.gzhead.extra && s.gzhead.extra.length) { - put_byte(s, s.gzhead.extra.length & 0xff); - put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); - } - if (s.gzhead.hcrc) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); - } - s.gzindex = 0; - s.status = EXTRA_STATE; - } - } - else // DEFLATE header - { - var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; - var level_flags = -1; - - if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { - level_flags = 0; - } else if (s.level < 6) { - level_flags = 1; - } else if (s.level === 6) { - level_flags = 2; - } else { - level_flags = 3; - } - header |= (level_flags << 6); - if (s.strstart !== 0) { header |= PRESET_DICT; } - header += 31 - (header % 31); - - s.status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s.strstart !== 0) { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - strm.adler = 1; // adler32(0L, Z_NULL, 0); - } - } - -//#ifdef GZIP - if (s.status === EXTRA_STATE) { - if (s.gzhead.extra/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - - while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - break; - } - } - put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); - s.gzindex++; - } - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (s.gzindex === s.gzhead.extra.length) { - s.gzindex = 0; - s.status = NAME_STATE; - } - } - else { - s.status = NAME_STATE; - } - } - if (s.status === NAME_STATE) { - if (s.gzhead.name/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.name.length) { - val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.gzindex = 0; - s.status = COMMENT_STATE; - } - } - else { - s.status = COMMENT_STATE; - } - } - if (s.status === COMMENT_STATE) { - if (s.gzhead.comment/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.comment.length) { - val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.status = HCRC_STATE; - } - } - else { - s.status = HCRC_STATE; - } - } - if (s.status === HCRC_STATE) { - if (s.gzhead.hcrc) { - if (s.pending + 2 > s.pending_buf_size) { - flush_pending(strm); - } - if (s.pending + 2 <= s.pending_buf_size) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - strm.adler = 0; //crc32(0L, Z_NULL, 0); - s.status = BUSY_STATE; - } - } - else { - s.status = BUSY_STATE; - } - } -//#endif - - /* Flush as much pending output as possible */ - if (s.pending !== 0) { - flush_pending(strm); - if (strm.avail_out === 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s.last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && - flush !== Z_FINISH) { - return err(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s.status === FINISH_STATE && strm.avail_in !== 0) { - return err(strm, Z_BUF_ERROR); - } - - /* Start a new block or continue the current one. - */ - if (strm.avail_in !== 0 || s.lookahead !== 0 || - (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { - var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : - (s.strategy === Z_RLE ? deflate_rle(s, flush) : - configuration_table[s.level].func(s, flush)); - - if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { - s.status = FINISH_STATE; - } - if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { - if (strm.avail_out === 0) { - s.last_flush = -1; - /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate === BS_BLOCK_DONE) { - if (flush === Z_PARTIAL_FLUSH) { - trees._tr_align(s); - } - else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - - trees._tr_stored_block(s, 0, 0, false); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush === Z_FULL_FLUSH) { - /*** CLEAR_HASH(s); ***/ /* forget history */ - zero(s.head); // Fill with NIL (= 0); - - if (s.lookahead === 0) { - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - } - } - flush_pending(strm); - if (strm.avail_out === 0) { - s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - //Assert(strm->avail_out > 0, "bug2"); - //if (strm.avail_out <= 0) { throw new Error("bug2");} - - if (flush !== Z_FINISH) { return Z_OK; } - if (s.wrap <= 0) { return Z_STREAM_END; } - - /* Write the trailer */ - if (s.wrap === 2) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - put_byte(s, (strm.adler >> 16) & 0xff); - put_byte(s, (strm.adler >> 24) & 0xff); - put_byte(s, strm.total_in & 0xff); - put_byte(s, (strm.total_in >> 8) & 0xff); - put_byte(s, (strm.total_in >> 16) & 0xff); - put_byte(s, (strm.total_in >> 24) & 0xff); - } - else - { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s.wrap > 0) { s.wrap = -s.wrap; } - /* write the trailer only once! */ - return s.pending !== 0 ? Z_OK : Z_STREAM_END; -} - -function deflateEnd(strm) { - var status; - - if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; - } - - status = strm.state.status; - if (status !== INIT_STATE && - status !== EXTRA_STATE && - status !== NAME_STATE && - status !== COMMENT_STATE && - status !== HCRC_STATE && - status !== BUSY_STATE && - status !== FINISH_STATE - ) { - return err(strm, Z_STREAM_ERROR); - } - - strm.state = null; - - return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; -} - -/* ========================================================================= - * Copy the source state to the destination state - */ -//function deflateCopy(dest, source) { -// -//} - -exports.deflateInit = deflateInit; -exports.deflateInit2 = deflateInit2; -exports.deflateReset = deflateReset; -exports.deflateResetKeep = deflateResetKeep; -exports.deflateSetHeader = deflateSetHeader; -exports.deflate = deflate; -exports.deflateEnd = deflateEnd; -exports.deflateInfo = 'pako deflate (from Nodeca project)'; - -/* Not implemented -exports.deflateBound = deflateBound; -exports.deflateCopy = deflateCopy; -exports.deflateSetDictionary = deflateSetDictionary; -exports.deflateParams = deflateParams; -exports.deflatePending = deflatePending; -exports.deflatePrime = deflatePrime; -exports.deflateTune = deflateTune; -*/ - -},{"../utils/common":4,"./adler32":6,"./crc32":8,"./messages":14,"./trees":15}],10:[function(require,module,exports){ -'use strict'; - - -function GZheader() { - /* true if compressed data believed to be text */ - this.text = 0; - /* modification time */ - this.time = 0; - /* extra flags (not used when writing a gzip file) */ - this.xflags = 0; - /* operating system */ - this.os = 0; - /* pointer to extra field or Z_NULL if none */ - this.extra = null; - /* extra field length (valid if extra != Z_NULL) */ - this.extra_len = 0; // Actually, we don't need it in JS, - // but leave for few code modifications - - // - // Setup limits is not necessary because in js we should not preallocate memory - // for inflate use constant limit in 65536 bytes - // - - /* space at extra (only when reading header) */ - // this.extra_max = 0; - /* pointer to zero-terminated file name or Z_NULL */ - this.name = ''; - /* space at name (only when reading header) */ - // this.name_max = 0; - /* pointer to zero-terminated comment or Z_NULL */ - this.comment = ''; - /* space at comment (only when reading header) */ - // this.comm_max = 0; - /* true if there was or will be a header crc */ - this.hcrc = 0; - /* true when done reading gzip header (not used when writing a gzip file) */ - this.done = false; -} - -module.exports = GZheader; - -},{}],11:[function(require,module,exports){ -'use strict'; - -// See state defs from inflate.js -var BAD = 30; /* got a data error -- remain here until reset */ -var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state.mode === LEN - strm.avail_in >= 6 - strm.avail_out >= 258 - start >= strm.avail_out - state.bits < 8 - - On return, state.mode is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if strm.avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires strm.avail_out >= 258 for each loop to avoid checking for - output space. - */ -module.exports = function inflate_fast(strm, start) { - var state; - var _in; /* local strm.input */ - var last; /* have enough input while in < last */ - var _out; /* local strm.output */ - var beg; /* inflate()'s initial strm.output */ - var end; /* while out < end, enough space available */ -//#ifdef INFLATE_STRICT - var dmax; /* maximum distance from zlib header */ -//#endif - var wsize; /* window size or zero if not using window */ - var whave; /* valid bytes in the window */ - var wnext; /* window write index */ - // Use `s_window` instead `window`, avoid conflict with instrumentation tools - var s_window; /* allocated sliding window, if wsize != 0 */ - var hold; /* local strm.hold */ - var bits; /* local strm.bits */ - var lcode; /* local strm.lencode */ - var dcode; /* local strm.distcode */ - var lmask; /* mask for first level of length codes */ - var dmask; /* mask for first level of distance codes */ - var here; /* retrieved table entry */ - var op; /* code bits, operation, extra bits, or */ - /* window position, window bytes to copy */ - var len; /* match length, unused bytes */ - var dist; /* match distance */ - var from; /* where to copy match from */ - var from_source; - - - var input, output; // JS specific, because we have no pointers - - /* copy state to local variables */ - state = strm.state; - //here = state.here; - _in = strm.next_in; - input = strm.input; - last = _in + (strm.avail_in - 5); - _out = strm.next_out; - output = strm.output; - beg = _out - (start - strm.avail_out); - end = _out + (strm.avail_out - 257); -//#ifdef INFLATE_STRICT - dmax = state.dmax; -//#endif - wsize = state.wsize; - whave = state.whave; - wnext = state.wnext; - s_window = state.window; - hold = state.hold; - bits = state.bits; - lcode = state.lencode; - dcode = state.distcode; - lmask = (1 << state.lenbits) - 1; - dmask = (1 << state.distbits) - 1; - - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - - top: - do { - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - - here = lcode[hold & lmask]; - - dolen: - for (;;) { // Goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - if (op === 0) { /* literal */ - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - output[_out++] = here & 0xffff/*here.val*/; - } - else if (op & 16) { /* length base */ - len = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (op) { - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - len += hold & ((1 << op) - 1); - hold >>>= op; - bits -= op; - } - //Tracevv((stderr, "inflate: length %u\n", len)); - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - here = dcode[hold & dmask]; - - dodist: - for (;;) { // goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - - if (op & 16) { /* distance base */ - dist = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - } - dist += hold & ((1 << op) - 1); -//#ifdef INFLATE_STRICT - if (dist > dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } -//#endif - hold >>>= op; - bits -= op; - //Tracevv((stderr, "inflate: distance %u\n", dist)); - op = _out - beg; /* max distance in output */ - if (dist > op) { /* see if copy from window */ - op = dist - op; /* distance back in window */ - if (op > whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } - -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// if (len <= op - whave) { -// do { -// output[_out++] = 0; -// } while (--len); -// continue top; -// } -// len -= op - whave; -// do { -// output[_out++] = 0; -// } while (--op > whave); -// if (op === 0) { -// from = _out - dist; -// do { -// output[_out++] = output[from++]; -// } while (--len); -// continue top; -// } -//#endif - } - from = 0; // window index - from_source = s_window; - if (wnext === 0) { /* very common case */ - from += wsize - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - else if (wnext < op) { /* wrap around window */ - from += wsize + wnext - op; - op -= wnext; - if (op < len) { /* some from end of window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = 0; - if (wnext < len) { /* some from start of window */ - op = wnext; - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - } - else { /* contiguous in window */ - from += wnext - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - while (len > 2) { - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - len -= 3; - } - if (len) { - output[_out++] = from_source[from++]; - if (len > 1) { - output[_out++] = from_source[from++]; - } - } - } - else { - from = _out - dist; /* copy direct from output */ - do { /* minimum length is three */ - output[_out++] = output[from++]; - output[_out++] = output[from++]; - output[_out++] = output[from++]; - len -= 3; - } while (len > 2); - if (len) { - output[_out++] = output[from++]; - if (len > 1) { - output[_out++] = output[from++]; - } - } - } - } - else if ((op & 64) === 0) { /* 2nd level distance code */ - here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dodist; - } - else { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } - else if ((op & 64) === 0) { /* 2nd level length code */ - here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dolen; - } - else if (op & 32) { /* end-of-block */ - //Tracevv((stderr, "inflate: end of block\n")); - state.mode = TYPE; - break top; - } - else { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } while (_in < last && _out < end); - - /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ - len = bits >> 3; - _in -= len; - bits -= len << 3; - hold &= (1 << bits) - 1; - - /* update state and return */ - strm.next_in = _in; - strm.next_out = _out; - strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); - strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); - state.hold = hold; - state.bits = bits; - return; -}; - -},{}],12:[function(require,module,exports){ -'use strict'; - - -var utils = require('../utils/common'); -var adler32 = require('./adler32'); -var crc32 = require('./crc32'); -var inflate_fast = require('./inffast'); -var inflate_table = require('./inftrees'); - -var CODES = 0; -var LENS = 1; -var DISTS = 2; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -/* Allowed flush values; see deflate() and inflate() below for details */ -//var Z_NO_FLUSH = 0; -//var Z_PARTIAL_FLUSH = 1; -//var Z_SYNC_FLUSH = 2; -//var Z_FULL_FLUSH = 3; -var Z_FINISH = 4; -var Z_BLOCK = 5; -var Z_TREES = 6; - - -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ -var Z_OK = 0; -var Z_STREAM_END = 1; -var Z_NEED_DICT = 2; -//var Z_ERRNO = -1; -var Z_STREAM_ERROR = -2; -var Z_DATA_ERROR = -3; -var Z_MEM_ERROR = -4; -var Z_BUF_ERROR = -5; -//var Z_VERSION_ERROR = -6; - -/* The deflate compression method */ -var Z_DEFLATED = 8; - - -/* STATES ====================================================================*/ -/* ===========================================================================*/ - - -var HEAD = 1; /* i: waiting for magic header */ -var FLAGS = 2; /* i: waiting for method and flags (gzip) */ -var TIME = 3; /* i: waiting for modification time (gzip) */ -var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ -var EXLEN = 5; /* i: waiting for extra length (gzip) */ -var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ -var NAME = 7; /* i: waiting for end of file name (gzip) */ -var COMMENT = 8; /* i: waiting for end of comment (gzip) */ -var HCRC = 9; /* i: waiting for header crc (gzip) */ -var DICTID = 10; /* i: waiting for dictionary check value */ -var DICT = 11; /* waiting for inflateSetDictionary() call */ -var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ -var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ -var STORED = 14; /* i: waiting for stored size (length and complement) */ -var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ -var COPY = 16; /* i/o: waiting for input or output to copy stored block */ -var TABLE = 17; /* i: waiting for dynamic block table lengths */ -var LENLENS = 18; /* i: waiting for code length code lengths */ -var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ -var LEN_ = 20; /* i: same as LEN below, but only first time in */ -var LEN = 21; /* i: waiting for length/lit/eob code */ -var LENEXT = 22; /* i: waiting for length extra bits */ -var DIST = 23; /* i: waiting for distance code */ -var DISTEXT = 24; /* i: waiting for distance extra bits */ -var MATCH = 25; /* o: waiting for output space to copy string */ -var LIT = 26; /* o: waiting for output space to write literal */ -var CHECK = 27; /* i: waiting for 32-bit check value */ -var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ -var DONE = 29; /* finished check, done -- remain here until reset */ -var BAD = 30; /* got a data error -- remain here until reset */ -var MEM = 31; /* got an inflate() memory error -- remain here until reset */ -var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ - -/* ===========================================================================*/ - - - -var ENOUGH_LENS = 852; -var ENOUGH_DISTS = 592; -//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -var MAX_WBITS = 15; -/* 32K LZ77 window */ -var DEF_WBITS = MAX_WBITS; - - -function ZSWAP32(q) { - return (((q >>> 24) & 0xff) + - ((q >>> 8) & 0xff00) + - ((q & 0xff00) << 8) + - ((q & 0xff) << 24)); -} - - -function InflateState() { - this.mode = 0; /* current inflate mode */ - this.last = false; /* true if processing last block */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.havedict = false; /* true if dictionary provided */ - this.flags = 0; /* gzip header method and flags (0 if zlib) */ - this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ - this.check = 0; /* protected copy of check value */ - this.total = 0; /* protected copy of output count */ - // TODO: may be {} - this.head = null; /* where to save gzip header information */ - - /* sliding window */ - this.wbits = 0; /* log base 2 of requested window size */ - this.wsize = 0; /* window size or zero if not using window */ - this.whave = 0; /* valid bytes in the window */ - this.wnext = 0; /* window write index */ - this.window = null; /* allocated sliding window, if needed */ - - /* bit accumulator */ - this.hold = 0; /* input bit accumulator */ - this.bits = 0; /* number of bits in "in" */ - - /* for string and stored block copying */ - this.length = 0; /* literal or length of data to copy */ - this.offset = 0; /* distance back to copy string from */ - - /* for table and code decoding */ - this.extra = 0; /* extra bits needed */ - - /* fixed and dynamic code tables */ - this.lencode = null; /* starting table for length/literal codes */ - this.distcode = null; /* starting table for distance codes */ - this.lenbits = 0; /* index bits for lencode */ - this.distbits = 0; /* index bits for distcode */ - - /* dynamic table building */ - this.ncode = 0; /* number of code length code lengths */ - this.nlen = 0; /* number of length code lengths */ - this.ndist = 0; /* number of distance code lengths */ - this.have = 0; /* number of code lengths in lens[] */ - this.next = null; /* next available space in codes[] */ - - this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ - this.work = new utils.Buf16(288); /* work area for code table building */ - - /* - because we don't have pointers in js, we use lencode and distcode directly - as buffers so we don't need codes - */ - //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ - this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ - this.distdyn = null; /* dynamic table for distance codes (JS specific) */ - this.sane = 0; /* if false, allow invalid distance too far */ - this.back = 0; /* bits back of last unprocessed length/lit */ - this.was = 0; /* initial length of match */ -} - -function inflateResetKeep(strm) { - var state; - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - strm.total_in = strm.total_out = state.total = 0; - strm.msg = ''; /*Z_NULL*/ - if (state.wrap) { /* to support ill-conceived Java test suite */ - strm.adler = state.wrap & 1; - } - state.mode = HEAD; - state.last = 0; - state.havedict = 0; - state.dmax = 32768; - state.head = null/*Z_NULL*/; - state.hold = 0; - state.bits = 0; - //state.lencode = state.distcode = state.next = state.codes; - state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); - state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); - - state.sane = 1; - state.back = -1; - //Tracev((stderr, "inflate: reset\n")); - return Z_OK; -} - -function inflateReset(strm) { - var state; - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - state.wsize = 0; - state.whave = 0; - state.wnext = 0; - return inflateResetKeep(strm); - -} - -function inflateReset2(strm, windowBits) { - var wrap; - var state; - - /* get the state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - - /* extract wrap request from windowBits parameter */ - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } - else { - wrap = (windowBits >> 4) + 1; - if (windowBits < 48) { - windowBits &= 15; - } - } - - /* set number of window bits, free window if different */ - if (windowBits && (windowBits < 8 || windowBits > 15)) { - return Z_STREAM_ERROR; - } - if (state.window !== null && state.wbits !== windowBits) { - state.window = null; - } - - /* update state and reset the rest of it */ - state.wrap = wrap; - state.wbits = windowBits; - return inflateReset(strm); -} - -function inflateInit2(strm, windowBits) { - var ret; - var state; - - if (!strm) { return Z_STREAM_ERROR; } - //strm.msg = Z_NULL; /* in case we return an error */ - - state = new InflateState(); - - //if (state === Z_NULL) return Z_MEM_ERROR; - //Tracev((stderr, "inflate: allocated\n")); - strm.state = state; - state.window = null/*Z_NULL*/; - ret = inflateReset2(strm, windowBits); - if (ret !== Z_OK) { - strm.state = null/*Z_NULL*/; - } - return ret; -} - -function inflateInit(strm) { - return inflateInit2(strm, DEF_WBITS); -} - - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -var virgin = true; - -var lenfix, distfix; // We have no pointers in JS, so keep tables separate - -function fixedtables(state) { - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - var sym; - - lenfix = new utils.Buf32(512); - distfix = new utils.Buf32(32); - - /* literal/length table */ - sym = 0; - while (sym < 144) { state.lens[sym++] = 8; } - while (sym < 256) { state.lens[sym++] = 9; } - while (sym < 280) { state.lens[sym++] = 7; } - while (sym < 288) { state.lens[sym++] = 8; } - - inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, {bits: 9}); - - /* distance table */ - sym = 0; - while (sym < 32) { state.lens[sym++] = 5; } - - inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, {bits: 5}); - - /* do this just once */ - virgin = false; - } - - state.lencode = lenfix; - state.lenbits = 9; - state.distcode = distfix; - state.distbits = 5; -} - - -/* - Update the window with the last wsize (normally 32K) bytes written before - returning. If window does not exist yet, create it. This is only called - when a window is already in use, or when output has been written during this - inflate call, but the end of the deflate stream has not been reached yet. - It is also called to create a window for dictionary data when a dictionary - is loaded. - - Providing output buffers larger than 32K to inflate() should provide a speed - advantage, since only the last 32K of output is copied to the sliding window - upon return from inflate(), and since all distances after the first 32K of - output will fall in the output data, making match copies simpler and faster. - The advantage may be dependent on the size of the processor's data caches. - */ -function updatewindow(strm, src, end, copy) { - var dist; - var state = strm.state; - - /* if it hasn't been done already, allocate space for the window */ - if (state.window === null) { - state.wsize = 1 << state.wbits; - state.wnext = 0; - state.whave = 0; - - state.window = new utils.Buf8(state.wsize); - } - - /* copy state->wsize or less output bytes into the circular window */ - if (copy >= state.wsize) { - utils.arraySet(state.window,src, end - state.wsize, state.wsize, 0); - state.wnext = 0; - state.whave = state.wsize; - } - else { - dist = state.wsize - state.wnext; - if (dist > copy) { - dist = copy; - } - //zmemcpy(state->window + state->wnext, end - copy, dist); - utils.arraySet(state.window,src, end - copy, dist, state.wnext); - copy -= dist; - if (copy) { - //zmemcpy(state->window, end - copy, copy); - utils.arraySet(state.window,src, end - copy, copy, 0); - state.wnext = copy; - state.whave = state.wsize; - } - else { - state.wnext += dist; - if (state.wnext === state.wsize) { state.wnext = 0; } - if (state.whave < state.wsize) { state.whave += dist; } - } - } - return 0; -} - -function inflate(strm, flush) { - var state; - var input, output; // input/output buffers - var next; /* next input INDEX */ - var put; /* next output INDEX */ - var have, left; /* available input and output */ - var hold; /* bit buffer */ - var bits; /* bits in bit buffer */ - var _in, _out; /* save starting available input and output */ - var copy; /* number of stored or match bytes to copy */ - var from; /* where to copy match bytes from */ - var from_source; - var here = 0; /* current decoding table entry */ - var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) - //var last; /* parent table entry */ - var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) - var len; /* length to copy for repeats, bits to drop */ - var ret; /* return code */ - var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ - var opts; - - var n; // temporary var for NEED_BITS - - var order = /* permutation of code lengths */ - [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - - - if (!strm || !strm.state || !strm.output || - (!strm.input && strm.avail_in !== 0)) { - return Z_STREAM_ERROR; - } - - state = strm.state; - if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ - - - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - _in = have; - _out = left; - ret = Z_OK; - - inf_leave: // goto emulation - for (;;) { - switch (state.mode) { - case HEAD: - if (state.wrap === 0) { - state.mode = TYPEDO; - break; - } - //=== NEEDBITS(16); - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ - state.check = 0/*crc32(0L, Z_NULL, 0)*/; - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = FLAGS; - break; - } - state.flags = 0; /* expect zlib header */ - if (state.head) { - state.head.done = false; - } - if (!(state.wrap & 1) || /* check if zlib header allowed */ - (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { - strm.msg = 'incorrect header check'; - state.mode = BAD; - break; - } - if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// - len = (hold & 0x0f)/*BITS(4)*/ + 8; - if (state.wbits === 0) { - state.wbits = len; - } - else if (len > state.wbits) { - strm.msg = 'invalid window size'; - state.mode = BAD; - break; - } - state.dmax = 1 << len; - //Tracev((stderr, "inflate: zlib header ok\n")); - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = hold & 0x200 ? DICTID : TYPE; - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - break; - case FLAGS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.flags = hold; - if ((state.flags & 0xff) !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - if (state.flags & 0xe000) { - strm.msg = 'unknown header flags set'; - state.mode = BAD; - break; - } - if (state.head) { - state.head.text = ((hold >> 8) & 1); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = TIME; - /* falls through */ - case TIME: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.time = hold; - } - if (state.flags & 0x0200) { - //=== CRC4(state.check, hold) - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - hbuf[2] = (hold >>> 16) & 0xff; - hbuf[3] = (hold >>> 24) & 0xff; - state.check = crc32(state.check, hbuf, 4, 0); - //=== - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = OS; - /* falls through */ - case OS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.xflags = (hold & 0xff); - state.head.os = (hold >> 8); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = EXLEN; - /* falls through */ - case EXLEN: - if (state.flags & 0x0400) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length = hold; - if (state.head) { - state.head.extra_len = hold; - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - else if (state.head) { - state.head.extra = null/*Z_NULL*/; - } - state.mode = EXTRA; - /* falls through */ - case EXTRA: - if (state.flags & 0x0400) { - copy = state.length; - if (copy > have) { copy = have; } - if (copy) { - if (state.head) { - len = state.head.extra_len - state.length; - if (!state.head.extra) { - // Use untyped array for more conveniend processing later - state.head.extra = new Array(state.head.extra_len); - } - utils.arraySet( - state.head.extra, - input, - next, - // extra field is limited to 65536 bytes - // - no need for additional size check - copy, - /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ - len - ); - //zmemcpy(state.head.extra + len, next, - // len + copy > state.head.extra_max ? - // state.head.extra_max - len : copy); - } - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - state.length -= copy; - } - if (state.length) { break inf_leave; } - } - state.length = 0; - state.mode = NAME; - /* falls through */ - case NAME: - if (state.flags & 0x0800) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - // TODO: 2 or 1 bytes? - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.name_max*/)) { - state.head.name += String.fromCharCode(len); - } - } while (len && copy < have); - - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.name = null; - } - state.length = 0; - state.mode = COMMENT; - /* falls through */ - case COMMENT: - if (state.flags & 0x1000) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.comm_max*/)) { - state.head.comment += String.fromCharCode(len); - } - } while (len && copy < have); - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.comment = null; - } - state.mode = HCRC; - /* falls through */ - case HCRC: - if (state.flags & 0x0200) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.check & 0xffff)) { - strm.msg = 'header crc mismatch'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - if (state.head) { - state.head.hcrc = ((state.flags >> 9) & 1); - state.head.done = true; - } - strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/; - state.mode = TYPE; - break; - case DICTID: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - strm.adler = state.check = ZSWAP32(hold); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = DICT; - /* falls through */ - case DICT: - if (state.havedict === 0) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - return Z_NEED_DICT; - } - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = TYPE; - /* falls through */ - case TYPE: - if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } - /* falls through */ - case TYPEDO: - if (state.last) { - //--- BYTEBITS() ---// - hold >>>= bits & 7; - bits -= bits & 7; - //---// - state.mode = CHECK; - break; - } - //=== NEEDBITS(3); */ - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.last = (hold & 0x01)/*BITS(1)*/; - //--- DROPBITS(1) ---// - hold >>>= 1; - bits -= 1; - //---// - - switch ((hold & 0x03)/*BITS(2)*/) { - case 0: /* stored block */ - //Tracev((stderr, "inflate: stored block%s\n", - // state.last ? " (last)" : "")); - state.mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - //Tracev((stderr, "inflate: fixed codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = LEN_; /* decode codes */ - if (flush === Z_TREES) { - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break inf_leave; - } - break; - case 2: /* dynamic block */ - //Tracev((stderr, "inflate: dynamic codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = TABLE; - break; - case 3: - strm.msg = 'invalid block type'; - state.mode = BAD; - } - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break; - case STORED: - //--- BYTEBITS() ---// /* go to byte boundary */ - hold >>>= bits & 7; - bits -= bits & 7; - //---// - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { - strm.msg = 'invalid stored block lengths'; - state.mode = BAD; - break; - } - state.length = hold & 0xffff; - //Tracev((stderr, "inflate: stored length %u\n", - // state.length)); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = COPY_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case COPY_: - state.mode = COPY; - /* falls through */ - case COPY: - copy = state.length; - if (copy) { - if (copy > have) { copy = have; } - if (copy > left) { copy = left; } - if (copy === 0) { break inf_leave; } - //--- zmemcpy(put, next, copy); --- - utils.arraySet(output, input, next, copy, put); - //---// - have -= copy; - next += copy; - left -= copy; - put += copy; - state.length -= copy; - break; - } - //Tracev((stderr, "inflate: stored end\n")); - state.mode = TYPE; - break; - case TABLE: - //=== NEEDBITS(14); */ - while (bits < 14) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// -//#ifndef PKZIP_BUG_WORKAROUND - if (state.nlen > 286 || state.ndist > 30) { - strm.msg = 'too many length or distance symbols'; - state.mode = BAD; - break; - } -//#endif - //Tracev((stderr, "inflate: table sizes ok\n")); - state.have = 0; - state.mode = LENLENS; - /* falls through */ - case LENLENS: - while (state.have < state.ncode) { - //=== NEEDBITS(3); - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - while (state.have < 19) { - state.lens[order[state.have++]] = 0; - } - // We have separate tables & no pointers. 2 commented lines below not needed. - //state.next = state.codes; - //state.lencode = state.next; - // Switch to use dynamic table - state.lencode = state.lendyn; - state.lenbits = 7; - - opts = {bits: state.lenbits}; - ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); - state.lenbits = opts.bits; - - if (ret) { - strm.msg = 'invalid code lengths set'; - state.mode = BAD; - break; - } - //Tracev((stderr, "inflate: code lengths ok\n")); - state.have = 0; - state.mode = CODELENS; - /* falls through */ - case CODELENS: - while (state.have < state.nlen + state.ndist) { - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_val < 16) { - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.lens[state.have++] = here_val; - } - else { - if (here_val === 16) { - //=== NEEDBITS(here.bits + 2); - n = here_bits + 2; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - if (state.have === 0) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - len = state.lens[state.have - 1]; - copy = 3 + (hold & 0x03);//BITS(2); - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - } - else if (here_val === 17) { - //=== NEEDBITS(here.bits + 3); - n = here_bits + 3; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 3 + (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - else { - //=== NEEDBITS(here.bits + 7); - n = here_bits + 7; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 11 + (hold & 0x7f);//BITS(7); - //--- DROPBITS(7) ---// - hold >>>= 7; - bits -= 7; - //---// - } - if (state.have + copy > state.nlen + state.ndist) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - while (copy--) { - state.lens[state.have++] = len; - } - } - } - - /* handle error breaks in while */ - if (state.mode === BAD) { break; } - - /* check for end-of-block code (better have one) */ - if (state.lens[256] === 0) { - strm.msg = 'invalid code -- missing end-of-block'; - state.mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state.lenbits = 9; - - opts = {bits: state.lenbits}; - ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.lenbits = opts.bits; - // state.lencode = state.next; - - if (ret) { - strm.msg = 'invalid literal/lengths set'; - state.mode = BAD; - break; - } - - state.distbits = 6; - //state.distcode.copy(state.codes); - // Switch to use dynamic table - state.distcode = state.distdyn; - opts = {bits: state.distbits}; - ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.distbits = opts.bits; - // state.distcode = state.next; - - if (ret) { - strm.msg = 'invalid distances set'; - state.mode = BAD; - break; - } - //Tracev((stderr, 'inflate: codes ok\n')); - state.mode = LEN_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case LEN_: - state.mode = LEN; - /* falls through */ - case LEN: - if (have >= 6 && left >= 258) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - inflate_fast(strm, _out); - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - if (state.mode === TYPE) { - state.back = -1; - } - break; - } - state.back = 0; - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) -1)]; /*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if (here_bits <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_op && (here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.lencode[last_val + - ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - state.length = here_val; - if (here_op === 0) { - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - state.mode = LIT; - break; - } - if (here_op & 32) { - //Tracevv((stderr, "inflate: end of block\n")); - state.back = -1; - state.mode = TYPE; - break; - } - if (here_op & 64) { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break; - } - state.extra = here_op & 15; - state.mode = LENEXT; - /* falls through */ - case LENEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } - //Tracevv((stderr, "inflate: length %u\n", state.length)); - state.was = state.length; - state.mode = DIST; - /* falls through */ - case DIST: - for (;;) { - here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if ((here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.distcode[last_val + - ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - if (here_op & 64) { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break; - } - state.offset = here_val; - state.extra = (here_op) & 15; - state.mode = DISTEXT; - /* falls through */ - case DISTEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } -//#ifdef INFLATE_STRICT - if (state.offset > state.dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -//#endif - //Tracevv((stderr, "inflate: distance %u\n", state.offset)); - state.mode = MATCH; - /* falls through */ - case MATCH: - if (left === 0) { break inf_leave; } - copy = _out - left; - if (state.offset > copy) { /* copy from window */ - copy = state.offset - copy; - if (copy > state.whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// Trace((stderr, "inflate.c too far\n")); -// copy -= state.whave; -// if (copy > state.length) { copy = state.length; } -// if (copy > left) { copy = left; } -// left -= copy; -// state.length -= copy; -// do { -// output[put++] = 0; -// } while (--copy); -// if (state.length === 0) { state.mode = LEN; } -// break; -//#endif - } - if (copy > state.wnext) { - copy -= state.wnext; - from = state.wsize - copy; - } - else { - from = state.wnext - copy; - } - if (copy > state.length) { copy = state.length; } - from_source = state.window; - } - else { /* copy from output */ - from_source = output; - from = put - state.offset; - copy = state.length; - } - if (copy > left) { copy = left; } - left -= copy; - state.length -= copy; - do { - output[put++] = from_source[from++]; - } while (--copy); - if (state.length === 0) { state.mode = LEN; } - break; - case LIT: - if (left === 0) { break inf_leave; } - output[put++] = state.length; - left--; - state.mode = LEN; - break; - case CHECK: - if (state.wrap) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - // Use '|' insdead of '+' to make sure that result is signed - hold |= input[next++] << bits; - bits += 8; - } - //===// - _out -= left; - strm.total_out += _out; - state.total += _out; - if (_out) { - strm.adler = state.check = - /*UPDATE(state.check, put - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); - - } - _out = left; - // NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too - if ((state.flags ? hold : ZSWAP32(hold)) !== state.check) { - strm.msg = 'incorrect data check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: check matches trailer\n")); - } - state.mode = LENGTH; - /* falls through */ - case LENGTH: - if (state.wrap && state.flags) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.total & 0xffffffff)) { - strm.msg = 'incorrect length check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: length matches trailer\n")); - } - state.mode = DONE; - /* falls through */ - case DONE: - ret = Z_STREAM_END; - break inf_leave; - case BAD: - ret = Z_DATA_ERROR; - break inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - /* falls through */ - default: - return Z_STREAM_ERROR; - } - } - - // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" - - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - - if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && - (state.mode < CHECK || flush !== Z_FINISH))) { - if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { - state.mode = MEM; - return Z_MEM_ERROR; - } - } - _in -= strm.avail_in; - _out -= strm.avail_out; - strm.total_in += _in; - strm.total_out += _out; - state.total += _out; - if (state.wrap && _out) { - strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); - } - strm.data_type = state.bits + (state.last ? 64 : 0) + - (state.mode === TYPE ? 128 : 0) + - (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); - if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { - ret = Z_BUF_ERROR; - } - return ret; -} - -function inflateEnd(strm) { - - if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { - return Z_STREAM_ERROR; - } - - var state = strm.state; - if (state.window) { - state.window = null; - } - strm.state = null; - return Z_OK; -} - -function inflateGetHeader(strm, head) { - var state; - - /* check state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } - - /* save header structure */ - state.head = head; - head.done = false; - return Z_OK; -} - - -exports.inflateReset = inflateReset; -exports.inflateReset2 = inflateReset2; -exports.inflateResetKeep = inflateResetKeep; -exports.inflateInit = inflateInit; -exports.inflateInit2 = inflateInit2; -exports.inflate = inflate; -exports.inflateEnd = inflateEnd; -exports.inflateGetHeader = inflateGetHeader; -exports.inflateInfo = 'pako inflate (from Nodeca project)'; - -/* Not implemented -exports.inflateCopy = inflateCopy; -exports.inflateGetDictionary = inflateGetDictionary; -exports.inflateMark = inflateMark; -exports.inflatePrime = inflatePrime; -exports.inflateSetDictionary = inflateSetDictionary; -exports.inflateSync = inflateSync; -exports.inflateSyncPoint = inflateSyncPoint; -exports.inflateUndermine = inflateUndermine; -*/ - -},{"../utils/common":4,"./adler32":6,"./crc32":8,"./inffast":11,"./inftrees":13}],13:[function(require,module,exports){ -'use strict'; - - -var utils = require('../utils/common'); - -var MAXBITS = 15; -var ENOUGH_LENS = 852; -var ENOUGH_DISTS = 592; -//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -var CODES = 0; -var LENS = 1; -var DISTS = 2; - -var lbase = [ /* Length codes 257..285 base */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -]; - -var lext = [ /* Length codes 257..285 extra */ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 -]; - -var dbase = [ /* Distance codes 0..29 base */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0 -]; - -var dext = [ /* Distance codes 0..29 extra */ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64 -]; - -module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) -{ - var bits = opts.bits; - //here = opts.here; /* table entry for duplication */ - - var len = 0; /* a code's length in bits */ - var sym = 0; /* index of code symbols */ - var min = 0, max = 0; /* minimum and maximum code lengths */ - var root = 0; /* number of index bits for root table */ - var curr = 0; /* number of index bits for current table */ - var drop = 0; /* code bits to drop for sub-table */ - var left = 0; /* number of prefix codes available */ - var used = 0; /* code entries in table used */ - var huff = 0; /* Huffman code */ - var incr; /* for incrementing code, index */ - var fill; /* index for replicating entries */ - var low; /* low bits for current root entry */ - var mask; /* mask for low root bits */ - var next; /* next available space in table */ - var base = null; /* base value table to use */ - var base_index = 0; -// var shoextra; /* extra bits table to use */ - var end; /* use base and extra for symbol > end */ - var count = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* number of codes of each length */ - var offs = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* offsets in table for each length */ - var extra = null; - var extra_index = 0; - - var here_bits, here_op, here_val; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..MAXBITS. The caller must assure this. - 1..MAXBITS is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ - for (len = 0; len <= MAXBITS; len++) { - count[len] = 0; - } - for (sym = 0; sym < codes; sym++) { - count[lens[lens_index + sym]]++; - } - - /* bound code lengths, force root to be within code lengths */ - root = bits; - for (max = MAXBITS; max >= 1; max--) { - if (count[max] !== 0) { break; } - } - if (root > max) { - root = max; - } - if (max === 0) { /* no symbols to code at all */ - //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ - //table.bits[opts.table_index] = 1; //here.bits = (var char)1; - //table.val[opts.table_index++] = 0; //here.val = (var short)0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - - //table.op[opts.table_index] = 64; - //table.bits[opts.table_index] = 1; - //table.val[opts.table_index++] = 0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - opts.bits = 1; - return 0; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) { - if (count[min] !== 0) { break; } - } - if (root < min) { - root = min; - } - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) { - return -1; - } /* over-subscribed */ - } - if (left > 0 && (type === CODES || max !== 1)) { - return -1; /* incomplete set */ - } - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) { - offs[len + 1] = offs[len] + count[len]; - } - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) { - if (lens[lens_index + sym] !== 0) { - work[offs[lens[lens_index + sym]]++] = sym; - } - } - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for LENS and DIST tables against - the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in - the initial root table size constants. See the comments in inftrees.h - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - // poor man optimization - use if-else instead of switch, - // to avoid deopts in old v8 - if (type === CODES) { - base = extra = work; /* dummy value--not used */ - end = 19; - - } else if (type === LENS) { - base = lbase; - base_index -= 257; - extra = lext; - extra_index -= 257; - end = 256; - - } else { /* DISTS */ - base = dbase; - extra = dext; - end = -1; - } - - /* initialize opts for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = table_index; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = -1; /* trigger new sub-table when len > root */ - used = 1 << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - /* check available table space */ - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - var i=0; - /* process all codes and make table entries */ - for (;;) { - i++; - /* create table entry */ - here_bits = len - drop; - if (work[sym] < end) { - here_op = 0; - here_val = work[sym]; - } - else if (work[sym] > end) { - here_op = extra[extra_index + work[sym]]; - here_val = base[base_index + work[sym]]; - } - else { - here_op = 32 + 64; /* end of block */ - here_val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1 << (len - drop); - fill = 1 << curr; - min = fill; /* save offset to next table */ - do { - fill -= incr; - table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; - } while (fill !== 0); - - /* backwards increment the len-bit code huff */ - incr = 1 << (len - 1); - while (huff & incr) { - incr >>= 1; - } - if (incr !== 0) { - huff &= incr - 1; - huff += incr; - } else { - huff = 0; - } - - /* go to next symbol, update count, len */ - sym++; - if (--count[len] === 0) { - if (len === max) { break; } - len = lens[lens_index + work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) !== low) { - /* if first time, transition to sub-tables */ - if (drop === 0) { - drop = root; - } - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = 1 << curr; - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) { break; } - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1 << curr; - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - /* point entry in root table to sub-table */ - low = huff & mask; - /*table.op[low] = curr; - table.bits[low] = root; - table.val[low] = next - opts.table_index;*/ - table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff !== 0) { - //table.op[next + huff] = 64; /* invalid code marker */ - //table.bits[next + huff] = len - drop; - //table.val[next + huff] = 0; - table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; - } - - /* set return parameters */ - //opts.table_index += used; - opts.bits = root; - return 0; -}; - -},{"../utils/common":4}],14:[function(require,module,exports){ -'use strict'; - -module.exports = { - '2': 'need dictionary', /* Z_NEED_DICT 2 */ - '1': 'stream end', /* Z_STREAM_END 1 */ - '0': '', /* Z_OK 0 */ - '-1': 'file error', /* Z_ERRNO (-1) */ - '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ - '-3': 'data error', /* Z_DATA_ERROR (-3) */ - '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ - '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ - '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ -}; - -},{}],15:[function(require,module,exports){ -'use strict'; - - -var utils = require('../utils/common'); - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -//var Z_FILTERED = 1; -//var Z_HUFFMAN_ONLY = 2; -//var Z_RLE = 3; -var Z_FIXED = 4; -//var Z_DEFAULT_STRATEGY = 0; - -/* Possible values of the data_type field (though see inflate()) */ -var Z_BINARY = 0; -var Z_TEXT = 1; -//var Z_ASCII = 1; // = Z_TEXT -var Z_UNKNOWN = 2; - -/*============================================================================*/ - - -function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } - -// From zutil.h - -var STORED_BLOCK = 0; -var STATIC_TREES = 1; -var DYN_TREES = 2; -/* The three kinds of block type */ - -var MIN_MATCH = 3; -var MAX_MATCH = 258; -/* The minimum and maximum match lengths */ - -// From deflate.h -/* =========================================================================== - * Internal compression state. - */ - -var LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ - -var LITERALS = 256; -/* number of literal bytes 0..255 */ - -var L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ - -var D_CODES = 30; -/* number of distance codes */ - -var BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ - -var HEAP_SIZE = 2*L_CODES + 1; -/* maximum heap size */ - -var MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -var Buf_size = 16; -/* size of bit buffer in bi_buf */ - - -/* =========================================================================== - * Constants - */ - -var MAX_BL_BITS = 7; -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -var END_BLOCK = 256; -/* end of block literal code */ - -var REP_3_6 = 16; -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -var REPZ_3_10 = 17; -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -var REPZ_11_138 = 18; -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -var extra_lbits = /* extra bits for each length code */ - [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]; - -var extra_dbits = /* extra bits for each distance code */ - [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]; - -var extra_blbits = /* extra bits for each bit length code */ - [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]; - -var bl_order = - [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]; -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -// We pre-fill arrays with 0 to avoid uninitialized gaps - -var DIST_CODE_LEN = 512; /* see definition of array dist_code below */ - -// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1 -var static_ltree = new Array((L_CODES+2) * 2); -zero(static_ltree); -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -var static_dtree = new Array(D_CODES * 2); -zero(static_dtree); -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -var _dist_code = new Array(DIST_CODE_LEN); -zero(_dist_code); -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -var _length_code = new Array(MAX_MATCH-MIN_MATCH+1); -zero(_length_code); -/* length code for each normalized match length (0 == MIN_MATCH) */ - -var base_length = new Array(LENGTH_CODES); -zero(base_length); -/* First normalized length for each code (0 = MIN_MATCH) */ - -var base_dist = new Array(D_CODES); -zero(base_dist); -/* First normalized distance for each code (0 = distance of 1) */ - - -var StaticTreeDesc = function (static_tree, extra_bits, extra_base, elems, max_length) { - - this.static_tree = static_tree; /* static tree or NULL */ - this.extra_bits = extra_bits; /* extra bits for each code or NULL */ - this.extra_base = extra_base; /* base index for extra_bits */ - this.elems = elems; /* max number of elements in the tree */ - this.max_length = max_length; /* max bit length for the codes */ - - // show if `static_tree` has data or dummy - needed for monomorphic objects - this.has_stree = static_tree && static_tree.length; -}; - - -var static_l_desc; -var static_d_desc; -var static_bl_desc; - - -var TreeDesc = function(dyn_tree, stat_desc) { - this.dyn_tree = dyn_tree; /* the dynamic tree */ - this.max_code = 0; /* largest code with non zero frequency */ - this.stat_desc = stat_desc; /* the corresponding static tree */ -}; - - - -function d_code(dist) { - return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; -} - - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -function put_short (s, w) { -// put_byte(s, (uch)((w) & 0xff)); -// put_byte(s, (uch)((ush)(w) >> 8)); - s.pending_buf[s.pending++] = (w) & 0xff; - s.pending_buf[s.pending++] = (w >>> 8) & 0xff; -} - - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 16 and value fits in length bits. - */ -function send_bits(s, value, length) { - if (s.bi_valid > (Buf_size - length)) { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - put_short(s, s.bi_buf); - s.bi_buf = value >> (Buf_size - s.bi_valid); - s.bi_valid += length - Buf_size; - } else { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - s.bi_valid += length; - } -} - - -function send_code(s, c, tree) { - send_bits(s, tree[c*2]/*.Code*/, tree[c*2 + 1]/*.Len*/); -} - - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -function bi_reverse(code, len) { - var res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; -} - - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -function bi_flush(s) { - if (s.bi_valid === 16) { - put_short(s, s.bi_buf); - s.bi_buf = 0; - s.bi_valid = 0; - - } else if (s.bi_valid >= 8) { - s.pending_buf[s.pending++] = s.bi_buf & 0xff; - s.bi_buf >>= 8; - s.bi_valid -= 8; - } -} - - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -function gen_bitlen(s, desc) -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - var tree = desc.dyn_tree; - var max_code = desc.max_code; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var extra = desc.stat_desc.extra_bits; - var base = desc.stat_desc.extra_base; - var max_length = desc.stat_desc.max_length; - var h; /* heap index */ - var n, m; /* iterate over the tree elements */ - var bits; /* bit length */ - var xbits; /* extra bits */ - var f; /* frequency */ - var overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) { - s.bl_count[bits] = 0; - } - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s.heap[s.heap_max]*2 + 1]/*.Len*/ = 0; /* root of the heap */ - - for (h = s.heap_max+1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n*2 +1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n*2 + 1]/*.Len*/ = bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) { continue; } /* not a leaf node */ - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) { - xbits = extra[n-base]; - } - f = tree[n * 2]/*.Freq*/; - s.opt_len += f * (bits + xbits); - if (has_stree) { - s.static_len += f * (stree[n*2 + 1]/*.Len*/ + xbits); - } - } - if (overflow === 0) { return; } - - // Trace((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length-1; - while (s.bl_count[bits] === 0) { bits--; } - s.bl_count[bits]--; /* move one leaf down the tree */ - s.bl_count[bits+1] += 2; /* move one overflow item as its brother */ - s.bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > max_code) { continue; } - if (tree[m*2 + 1]/*.Len*/ !== bits) { - // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s.opt_len += (bits - tree[m*2 + 1]/*.Len*/)*tree[m*2]/*.Freq*/; - tree[m*2 + 1]/*.Len*/ = bits; - } - n--; - } - } -} - - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -function gen_codes(tree, max_code, bl_count) -// ct_data *tree; /* the tree to decorate */ -// int max_code; /* largest code with non zero frequency */ -// ushf *bl_count; /* number of codes at each bit length */ -{ - var next_code = new Array(MAX_BITS+1); /* next code value for each bit length */ - var code = 0; /* running code value */ - var bits; /* bit index */ - var n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - //Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES-1; code++) { - base_length[code] = length; - for (n = 0; n < (1< dist code (0..29) */ - dist = 0; - for (code = 0 ; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ - for (; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { - _dist_code[256 + dist++] = code; - } - } - //Assert (dist == 256, "tr_static_init: 256+dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) { - bl_count[bits] = 0; - } - - n = 0; - while (n <= 143) { - static_ltree[n*2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - while (n <= 255) { - static_ltree[n*2 + 1]/*.Len*/ = 9; - n++; - bl_count[9]++; - } - while (n <= 279) { - static_ltree[n*2 + 1]/*.Len*/ = 7; - n++; - bl_count[7]++; - } - while (n <= 287) { - static_ltree[n*2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes(static_ltree, L_CODES+1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n*2 + 1]/*.Len*/ = 5; - static_dtree[n*2]/*.Code*/ = bi_reverse(n, 5); - } - - // Now data ready and we can init static trees - static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS); - static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); - static_bl_desc =new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); - - //static_init_done = true; -} - - -/* =========================================================================== - * Initialize a new block. - */ -function init_block(s) { - var n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n*2]/*.Freq*/ = 0; } - for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n*2]/*.Freq*/ = 0; } - for (n = 0; n < BL_CODES; n++) { s.bl_tree[n*2]/*.Freq*/ = 0; } - - s.dyn_ltree[END_BLOCK*2]/*.Freq*/ = 1; - s.opt_len = s.static_len = 0; - s.last_lit = s.matches = 0; -} - - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -function bi_windup(s) -{ - if (s.bi_valid > 8) { - put_short(s, s.bi_buf); - } else if (s.bi_valid > 0) { - //put_byte(s, (Byte)s->bi_buf); - s.pending_buf[s.pending++] = s.bi_buf; - } - s.bi_buf = 0; - s.bi_valid = 0; -} - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -function copy_block(s, buf, len, header) -//DeflateState *s; -//charf *buf; /* the input data */ -//unsigned len; /* its length */ -//int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, len); - put_short(s, ~len); - } -// while (len--) { -// put_byte(s, *buf++); -// } - utils.arraySet(s.pending_buf, s.window, buf, len, s.pending); - s.pending += len; -} - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -function smaller(tree, n, m, depth) { - var _n2 = n*2; - var _m2 = m*2; - return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || - (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); -} - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -function pqdownheap(s, tree, k) -// deflate_state *s; -// ct_data *tree; /* the tree to restore */ -// int k; /* node to move down */ -{ - var v = s.heap[k]; - var j = k << 1; /* left son of k */ - while (j <= s.heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s.heap_len && - smaller(tree, s.heap[j+1], s.heap[j], s.depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s.heap[j], s.depth)) { break; } - - /* Exchange v with the smallest son */ - s.heap[k] = s.heap[j]; - k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s.heap[k] = v; -} - - -// inlined manually -// var SMALLEST = 1; - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -function compress_block(s, ltree, dtree) -// deflate_state *s; -// const ct_data *ltree; /* literal tree */ -// const ct_data *dtree; /* distance tree */ -{ - var dist; /* distance of matched string */ - var lc; /* match length or unmatched char (if dist == 0) */ - var lx = 0; /* running index in l_buf */ - var code; /* the code to send */ - var extra; /* number of extra bits to send */ - - if (s.last_lit !== 0) { - do { - dist = (s.pending_buf[s.d_buf + lx*2] << 8) | (s.pending_buf[s.d_buf + lx*2 + 1]); - lc = s.pending_buf[s.l_buf + lx]; - lx++; - - if (dist === 0) { - send_code(s, lc, ltree); /* send a literal byte */ - //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code+LITERALS+1, ltree); /* send the length code */ - extra = extra_lbits[code]; - if (extra !== 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - //Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra !== 0) { - dist -= base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, - // "pendingBuf overflow"); - - } while (lx < s.last_lit); - } - - send_code(s, END_BLOCK, ltree); -} - - -/* =========================================================================== - * Construct one Huffman tree and assigns the code bit strings and lengths. - * Update the total bit length for the current block. - * IN assertion: the field freq is set for all tree elements. - * OUT assertions: the fields len and code are set to the optimal bit length - * and corresponding code. The length opt_len is updated; static_len is - * also updated if stree is not null. The field max_code is set. - */ -function build_tree(s, desc) -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - var tree = desc.dyn_tree; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var elems = desc.stat_desc.elems; - var n, m; /* iterate over heap elements */ - var max_code = -1; /* largest code with non zero frequency */ - var node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n * 2]/*.Freq*/ !== 0) { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - - } else { - tree[n*2 + 1]/*.Len*/ = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s.heap_len < 2) { - node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); - tree[node * 2]/*.Freq*/ = 1; - s.depth[node] = 0; - s.opt_len--; - - if (has_stree) { - s.static_len -= stree[node*2 + 1]/*.Len*/; - } - /* node is 0 or 1 so it does not have extra bits */ - } - desc.max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - //pqremove(s, tree, n); /* n = node of least frequency */ - /*** pqremove ***/ - n = s.heap[1/*SMALLEST*/]; - s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; - pqdownheap(s, tree, 1/*SMALLEST*/); - /***/ - - m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ - - s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ - s.heap[--s.heap_max] = m; - - /* Create a new node father of n and m */ - tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; - s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; - tree[n*2 + 1]/*.Dad*/ = tree[m*2 + 1]/*.Dad*/ = node; - - /* and insert the new node in the heap */ - s.heap[1/*SMALLEST*/] = node++; - pqdownheap(s, tree, 1/*SMALLEST*/); - - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes(tree, max_code, s.bl_count); -} - - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -function scan_tree(s, tree, max_code) -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - var n; /* iterates over all tree elements */ - var prevlen = -1; /* last emitted length */ - var curlen; /* length of current code */ - - var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */ - - var count = 0; /* repeat count of the current code */ - var max_count = 7; /* max repeat count */ - var min_count = 4; /* min repeat count */ - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code+1)*2 + 1]/*.Len*/ = 0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n+1)*2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - s.bl_tree[curlen * 2]/*.Freq*/ += count; - - } else if (curlen !== 0) { - - if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } - s.bl_tree[REP_3_6*2]/*.Freq*/++; - - } else if (count <= 10) { - s.bl_tree[REPZ_3_10*2]/*.Freq*/++; - - } else { - s.bl_tree[REPZ_11_138*2]/*.Freq*/++; - } - - count = 0; - prevlen = curlen; - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -} - - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -function send_tree(s, tree, max_code) -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - var n; /* iterates over all tree elements */ - var prevlen = -1; /* last emitted length */ - var curlen; /* length of current code */ - - var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */ - - var count = 0; /* repeat count of the current code */ - var max_count = 7; /* max repeat count */ - var min_count = 4; /* min repeat count */ - - /* tree[max_code+1].Len = -1; */ /* guard already set */ - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n+1)*2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); - - } else if (curlen !== 0) { - if (curlen !== prevlen) { - send_code(s, curlen, s.bl_tree); - count--; - } - //Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s.bl_tree); - send_bits(s, count-3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s.bl_tree); - send_bits(s, count-3, 3); - - } else { - send_code(s, REPZ_11_138, s.bl_tree); - send_bits(s, count-11, 7); - } - - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -} - - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -function build_bl_tree(s) { - var max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, s.dyn_ltree, s.l_desc.max_code); - scan_tree(s, s.dyn_dtree, s.d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, s.bl_desc); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { - if (s.bl_tree[bl_order[max_blindex]*2 + 1]/*.Len*/ !== 0) { - break; - } - } - /* Update opt_len to include the bit length tree and counts */ - s.opt_len += 3*(max_blindex+1) + 5+5+4; - //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", - // s->opt_len, s->static_len)); - - return max_blindex; -} - - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -function send_all_trees(s, lcodes, dcodes, blcodes) -// deflate_state *s; -// int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ - var rank; /* index in bl_order */ - - //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - // "too many codes"); - //Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes-1, 5); - send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s.bl_tree[bl_order[rank]*2 + 1]/*.Len*/, 3); - } - //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_ltree, lcodes-1); /* literal tree */ - //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_dtree, dcodes-1); /* distance tree */ - //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); -} - - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "black list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -function detect_data_type(s) { - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - var black_mask = 0xf3ffc07f; - var n; - - /* Check for non-textual ("black-listed") bytes. */ - for (n = 0; n <= 31; n++, black_mask >>>= 1) { - if ((black_mask & 1) && (s.dyn_ltree[n*2]/*.Freq*/ !== 0)) { - return Z_BINARY; - } - } - - /* Check for textual ("white-listed") bytes. */ - if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || - s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - for (n = 32; n < LITERALS; n++) { - if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - } - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - - -var static_init_done = false; - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -function _tr_init(s) -{ - - if (!static_init_done) { - tr_static_init(); - static_init_done = true; - } - - s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); - s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); - s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); - - s.bi_buf = 0; - s.bi_valid = 0; - - /* Initialize the first block of the first file: */ - init_block(s); -} - - -/* =========================================================================== - * Send a stored block - */ -function _tr_stored_block(s, buf, stored_len, last) -//DeflateState *s; -//charf *buf; /* input block */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - send_bits(s, (STORED_BLOCK<<1)+(last ? 1 : 0), 3); /* send block type */ - copy_block(s, buf, stored_len, true); /* with header */ -} - - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -function _tr_align(s) { - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); - bi_flush(s); -} - - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. - */ -function _tr_flush_block(s, buf, stored_len, last) -//DeflateState *s; -//charf *buf; /* input block, or NULL if too old */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - var opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - var max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s.level > 0) { - - /* Check if the file is binary or text */ - if (s.strm.data_type === Z_UNKNOWN) { - s.strm.data_type = detect_data_type(s); - } - - /* Construct the literal and distance trees */ - build_tree(s, s.l_desc); - // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - - build_tree(s, s.d_desc); - // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s.opt_len+3+7) >>> 3; - static_lenb = (s.static_len+3+7) >>> 3; - - // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", - // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - // s->last_lit)); - - if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } - - } else { - // Assert(buf != (char*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - - if ((stored_len+4 <= opt_lenb) && (buf !== -1)) { - /* 4: two words for the lengths */ - - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - - } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { - - send_bits(s, (STATIC_TREES<<1) + (last ? 1 : 0), 3); - compress_block(s, static_ltree, static_dtree); - - } else { - send_bits(s, (DYN_TREES<<1) + (last ? 1 : 0), 3); - send_all_trees(s, s.l_desc.max_code+1, s.d_desc.max_code+1, max_blindex+1); - compress_block(s, s.dyn_ltree, s.dyn_dtree); - } - // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - /* The above check is made mod 2^32, for files larger than 512 MB - * and uLong implemented on 32 bits. - */ - init_block(s); - - if (last) { - bi_windup(s); - } - // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - // s->compressed_len-7*last)); -} - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -function _tr_tally(s, dist, lc) -// deflate_state *s; -// unsigned dist; /* distance of matched string */ -// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ -{ - //var out_length, in_length, dcode; - - s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; - s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; - - s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; - s.last_lit++; - - if (dist === 0) { - /* lc is the unmatched char */ - s.dyn_ltree[lc*2]/*.Freq*/++; - } else { - s.matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - //Assert((ush)dist < (ush)MAX_DIST(s) && - // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s.dyn_ltree[(_length_code[lc]+LITERALS+1) * 2]/*.Freq*/++; - s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; - } - -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility - -//#ifdef TRUNCATE_BLOCK -// /* Try to guess if it is profitable to stop the current block here */ -// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { -// /* Compute an upper bound for the compressed length */ -// out_length = s.last_lit*8; -// in_length = s.strstart - s.block_start; -// -// for (dcode = 0; dcode < D_CODES; dcode++) { -// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); -// } -// out_length >>>= 3; -// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", -// // s->last_lit, in_length, out_length, -// // 100L - out_length*100L/in_length)); -// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { -// return true; -// } -// } -//#endif - - return (s.last_lit === s.lit_bufsize-1); - /* We avoid equality with lit_bufsize because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ -} - -exports._tr_init = _tr_init; -exports._tr_stored_block = _tr_stored_block; -exports._tr_flush_block = _tr_flush_block; -exports._tr_tally = _tr_tally; -exports._tr_align = _tr_align; - -},{"../utils/common":4}],16:[function(require,module,exports){ -'use strict'; - - -function ZStream() { - /* next input byte */ - this.input = null; // JS specific, because we have no pointers - this.next_in = 0; - /* number of bytes available at input */ - this.avail_in = 0; - /* total number of input bytes read so far */ - this.total_in = 0; - /* next output byte should be put there */ - this.output = null; // JS specific, because we have no pointers - this.next_out = 0; - /* remaining free space at output */ - this.avail_out = 0; - /* total number of bytes output so far */ - this.total_out = 0; - /* last error message, NULL if no error */ - this.msg = ''/*Z_NULL*/; - /* not visible by applications */ - this.state = null; - /* best guess about the data type: binary or text */ - this.data_type = 2/*Z_UNKNOWN*/; - /* adler32 value of the uncompressed data */ - this.adler = 0; -} - -module.exports = ZStream; - -},{}],17:[function(require,module,exports){ - -/*jslint browser: true, node: true */ -/*global require, module */ - -"use strict"; - -/*** Imports ***/ - -/** - * nifti - * @type {*|{}} - */ -var nifti = nifti || {}; -nifti.NIFTI1 = nifti.NIFTI1 || ((typeof require !== 'undefined') ? require('./nifti1.js') : null); -nifti.NIFTI2 = nifti.NIFTI2 || ((typeof require !== 'undefined') ? require('./nifti2.js') : null); -nifti.Utils = nifti.Utils || ((typeof require !== 'undefined') ? require('./utilities.js') : null); - -var pako = pako || ((typeof require !== 'undefined') ? require('pako') : null); - - - -/*** Static Methods ***/ - -/** - * Returns true if this data represents a NIFTI-1 header. - * @param {ArrayBuffer} data - * @returns {boolean} - */ -nifti.isNIFTI1 = function (data) { - var buf, mag1, mag2, mag3; - - if (data.byteLength < nifti.NIFTI1.STANDARD_HEADER_SIZE) { - return false; - } - - buf = new DataView(data); - - if (buf) - - mag1 = buf.getUint8(nifti.NIFTI1.MAGIC_NUMBER_LOCATION); - mag2 = buf.getUint8(nifti.NIFTI1.MAGIC_NUMBER_LOCATION + 1); - mag3 = buf.getUint8(nifti.NIFTI1.MAGIC_NUMBER_LOCATION + 2); - - return !!((mag1 === nifti.NIFTI1.MAGIC_NUMBER[0]) && (mag2 === nifti.NIFTI1.MAGIC_NUMBER[1]) && - (mag3 === nifti.NIFTI1.MAGIC_NUMBER[2])); -}; - - -/** - * Returns true if this data represents a NIFTI-2 header. - * @param {ArrayBuffer} data - * @returns {boolean} - */ -nifti.isNIFTI2 = function (data) { - var buf, mag1, mag2, mag3; - - if (data.byteLength < nifti.NIFTI1.STANDARD_HEADER_SIZE) { - return false; - } - - buf = new DataView(data); - mag1 = buf.getUint8(nifti.NIFTI2.MAGIC_NUMBER_LOCATION); - mag2 = buf.getUint8(nifti.NIFTI2.MAGIC_NUMBER_LOCATION + 1); - mag3 = buf.getUint8(nifti.NIFTI2.MAGIC_NUMBER_LOCATION + 2); - - return !!((mag1 === nifti.NIFTI2.MAGIC_NUMBER[0]) && (mag2 === nifti.NIFTI2.MAGIC_NUMBER[1]) && - (mag3 === nifti.NIFTI2.MAGIC_NUMBER[2])); -}; - - - -/** - * Returns true if this data represents a NIFTI header. - * @param {ArrayBuffer} data - * @returns {boolean} - */ -nifti.isNIFTI = function (data) { - return (nifti.isNIFTI1(data) || nifti.isNIFTI2(data)); -}; - - - -/** - * Returns true if this data is GZIP compressed. - * @param {ArrayBuffer} data - * @returns {boolean} - */ -nifti.isCompressed = function (data) { - var buf, magicCookie1, magicCookie2; - - if (data) { - buf = new DataView(data); - - magicCookie1 = buf.getUint8(0); - magicCookie2 = buf.getUint8(1); - - if (magicCookie1 === nifti.Utils.GUNZIP_MAGIC_COOKIE1) { - return true; - } - - if (magicCookie2 === nifti.Utils.GUNZIP_MAGIC_COOKIE2) { - return true; - } - } - - return false; -}; - - - -/** - * Returns decompressed data. - * @param {ArrayBuffer} data - * @returns {ArrayBuffer} - */ -nifti.decompress = function (data) { - return pako.inflate(data).buffer; -}; - - - -/** - * Reads and returns the header object. - * @param {ArrayBuffer} data - * @returns {nifti.NIFTI1|nifti.NIFTI2|null} - */ -nifti.readHeader = function (data) { - var header = null; - - if (nifti.isCompressed(data)) { - data = nifti.decompress(data); - } - - if (nifti.isNIFTI1(data)) { - header = new nifti.NIFTI1(); - } else if (nifti.isNIFTI2(data)) { - header = new nifti.NIFTI2(); - } - - if (header) { - header.readHeader(data); - } else { - console.error("That file does not appear to be NIFTI!"); - } - - return header; -}; - - - -/** - * Returns true if this header contains an extension. - * @param {nifti.NIFTI1|nifti.NIFTI2} header - * @returns {boolean} - */ -nifti.hasExtension = function (header) { - return (header.extensionFlag[0] != 0); -}; - - - -/** - * Returns the image data. - * @param {nifti.NIFTI1|nifti.NIFTI2} header - * @param {ArrayBuffer} data - * @returns {ArrayBuffer} - */ -nifti.readImage = function (header, data) { - var imageOffset = header.vox_offset, - timeDim = 1, - statDim = 1; - - if (header.dims[4]) { - timeDim = header.dims[4]; - } - - if (header.dims[5]) { - statDim = header.dims[5]; - } - - var imageSize = header.dims[1] * header.dims[2] * header.dims[3] * timeDim * statDim * (header.numBitsPerVoxel / 8); - return data.slice(imageOffset, imageOffset + imageSize); -}; - - - -/** - * Returns the extension data (including extension header). - * @param {nifti.NIFTI1|nifti.NIFTI2} header - * @param {ArrayBuffer} data - * @returns {ArrayBuffer} - */ -nifti.readExtension = function (header, data) { - var loc = header.getExtensionLocation(), - size = header.extensionSize; - - return data.slice(loc, loc + size); -}; - - - -/** - * Returns the extension data. - * @param {nifti.NIFTI1|nifti.NIFTI2} header - * @param {ArrayBuffer} data - * @returns {ArrayBuffer} - */ -nifti.readExtensionData = function (header, data) { - var loc = header.getExtensionLocation(), - size = header.extensionSize; - - return data.slice(loc + 8, loc + size - 8); -}; - - -/*** Exports ***/ - -var moduleType = typeof module; -if ((moduleType !== 'undefined') && module.exports) { - module.exports = nifti; -} - -},{"./nifti1.js":18,"./nifti2.js":19,"./utilities.js":20,"pako":1}],18:[function(require,module,exports){ - -/*jslint browser: true, node: true */ -/*global */ - -"use strict"; - -/*** Imports ***/ - -var nifti = nifti || {}; -nifti.Utils = nifti.Utils || ((typeof require !== 'undefined') ? require('./utilities.js') : null); - - - -/*** Constructor ***/ - -/** - * The NIFTI1 constructor. - * @constructor - * @property {boolean} littleEndian - * @property {number} dim_info - * @property {number[]} dims - image dimensions - * @property {number} intent_p1 - * @property {number} intent_p2 - * @property {number} intent_p3 - * @property {number} intent_code - * @property {number} datatypeCode - * @property {number} numBitsPerVoxel - * @property {number} slice_start - * @property {number} slice_end - * @property {number} slice_code - * @property {number[]} pixDims - voxel dimensions - * @property {number} vox_offset - * @property {number} scl_slope - * @property {number} scl_inter - * @property {number} xyzt_units - * @property {number} cal_max - * @property {number} cal_min - * @property {number} slice_duration - * @property {number} toffset - * @property {string} description - * @property {string} aux_file - * @property {string} intent_name - * @property {number} qform_code - * @property {number} sform_code - * @property {number} quatern_b - * @property {number} quatern_c - * @property {number} quatern_d - * @property {number} quatern_x - * @property {number} quatern_y - * @property {number} quatern_z - * @property {Array.>} affine - * @property {string} magic - * @property {boolean} isHDR - if hdr/img format - * @property {number[]} extensionFlag - * @property {number} extensionSize - * @property {number} extensionCode - * @type {Function} - */ -nifti.NIFTI1 = nifti.NIFTI1 || function () { - this.littleEndian = false; - this.dim_info = 0; - this.dims = []; - this.intent_p1 = 0; - this.intent_p2 = 0; - this.intent_p3 = 0; - this.intent_code = 0; - this.datatypeCode = 0; - this.numBitsPerVoxel = 0; - this.slice_start = 0; - this.slice_end = 0; - this.slice_code = 0; - this.pixDims = []; - this.vox_offset = 0; - this.scl_slope = 1; - this.scl_inter = 0; - this.xyzt_units = 0; - this.cal_max = 0; - this.cal_min = 0; - this.slice_duration = 0; - this.toffset = 0; - this.description = ""; - this.aux_file = ""; - this.intent_name = ""; - this.qform_code = 0; - this.sform_code = 0; - this.quatern_b = 0; - this.quatern_c = 0; - this.quatern_d = 0; - this.qoffset_x = 0; - this.qoffset_y = 0; - this.qoffset_z = 0; - this.affine = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; - this.magic = 0; - this.isHDR = false; - this.extensionFlag = [0, 0, 0, 0]; - this.extensionSize = 0; - this.extensionCode = 0; -}; - - - -/*** Static Pseudo-constants ***/ - -// datatype codes -nifti.NIFTI1.TYPE_NONE = 0; -nifti.NIFTI1.TYPE_BINARY = 1; -nifti.NIFTI1.TYPE_UINT8 = 2; -nifti.NIFTI1.TYPE_INT16 = 4; -nifti.NIFTI1.TYPE_INT32 = 8; -nifti.NIFTI1.TYPE_FLOAT32 = 16; -nifti.NIFTI1.TYPE_COMPLEX64 = 32; -nifti.NIFTI1.TYPE_FLOAT64 = 64; -nifti.NIFTI1.TYPE_RGB24 = 128; -nifti.NIFTI1.TYPE_INT8 = 256; -nifti.NIFTI1.TYPE_UINT16 = 512; -nifti.NIFTI1.TYPE_UINT32 = 768; -nifti.NIFTI1.TYPE_INT64 = 1024; -nifti.NIFTI1.TYPE_UINT64 = 1280; -nifti.NIFTI1.TYPE_FLOAT128 = 1536; -nifti.NIFTI1.TYPE_COMPLEX128 = 1792; -nifti.NIFTI1.TYPE_COMPLEX256 = 2048; - -// transform codes -nifti.NIFTI1.XFORM_UNKNOWN = 0; -nifti.NIFTI1.XFORM_SCANNER_ANAT = 1; -nifti.NIFTI1.XFORM_ALIGNED_ANAT = 2; -nifti.NIFTI1.XFORM_TALAIRACH = 3; -nifti.NIFTI1.XFORM_MNI_152 = 4; - -// unit codes -nifti.NIFTI1.SPATIAL_UNITS_MASK = 0x07; -nifti.NIFTI1.TEMPORAL_UNITS_MASK = 0x38; -nifti.NIFTI1.UNITS_UNKNOWN = 0; -nifti.NIFTI1.UNITS_METER = 1; -nifti.NIFTI1.UNITS_MM = 2; -nifti.NIFTI1.UNITS_MICRON = 3; -nifti.NIFTI1.UNITS_SEC = 8; -nifti.NIFTI1.UNITS_MSEC = 16; -nifti.NIFTI1.UNITS_USEC = 24; -nifti.NIFTI1.UNITS_HZ = 32; -nifti.NIFTI1.UNITS_PPM = 40; -nifti.NIFTI1.UNITS_RADS = 48; - -// nifti1 codes -nifti.NIFTI1.MAGIC_COOKIE = 348; -nifti.NIFTI1.STANDARD_HEADER_SIZE = 348; -nifti.NIFTI1.MAGIC_NUMBER_LOCATION = 344; -nifti.NIFTI1.MAGIC_NUMBER = [0x6E, 0x2B, 0x31]; // n+1 (.nii) -nifti.NIFTI1.MAGIC_NUMBER2 = [0x6E, 0x69, 0x31]; // ni1 (.hdr/.img) -nifti.NIFTI1.EXTENSION_HEADER_SIZE = 8; - - -/*** Prototype Methods ***/ - -/** - * Reads the header data. - * @param {ArrayBuffer} data - */ -nifti.NIFTI1.prototype.readHeader = function (data) { - var rawData = new DataView(data), - magicCookieVal = nifti.Utils.getIntAt(rawData, 0, this.littleEndian), - ctr, - ctrOut, - ctrIn, - index; - - if (magicCookieVal !== nifti.NIFTI1.MAGIC_COOKIE) { // try as little endian - this.littleEndian = true; - magicCookieVal = nifti.Utils.getIntAt(rawData, 0, this.littleEndian); - } - - if (magicCookieVal !== nifti.NIFTI1.MAGIC_COOKIE) { - throw new Error("This does not appear to be a NIFTI file!"); - } - - this.dim_info = nifti.Utils.getByteAt(rawData, 39); - - for (ctr = 0; ctr < 8; ctr += 1) { - index = 40 + (ctr * 2); - this.dims[ctr] = nifti.Utils.getShortAt(rawData, index, this.littleEndian); - } - - this.intent_p1 = nifti.Utils.getFloatAt(rawData, 56, this.littleEndian); - this.intent_p2 = nifti.Utils.getFloatAt(rawData, 60, this.littleEndian); - this.intent_p3 = nifti.Utils.getFloatAt(rawData, 64, this.littleEndian); - this.intent_code = nifti.Utils.getShortAt(rawData, 68, this.littleEndian); - - this.datatypeCode = nifti.Utils.getShortAt(rawData, 70, this.littleEndian); - this.numBitsPerVoxel = nifti.Utils.getShortAt(rawData, 72, this.littleEndian); - - this.slice_start = nifti.Utils.getShortAt(rawData, 74, this.littleEndian); - - for (ctr = 0; ctr < 8; ctr += 1) { - index = 76 + (ctr * 4); - this.pixDims[ctr] = nifti.Utils.getFloatAt(rawData, index, this.littleEndian); - } - - this.vox_offset = nifti.Utils.getFloatAt(rawData, 108, this.littleEndian); - - this.scl_slope = nifti.Utils.getFloatAt(rawData, 112, this.littleEndian); - this.scl_inter = nifti.Utils.getFloatAt(rawData, 116, this.littleEndian); - - this.slice_end = nifti.Utils.getShortAt(rawData, 120, this.littleEndian); - this.slice_code = nifti.Utils.getByteAt(rawData, 122); - - this.xyzt_units = nifti.Utils.getByteAt(rawData, 123); - - this.cal_max = nifti.Utils.getFloatAt(rawData, 124, this.littleEndian); - this.cal_min = nifti.Utils.getFloatAt(rawData, 128, this.littleEndian); - - this.slice_duration = nifti.Utils.getFloatAt(rawData, 132, this.littleEndian); - this.toffset = nifti.Utils.getFloatAt(rawData, 136, this.littleEndian); - - this.description = nifti.Utils.getStringAt(rawData, 148, 228); - this.aux_file = nifti.Utils.getStringAt(rawData, 228, 252); - - this.qform_code = nifti.Utils.getShortAt(rawData, 252, this.littleEndian); - this.sform_code = nifti.Utils.getShortAt(rawData, 254, this.littleEndian); - - this.quatern_b = nifti.Utils.getFloatAt(rawData, 256, this.littleEndian); - this.quatern_c = nifti.Utils.getFloatAt(rawData, 260, this.littleEndian); - this.quatern_d = nifti.Utils.getFloatAt(rawData, 264, this.littleEndian); - this.qoffset_x = nifti.Utils.getFloatAt(rawData, 268, this.littleEndian); - this.qoffset_y = nifti.Utils.getFloatAt(rawData, 272, this.littleEndian); - this.qoffset_z = nifti.Utils.getFloatAt(rawData, 276, this.littleEndian); - - for (ctrOut = 0; ctrOut < 3; ctrOut += 1) { - for (ctrIn = 0; ctrIn < 4; ctrIn += 1) { - index = 280 + (((ctrOut * 4) + ctrIn) * 4); - this.affine[ctrOut][ctrIn] = nifti.Utils.getFloatAt(rawData, index, this.littleEndian); - } - } - - this.affine[3][0] = 0; - this.affine[3][1] = 0; - this.affine[3][2] = 0; - this.affine[3][3] = 1; - - this.intent_name = nifti.Utils.getStringAt(rawData, 328, 344); - this.magic = nifti.Utils.getStringAt(rawData, 344, 348); - - this.isHDR = (this.magic === nifti.NIFTI1.MAGIC_NUMBER2); - - if (rawData.byteLength > nifti.NIFTI1.MAGIC_COOKIE) { - this.extensionFlag[0] = nifti.Utils.getByteAt(rawData, 348); - this.extensionFlag[1] = nifti.Utils.getByteAt(rawData, 348 + 1); - this.extensionFlag[2] = nifti.Utils.getByteAt(rawData, 348 + 2); - this.extensionFlag[3] = nifti.Utils.getByteAt(rawData, 348 + 3); - - if (this.extensionFlag[0]) { - this.extensionSize = this.getExtensionSize(rawData); - this.extensionCode = this.getExtensionCode(rawData); - } - } -}; - - -/** - * Returns a formatted string of header fields. - * @returns {string} - */ -nifti.NIFTI1.prototype.toFormattedString = function () { - var fmt = nifti.Utils.formatNumber, - string = ""; - - string += ("Dim Info = " + this.dim_info + "\n"); - - string += ("Image Dimensions (1-8): " + - this.dims[0] + ", " + - this.dims[1] + ", " + - this.dims[2] + ", " + - this.dims[3] + ", " + - this.dims[4] + ", " + - this.dims[5] + ", " + - this.dims[6] + ", " + - this.dims[7] + "\n"); - - string += ("Intent Parameters (1-3): " + - this.intent_p1 + ", " + - this.intent_p2 + ", " + - this.intent_p3) + "\n"; - - string += ("Intent Code = " + this.intent_code + "\n"); - string += ("Datatype = " + this.datatypeCode + " (" + this.getDatatypeCodeString(this.datatypeCode) + ")\n"); - string += ("Bits Per Voxel = " + this.numBitsPerVoxel + "\n"); - string += ("Slice Start = " + this.slice_start + "\n"); - string += ("Voxel Dimensions (1-8): " + - fmt(this.pixDims[0]) + ", " + - fmt(this.pixDims[1]) + ", " + - fmt(this.pixDims[2]) + ", " + - fmt(this.pixDims[3]) + ", " + - fmt(this.pixDims[4]) + ", " + - fmt(this.pixDims[5]) + ", " + - fmt(this.pixDims[6]) + ", " + - fmt(this.pixDims[7]) + "\n"); - - string += ("Image Offset = " + this.vox_offset + "\n"); - string += ("Data Scale: Slope = " + fmt(this.scl_slope) + " Intercept = " + fmt(this.scl_inter) + "\n"); - string += ("Slice End = " + this.slice_end + "\n"); - string += ("Slice Code = " + this.slice_code + "\n"); - string += ("Units Code = " + this.xyzt_units + " (" + this.getUnitsCodeString(nifti.NIFTI1.SPATIAL_UNITS_MASK & this.xyzt_units) + ", " + this.getUnitsCodeString(nifti.NIFTI1.TEMPORAL_UNITS_MASK & this.xyzt_units) + ")\n"); - string += ("Display Range: Max = " + fmt(this.cal_max) + " Min = " + fmt(this.cal_min) + "\n"); - string += ("Slice Duration = " + this.slice_duration + "\n"); - string += ("Time Axis Shift = " + this.toffset + "\n"); - string += ("Description: \"" + this.description + "\"\n"); - string += ("Auxiliary File: \"" + this.aux_file + "\"\n"); - string += ("Q-Form Code = " + this.qform_code + " (" + this.getTransformCodeString(this.qform_code) + ")\n"); - string += ("S-Form Code = " + this.sform_code + " (" + this.getTransformCodeString(this.sform_code) + ")\n"); - string += ("Quaternion Parameters: " + - "b = " + fmt(this.quatern_b) + " " + - "c = " + fmt(this.quatern_c) + " " + - "d = " + fmt(this.quatern_d) + "\n"); - - string += ("Quaternion Offsets: " + - "x = " + this.qoffset_x + " " + - "y = " + this.qoffset_y + " " + - "z = " + this.qoffset_z + "\n"); - - string += ("S-Form Parameters X: " + - fmt(this.affine[0][0]) + ", " + - fmt(this.affine[0][1]) + ", " + - fmt(this.affine[0][2]) + ", " + - fmt(this.affine[0][3]) + "\n"); - - string += ("S-Form Parameters Y: " + - fmt(this.affine[1][0]) + ", " + - fmt(this.affine[1][1]) + ", " + - fmt(this.affine[1][2]) + ", " + - fmt(this.affine[1][3]) + "\n"); - - string += ("S-Form Parameters Z: " + - fmt(this.affine[2][0]) + ", " + - fmt(this.affine[2][1]) + ", " + - fmt(this.affine[2][2]) + ", " + - fmt(this.affine[2][3]) + "\n"); - - string += ("Intent Name: \"" + this.intent_name + "\"\n"); - - if (this.extensionFlag[0]) { - string += ("Extension: Size = " + this.extensionSize + " Code = " + this.extensionCode + "\n"); - - } - - return string; -}; - - -/** - * Returns a human-readable string of datatype. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI1.prototype.getDatatypeCodeString = function (code) { - if (code === nifti.NIFTI1.TYPE_UINT8) { - return "1-Byte Unsigned Integer"; - } else if (code === nifti.NIFTI1.TYPE_INT16) { - return "2-Byte Signed Integer"; - } else if (code === nifti.NIFTI1.TYPE_INT32) { - return "4-Byte Signed Integer"; - } else if (code === nifti.NIFTI1.TYPE_FLOAT32) { - return "4-Byte Float"; - } else if (code === nifti.NIFTI1.TYPE_FLOAT64) { - return "8-Byte Float"; - } else if (code === nifti.NIFTI1.TYPE_RGB24) { - return "RGB"; - } else if (code === nifti.NIFTI1.TYPE_INT8) { - return "1-Byte Signed Integer"; - } else if (code === nifti.NIFTI1.TYPE_UINT16) { - return "2-Byte Unsigned Integer"; - } else if (code === nifti.NIFTI1.TYPE_UINT32) { - return "4-Byte Unsigned Integer"; - } else if (code === nifti.NIFTI1.TYPE_INT64) { - return "8-Byte Signed Integer"; - } else if (code === nifti.NIFTI1.TYPE_UINT64) { - return "8-Byte Unsigned Integer"; - } else { - return "Unknown"; - } -}; - - -/** - * Returns a human-readable string of transform type. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI1.prototype.getTransformCodeString = function (code) { - if (code === nifti.NIFTI1.XFORM_SCANNER_ANAT) { - return "Scanner"; - } else if (code === nifti.NIFTI1.XFORM_ALIGNED_ANAT) { - return "Aligned"; - } else if (code === nifti.NIFTI1.XFORM_TALAIRACH) { - return "Talairach"; - } else if (code === nifti.NIFTI1.XFORM_MNI_152) { - return "MNI"; - } else { - return "Unknown"; - } -}; - - -/** - * Returns a human-readable string of spatial and temporal units. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI1.prototype.getUnitsCodeString = function (code) { - if (code === nifti.NIFTI1.UNITS_METER) { - return "Meters"; - } else if (code === nifti.NIFTI1.UNITS_MM) { - return "Millimeters"; - } else if (code === nifti.NIFTI1.UNITS_MICRON) { - return "Microns"; - } else if (code === nifti.NIFTI1.UNITS_SEC) { - return "Seconds"; - } else if (code === nifti.NIFTI1.UNITS_MSEC) { - return "Milliseconds"; - } else if (code === nifti.NIFTI1.UNITS_USEC) { - return "Microseconds"; - } else if (code === nifti.NIFTI1.UNITS_HZ) { - return "Hz"; - } else if (code === nifti.NIFTI1.UNITS_PPM) { - return "PPM"; - } else if (code === nifti.NIFTI1.UNITS_RADS) { - return "Rads"; - } else { - return "Unknown"; - } -}; - - -/** - * Returns the qform matrix. - * @returns {Array.>} - */ -nifti.NIFTI1.prototype.getQformMat = function () { - return this.convertNiftiQFormToNiftiSForm(this.quatern_b, this.quatern_c, this.quatern_d, this.qoffset_x, - this.qoffset_y, this.qoffset_z, this.pixDims[1], this.pixDims[2], this.pixDims[3], this.pixDims[0]); -}; - - - -/** - * Converts qform to an affine. (See http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c) - * @param {number} qb - * @param {number} qc - * @param {number} qd - * @param {number} qx - * @param {number} qy - * @param {number} qz - * @param {number} dx - * @param {number} dy - * @param {number} dz - * @param {number} qfac - * @returns {Array.>} - */ -nifti.NIFTI1.prototype.convertNiftiQFormToNiftiSForm = function (qb, qc, qd, qx, qy, qz, dx, dy, dz, - qfac) { - var R = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], - a, - b = qb, - c = qc, - d = qd, - xd, - yd, - zd; - - // last row is always [ 0 0 0 1 ] - R[3][0] = R[3][1] = R[3][2] = 0.0; - R[3][3] = 1.0; - - // compute a parameter from b,c,d - a = 1.0 - (b * b + c * c + d * d); - if (a < 0.0000001) { /* special case */ - - a = 1.0 / Math.sqrt(b * b + c * c + d * d); - b *= a; - c *= a; - d *= a; /* normalize (b,c,d) vector */ - a = 0.0; /* a = 0 ==> 180 degree rotation */ - } else { - - a = Math.sqrt(a); /* angle = 2*arccos(a) */ - } - - // load rotation matrix, including scaling factors for voxel sizes - xd = (dx > 0.0) ? dx : 1.0; /* make sure are positive */ - yd = (dy > 0.0) ? dy : 1.0; - zd = (dz > 0.0) ? dz : 1.0; - - if (qfac < 0.0) { - zd = -zd; /* left handedness? */ - } - - R[0][0] = (a * a + b * b - c * c - d * d) * xd; - R[0][1] = 2.0 * (b * c - a * d) * yd; - R[0][2] = 2.0 * (b * d + a * c) * zd; - R[1][0] = 2.0 * (b * c + a * d) * xd; - R[1][1] = (a * a + c * c - b * b - d * d) * yd; - R[1][2] = 2.0 * (c * d - a * b) * zd; - R[2][0] = 2.0 * (b * d - a * c) * xd; - R[2][1] = 2.0 * (c * d + a * b) * yd; - R[2][2] = (a * a + d * d - c * c - b * b) * zd; - - // load offsets - R[0][3] = qx; - R[1][3] = qy; - R[2][3] = qz; - - return R; -}; - - - -/** - * Converts sform to an orientation string (e.g., XYZ+--). (See http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c) - * @param {Array.>} R - * @returns {string} - */ -nifti.NIFTI1.prototype.convertNiftiSFormToNEMA = function (R) { - var xi, xj, xk, yi, yj, yk, zi, zj, zk, val, detQ, detP, i, j, k, p, q, r, ibest, jbest, kbest, pbest, qbest, rbest, - M, vbest, Q, P, iChar, jChar, kChar, iSense, jSense, kSense; - k = 0; - - Q = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; - P = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; - - //if( icod == NULL || jcod == NULL || kcod == NULL ) return ; /* bad */ - - //*icod = *jcod = *kcod = 0 ; /* this.errorMessage returns, if sh*t happens */ - - /* load column vectors for each (i,j,k) direction from matrix */ - - /*-- i axis --*/ /*-- j axis --*/ /*-- k axis --*/ - - xi = R[0][0]; - xj = R[0][1]; - xk = R[0][2]; - - yi = R[1][0]; - yj = R[1][1]; - yk = R[1][2]; - - zi = R[2][0]; - zj = R[2][1]; - zk = R[2][2]; - - /* normalize column vectors to get unit vectors along each ijk-axis */ - - /* normalize i axis */ - val = Math.sqrt(xi * xi + yi * yi + zi * zi); - if (val === 0.0) { /* stupid input */ - return null; - } - - xi /= val; - yi /= val; - zi /= val; - - /* normalize j axis */ - val = Math.sqrt(xj * xj + yj * yj + zj * zj); - if (val === 0.0) { /* stupid input */ - return null; - } - - xj /= val; - yj /= val; - zj /= val; - - /* orthogonalize j axis to i axis, if needed */ - val = xi * xj + yi * yj + zi * zj; /* dot product between i and j */ - if (Math.abs(val) > 1.E-4) { - xj -= val * xi; - yj -= val * yi; - zj -= val * zi; - val = Math.sqrt(xj * xj + yj * yj + zj * zj); /* must renormalize */ - if (val === 0.0) { /* j was parallel to i? */ - return null; - } - xj /= val; - yj /= val; - zj /= val; - } - - /* normalize k axis; if it is zero, make it the cross product i x j */ - val = Math.sqrt(xk * xk + yk * yk + zk * zk); - if (val === 0.0) { - xk = yi * zj - zi * yj; - yk = zi * xj - zj * xi; - zk = xi * yj - yi * xj; - } else { - xk /= val; - yk /= val; - zk /= val; - } - - /* orthogonalize k to i */ - val = xi * xk + yi * yk + zi * zk; /* dot product between i and k */ - if (Math.abs(val) > 1.E-4) { - xk -= val * xi; - yk -= val * yi; - zk -= val * zi; - val = Math.sqrt(xk * xk + yk * yk + zk * zk); - if (val === 0.0) { /* bad */ - return null; - } - xk /= val; - yk /= val; - zk /= val; - } - - /* orthogonalize k to j */ - val = xj * xk + yj * yk + zj * zk; /* dot product between j and k */ - if (Math.abs(val) > 1.e-4) { - xk -= val * xj; - yk -= val * yj; - zk -= val * zj; - val = Math.sqrt(xk * xk + yk * yk + zk * zk); - if (val === 0.0) { /* bad */ - return null; - } - xk /= val; - yk /= val; - zk /= val; - } - - Q[0][0] = xi; - Q[0][1] = xj; - Q[0][2] = xk; - Q[1][0] = yi; - Q[1][1] = yj; - Q[1][2] = yk; - Q[2][0] = zi; - Q[2][1] = zj; - Q[2][2] = zk; - - /* at this point, Q is the rotation matrix from the (i,j,k) to (x,y,z) axes */ - - detQ = this.nifti_mat33_determ(Q); - if (detQ === 0.0) { /* shouldn't happen unless user is a DUFIS */ - return null; - } - - /* Build and test all possible +1/-1 coordinate permutation matrices P; - then find the P such that the rotation matrix M=PQ is closest to the - identity, in the sense of M having the smallest total rotation angle. */ - - /* Despite the formidable looking 6 nested loops, there are - only 3*3*3*2*2*2 = 216 passes, which will run very quickly. */ - - vbest = -666.0; - ibest = pbest = qbest = rbest = 1; - jbest = 2; - kbest = 3; - - for (i = 1; i <= 3; i += 1) { /* i = column number to use for row #1 */ - for (j = 1; j <= 3; j += 1) { /* j = column number to use for row #2 */ - if (i !== j) { - for (k = 1; k <= 3; k += 1) { /* k = column number to use for row #3 */ - if (!(i === k || j === k)) { - P[0][0] = P[0][1] = P[0][2] = P[1][0] = P[1][1] = P[1][2] = P[2][0] = P[2][1] = P[2][2] = 0.0; - for (p = -1; p <= 1; p += 2) { /* p,q,r are -1 or +1 */ - for (q = -1; q <= 1; q += 2) { /* and go into rows #1,2,3 */ - for (r = -1; r <= 1; r += 2) { - P[0][i - 1] = p; - P[1][j - 1] = q; - P[2][k - 1] = r; - detP = this.nifti_mat33_determ(P); /* sign of permutation */ - if ((detP * detQ) > 0.0) { - M = this.nifti_mat33_mul(P, Q); - - /* angle of M rotation = 2.0*acos(0.5*sqrt(1.0+trace(M))) */ - /* we want largest trace(M) == smallest angle == M nearest to I */ - - val = M[0][0] + M[1][1] + M[2][2]; /* trace */ - if (val > vbest) { - vbest = val; - ibest = i; - jbest = j; - kbest = k; - pbest = p; - qbest = q; - rbest = r; - } - } /* doesn't match sign of Q */ - } - } - } - } - } - } - } - } - - /* At this point ibest is 1 or 2 or 3; pbest is -1 or +1; etc. - - The matrix P that corresponds is the best permutation approximation - to Q-inverse; that is, P (approximately) takes (x,y,z) coordinates - to the (i,j,k) axes. - - For example, the first row of P (which contains pbest in column ibest) - determines the way the i axis points relative to the anatomical - (x,y,z) axes. If ibest is 2, then the i axis is along the y axis, - which is direction P2A (if pbest > 0) or A2P (if pbest < 0). - - So, using ibest and pbest, we can assign the output code for - the i axis. Mutatis mutandis for the j and k axes, of course. */ - - iChar = jChar = kChar = iSense = jSense = kSense = 0; - - switch (ibest * pbest) { - case 1: /*i = NIFTI_L2R*/ - iChar = 'X'; - iSense = '+'; - break; - case -1: /*i = NIFTI_R2L*/ - iChar = 'X'; - iSense = '-'; - break; - case 2: /*i = NIFTI_P2A*/ - iChar = 'Y'; - iSense = '+'; - break; - case -2: /*i = NIFTI_A2P*/ - iChar = 'Y'; - iSense = '-'; - break; - case 3: /*i = NIFTI_I2S*/ - iChar = 'Z'; - iSense = '+'; - break; - case -3: /*i = NIFTI_S2I*/ - iChar = 'Z'; - iSense = '-'; - break; - } - - switch (jbest * qbest) { - case 1: /*j = NIFTI_L2R*/ - jChar = 'X'; - jSense = '+'; - break; - case -1: /*j = NIFTI_R2L*/ - jChar = 'X'; - jSense = '-'; - break; - case 2: /*j = NIFTI_P2A*/ - jChar = 'Y'; - jSense = '+'; - break; - case -2: /*j = NIFTI_A2P*/ - jChar = 'Y'; - jSense = '-'; - break; - case 3: /*j = NIFTI_I2S*/ - jChar = 'Z'; - jSense = '+'; - break; - case -3: /*j = NIFTI_S2I*/ - jChar = 'Z'; - jSense = '-'; - break; - } - - switch (kbest * rbest) { - case 1: /*k = NIFTI_L2R*/ - kChar = 'X'; - kSense = '+'; - break; - case -1: /*k = NIFTI_R2L*/ - kChar = 'X'; - kSense = '-'; - break; - case 2: /*k = NIFTI_P2A*/ - kChar = 'Y'; - kSense = '+'; - break; - case -2: /*k = NIFTI_A2P*/ - kChar = 'Y'; - kSense = '-'; - break; - case 3: /*k = NIFTI_I2S*/ - kChar = 'Z'; - kSense = '+'; - break; - case -3: /*k = NIFTI_S2I*/ - kChar = 'Z'; - kSense = '-'; - break; - } - - return (iChar + jChar + kChar + iSense + jSense + kSense); -}; - - - -nifti.NIFTI1.prototype.nifti_mat33_mul = function (A, B) { - var C = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], - i, - j; - - for (i = 0; i < 3; i += 1) { - for (j = 0; j < 3; j += 1) { - C[i][j] = A[i][0] * B[0][j] + A[i][1] * B[1][j] + A[i][2] * B[2][j]; - } - } - - return C; -}; - - - -nifti.NIFTI1.prototype.nifti_mat33_determ = function (R) { - var r11, r12, r13, r21, r22, r23, r31, r32, r33; - /* INPUT MATRIX: */ - r11 = R[0][0]; - r12 = R[0][1]; - r13 = R[0][2]; - r21 = R[1][0]; - r22 = R[1][1]; - r23 = R[1][2]; - r31 = R[2][0]; - r32 = R[2][1]; - r33 = R[2][2]; - - return (r11 * r22 * r33 - r11 * r32 * r23 - r21 * r12 * r33 + r21 * r32 * r13 + r31 * r12 * r23 - r31 * r22 * r13); -}; - - -/** - * Returns the byte index of the extension. - * @returns {number} - */ -nifti.NIFTI1.prototype.getExtensionLocation = function() { - return nifti.NIFTI1.MAGIC_COOKIE + 4; -}; - - -/** - * Returns the extension size. - * @param {DataView} data - * @returns {number} - */ -nifti.NIFTI1.prototype.getExtensionSize = function(data) { - return nifti.Utils.getIntAt(data, this.getExtensionLocation(), this.littleEndian); -}; - - - -/** - * Returns the extension code. - * @param {DataView} data - * @returns {number} - */ -nifti.NIFTI1.prototype.getExtensionCode = function(data) { - return nifti.Utils.getIntAt(data, this.getExtensionLocation() + 4, this.littleEndian); -}; - - - -/*** Exports ***/ - -var moduleType = typeof module; -if ((moduleType !== 'undefined') && module.exports) { - module.exports = nifti.NIFTI1; -} - -},{"./utilities.js":20}],19:[function(require,module,exports){ - -/*jslint browser: true, node: true */ -/*global */ - -"use strict"; - -/*** Imports ***/ - -var nifti = nifti || {}; -nifti.Utils = nifti.Utils || ((typeof require !== 'undefined') ? require('./utilities.js') : null); -nifti.NIFTI1 = nifti.NIFTI1 || ((typeof require !== 'undefined') ? require('./nifti1.js') : null); - - -/*** Constructor ***/ - -/** - * The NIFTI2 constructor. - * @constructor - * @property {boolean} littleEndian - * @property {number} dim_info - * @property {number[]} dims - image dimensions - * @property {number} intent_p1 - * @property {number} intent_p2 - * @property {number} intent_p3 - * @property {number} intent_code - * @property {number} datatypeCode - * @property {number} numBitsPerVoxel - * @property {number} slice_start - * @property {number} slice_end - * @property {number} slice_code - * @property {number[]} pixDims - voxel dimensions - * @property {number} vox_offset - * @property {number} scl_slope - * @property {number} scl_inter - * @property {number} xyzt_units - * @property {number} cal_max - * @property {number} cal_min - * @property {number} slice_duration - * @property {number} toffset - * @property {string} description - * @property {string} aux_file - * @property {string} intent_name - * @property {number} qform_code - * @property {number} sform_code - * @property {number} quatern_b - * @property {number} quatern_c - * @property {number} quatern_d - * @property {number} quatern_x - * @property {number} quatern_y - * @property {number} quatern_z - * @property {Array.>} affine - * @property {string} magic - * @property {number[]} extensionFlag - * @type {Function} - */ -nifti.NIFTI2 = nifti.NIFTI2 || function () { - this.littleEndian = false; - this.dim_info = 0; - this.dims = []; - this.intent_p1 = 0; - this.intent_p2 = 0; - this.intent_p3 = 0; - this.intent_code = 0; - this.datatypeCode = 0; - this.numBitsPerVoxel = 0; - this.slice_start = 0; - this.slice_end = 0; - this.slice_code = 0; - this.pixDims = []; - this.vox_offset = 0; - this.scl_slope = 1; - this.scl_inter = 0; - this.xyzt_units = 0; - this.cal_max = 0; - this.cal_min = 0; - this.slice_duration = 0; - this.toffset = 0; - this.description = ""; - this.aux_file = ""; - this.intent_name = ""; - this.qform_code = 0; - this.sform_code = 0; - this.quatern_b = 0; - this.quatern_c = 0; - this.quatern_d = 0; - this.qoffset_x = 0; - this.qoffset_y = 0; - this.qoffset_z = 0; - this.affine = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; - this.magic = 0; - this.extensionFlag = [0, 0, 0, 0]; -}; - - - -/*** Static Pseudo-constants ***/ - -nifti.NIFTI2.MAGIC_COOKIE = 540; -nifti.NIFTI2.MAGIC_NUMBER_LOCATION = 4; -nifti.NIFTI2.MAGIC_NUMBER = [0x6E, 0x2B, 0x32, 0, 0x0D, 0x0A, 0x1A, 0x0A]; // n+2\0 - - - -/*** Prototype Methods ***/ - -/** - * Reads the header data. - * @param {ArrayBuffer} data - */ -nifti.NIFTI2.prototype.readHeader = function (data) { - var rawData = new DataView(data), - magicCookieVal = nifti.Utils.getIntAt(rawData, 0, this.littleEndian), - ctr, - ctrOut, - ctrIn, - index, - array; - - if (magicCookieVal !== nifti.NIFTI2.MAGIC_COOKIE) { // try as little endian - this.littleEndian = true; - magicCookieVal = nifti.Utils.getIntAt(rawData, 0, this.littleEndian); - } - - if (magicCookieVal !== nifti.NIFTI2.MAGIC_COOKIE) { - throw new Error("This does not appear to be a NIFTI file!"); - } - - this.datatypeCode = nifti.Utils.getShortAt(rawData, 12, this.littleEndian); - this.numBitsPerVoxel = nifti.Utils.getShortAt(rawData, 14, this.littleEndian); - - for (ctr = 0; ctr < 8; ctr += 1) { - index = 16 + (ctr * 8); - this.dims[ctr] = nifti.Utils.getLongAt(rawData, index, this.littleEndian); - } - - this.intent_p1 = nifti.Utils.getDoubleAt(rawData, 80, this.littleEndian); - this.intent_p2 = nifti.Utils.getDoubleAt(rawData, 88, this.littleEndian); - this.intent_p3 = nifti.Utils.getDoubleAt(rawData, 96, this.littleEndian); - - for (ctr = 0; ctr < 8; ctr += 1) { - index = 104 + (ctr * 8); - this.pixDims[ctr] = nifti.Utils.getDoubleAt(rawData, index, this.littleEndian); - } - - this.vox_offset = nifti.Utils.getLongAt(rawData, 168, this.littleEndian); - - this.scl_slope = nifti.Utils.getDoubleAt(rawData, 176, this.littleEndian); - this.scl_inter = nifti.Utils.getDoubleAt(rawData, 184, this.littleEndian); - - this.cal_max = nifti.Utils.getDoubleAt(rawData, 192, this.littleEndian); - this.cal_min = nifti.Utils.getDoubleAt(rawData, 200, this.littleEndian); - - this.slice_duration = nifti.Utils.getDoubleAt(rawData, 208, this.littleEndian); - - this.toffset = nifti.Utils.getDoubleAt(rawData, 216, this.littleEndian); - - this.slice_start = nifti.Utils.getLongAt(rawData, 224, this.littleEndian); - this.slice_end = nifti.Utils.getLongAt(rawData, 232, this.littleEndian); - - this.description = nifti.Utils.getStringAt(rawData, 240, 240 + 80); - this.aux_file = nifti.Utils.getStringAt(rawData, 320, 320 + 24); - - this.qform_code = nifti.Utils.getIntAt(rawData, 344, this.littleEndian); - this.sform_code = nifti.Utils.getIntAt(rawData, 348, this.littleEndian); - - this.quatern_b = nifti.Utils.getDoubleAt(rawData, 352, this.littleEndian); - this.quatern_c = nifti.Utils.getDoubleAt(rawData, 360, this.littleEndian); - this.quatern_d = nifti.Utils.getDoubleAt(rawData, 368, this.littleEndian); - this.qoffset_x = nifti.Utils.getDoubleAt(rawData, 376, this.littleEndian); - this.qoffset_y = nifti.Utils.getDoubleAt(rawData, 384, this.littleEndian); - this.qoffset_z = nifti.Utils.getDoubleAt(rawData, 392, this.littleEndian); - - for (ctrOut = 0; ctrOut < 3; ctrOut += 1) { - for (ctrIn = 0; ctrIn < 4; ctrIn += 1) { - index = 400 + (((ctrOut * 4) + ctrIn) * 8); - this.affine[ctrOut][ctrIn] = nifti.Utils.getDoubleAt(rawData, index, this.littleEndian); - } - } - - this.affine[3][0] = 0; - this.affine[3][1] = 0; - this.affine[3][2] = 0; - this.affine[3][3] = 1; - - this.slice_code = nifti.Utils.getIntAt(rawData, 496, this.littleEndian); - this.xyzt_units = nifti.Utils.getIntAt(rawData, 500, this.littleEndian); - this.intent_code = nifti.Utils.getIntAt(rawData, 504, this.littleEndian); - this.intent_name = nifti.Utils.getStringAt(rawData, 508, 508 + 16); - - this.dim_info = nifti.Utils.getByteAt(rawData, 524); - - if (rawData.byteLength > nifti.NIFTI2.MAGIC_COOKIE) { - this.extensionFlag[0] = nifti.Utils.getByteAt(rawData, 540); - this.extensionFlag[1] = nifti.Utils.getByteAt(rawData, 540 + 1); - this.extensionFlag[2] = nifti.Utils.getByteAt(rawData, 540 + 2); - this.extensionFlag[3] = nifti.Utils.getByteAt(rawData, 540 + 3); - - if (this.extensionFlag[0]) { - this.extensionSize = this.getExtensionSize(rawData); - this.extensionCode = this.getExtensionCode(rawData); - } - } -}; - - - -/** - * Returns a formatted string of header fields. - * @returns {string} - */ -nifti.NIFTI2.prototype.toFormattedString = function () { - var fmt = nifti.Utils.formatNumber, - string = ""; - - string += ("Datatype = " + + this.datatypeCode + " (" + this.getDatatypeCodeString(this.datatypeCode) + ")\n"); - string += ("Bits Per Voxel = " + " = " + this.numBitsPerVoxel + "\n"); - string += ("Image Dimensions" + " (1-8): " + - this.dims[0] + ", " + - this.dims[1] + ", " + - this.dims[2] + ", " + - this.dims[3] + ", " + - this.dims[4] + ", " + - this.dims[5] + ", " + - this.dims[6] + ", " + - this.dims[7] + "\n"); - - string += ("Intent Parameters (1-3): " + - this.intent_p1 + ", " + - this.intent_p2 + ", " + - this.intent_p3) + "\n"; - - string += ("Voxel Dimensions (1-8): " + - fmt(this.pixDims[0]) + ", " + - fmt(this.pixDims[1]) + ", " + - fmt(this.pixDims[2]) + ", " + - fmt(this.pixDims[3]) + ", " + - fmt(this.pixDims[4]) + ", " + - fmt(this.pixDims[5]) + ", " + - fmt(this.pixDims[6]) + ", " + - fmt(this.pixDims[7]) + "\n"); - - string += ("Image Offset = " + this.vox_offset + "\n"); - string += ("Data Scale: Slope = " + fmt(this.scl_slope) + " Intercept = " + fmt(this.scl_inter) + "\n"); - string += ("Display Range: Max = " + fmt(this.cal_max) + " Min = " + fmt(this.cal_min) + "\n"); - string += ("Slice Duration = " + this.slice_duration + "\n"); - string += ("Time Axis Shift = " + this.toffset + "\n"); - string += ("Slice Start = " + this.slice_start + "\n"); - string += ("Slice End = " + this.slice_end + "\n"); - string += ("Description: \"" + this.description + "\"\n"); - string += ("Auxiliary File: \"" + this.aux_file + "\"\n"); - string += ("Q-Form Code = " + this.qform_code + " (" + this.getTransformCodeString(this.qform_code) + ")\n"); - string += ("S-Form Code = " + this.sform_code + " (" + this.getTransformCodeString(this.sform_code) + ")\n"); - string += ("Quaternion Parameters: " + - "b = " + fmt(this.quatern_b) + " " + - "c = " + fmt(this.quatern_c) + " " + - "d = " + fmt(this.quatern_d) + "\n"); - - string += ("Quaternion Offsets: " + - "x = " + this.qoffset_x + " " + - "y = " + this.qoffset_y + " " + - "z = " + this.qoffset_z + "\n"); - - string += ("S-Form Parameters X: " + - fmt(this.affine[0][0]) + ", " + - fmt(this.affine[0][1]) + ", " + - fmt(this.affine[0][2]) + ", " + - fmt(this.affine[0][3]) + "\n"); - - string += ("S-Form Parameters Y: " + - fmt(this.affine[1][0]) + ", " + - fmt(this.affine[1][1]) + ", " + - fmt(this.affine[1][2]) + ", " + - fmt(this.affine[1][3]) + "\n"); - - string += ("S-Form Parameters Z: " + - fmt(this.affine[2][0]) + ", " + - fmt(this.affine[2][1]) + ", " + - fmt(this.affine[2][2]) + ", " + - fmt(this.affine[2][3]) + "\n"); - - string += ("Slice Code = " + this.slice_code + "\n"); - string += ("Units Code = " + this.xyzt_units + " (" + this.getUnitsCodeString(nifti.NIFTI1.SPATIAL_UNITS_MASK & this.xyzt_units) + ", " + this.getUnitsCodeString(nifti.NIFTI1.TEMPORAL_UNITS_MASK & this.xyzt_units) + ")\n"); - string += ("Intent Code = " + this.intent_code + "\n"); - string += ("Intent Name: \"" + this.intent_name + "\"\n"); - - string += ("Dim Info = " + this.dim_info + "\n"); - - return string; -}; - - - -/** - * Returns the byte index of the extension. - * @returns {number} - */ -nifti.NIFTI2.prototype.getExtensionLocation = function() { - return nifti.NIFTI2.MAGIC_COOKIE + 4; -}; - - - -/** - * Returns the extension size. - * @param {DataView} data - * @returns {number} - */ -nifti.NIFTI2.prototype.getExtensionSize = nifti.NIFTI1.prototype.getExtensionSize; - - - -/** - * Returns the extension code. - * @param {DataView} data - * @returns {number} - */ -nifti.NIFTI2.prototype.getExtensionCode = nifti.NIFTI1.prototype.getExtensionCode; - - - -/** - * Returns a human-readable string of datatype. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI2.prototype.getDatatypeCodeString = nifti.NIFTI1.prototype.getDatatypeCodeString; - - - -/** - * Returns a human-readable string of transform type. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI2.prototype.getTransformCodeString = nifti.NIFTI1.prototype.getTransformCodeString; - - - -/** - * Returns a human-readable string of spatial and temporal units. - * @param {number} code - * @returns {string} - */ -nifti.NIFTI2.prototype.getUnitsCodeString = nifti.NIFTI1.prototype.getUnitsCodeString; - - - -/** - * Returns the qform matrix. - * @returns {Array.>} - */ -nifti.NIFTI2.prototype.getQformMat = nifti.NIFTI1.prototype.getQformMat; - - - -/** - * Converts qform to an affine. (See http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c) - * @param {number} qb - * @param {number} qc - * @param {number} qd - * @param {number} qx - * @param {number} qy - * @param {number} qz - * @param {number} dx - * @param {number} dy - * @param {number} dz - * @param {number} qfac - * @returns {Array.>} - */ -nifti.NIFTI2.prototype.convertNiftiQFormToNiftiSForm = nifti.NIFTI1.prototype.convertNiftiQFormToNiftiSForm; - - - -/** - * Converts sform to an orientation string (e.g., XYZ+--). (See http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c) - * @param {Array.>} R - * @returns {string} - */ -nifti.NIFTI2.prototype.convertNiftiSFormToNEMA = nifti.NIFTI1.prototype.convertNiftiSFormToNEMA; - - - -nifti.NIFTI2.prototype.nifti_mat33_mul = nifti.NIFTI1.prototype.nifti_mat33_mul; - - - -nifti.NIFTI2.prototype.nifti_mat33_determ = nifti.NIFTI1.prototype.nifti_mat33_determ; - - - -/*** Exports ***/ - -var moduleType = typeof module; -if ((moduleType !== 'undefined') && module.exports) { - module.exports = nifti.NIFTI2; -} - -},{"./nifti1.js":18,"./utilities.js":20}],20:[function(require,module,exports){ - -/*jslint browser: true, node: true */ -/*global require, module */ - -"use strict"; - -/*** Imports ***/ - -var nifti = nifti || {}; -nifti.Utils = nifti.Utils || {}; - - - -/*** Static Pseudo-constants ***/ - -nifti.Utils.crcTable = null; -nifti.Utils.GUNZIP_MAGIC_COOKIE1 = 31; -nifti.Utils.GUNZIP_MAGIC_COOKIE2 = 139; - - - -/*** Static methods ***/ - -nifti.Utils.getStringAt = function (data, start, end) { - var str = "", ctr, ch; - - for (ctr = start; ctr < end; ctr += 1) { - ch = data.getUint8(ctr); - - if (ch !== 0) { - str += String.fromCharCode(ch); - } - } - - return str; -}; - - - -nifti.Utils.getByteAt = function (data, start) { - return data.getInt8(start); -}; - - - -nifti.Utils.getShortAt = function (data, start, littleEndian) { - return data.getInt16(start, littleEndian); -}; - - - -nifti.Utils.getIntAt = function (data, start, littleEndian) { - return data.getInt32(start, littleEndian); -}; - - - -nifti.Utils.getFloatAt = function (data, start, littleEndian) { - return data.getFloat32(start, littleEndian); -}; - - - -nifti.Utils.getDoubleAt = function (data, start, littleEndian) { - return data.getFloat64(start, littleEndian); -}; - - - -nifti.Utils.getLongAt = function (data, start, littleEndian) { - var ctr, array = [], value = 0; - - for (ctr = 0; ctr < 8; ctr += 1) { - array[ctr] = nifti.Utils.getByteAt(data, start + ctr, littleEndian); - } - - for (ctr = array.length - 1; ctr >= 0; ctr--) { - value = (value * 256) + array[ctr]; - } - - return value; -}; - - - -nifti.Utils.toArrayBuffer = function (buffer) { - var ab, view, i; - - ab = new ArrayBuffer(buffer.length); - view = new Uint8Array(ab); - for (i = 0; i < buffer.length; i += 1) { - view[i] = buffer[i]; - } - return ab; -}; - - - -nifti.Utils.isString = function (obj) { - return (typeof obj === "string" || obj instanceof String); -}; - - -nifti.Utils.formatNumber = function (num, shortFormat) { - var val = 0; - - if (nifti.Utils.isString(num)) { - val = Number(num); - } else { - val = num; - } - - if (shortFormat) { - val = val.toPrecision(5); - } else { - val = val.toPrecision(7); - } - - return parseFloat(val); -}; - - - -// http://stackoverflow.com/questions/18638900/javascript-crc32 -nifti.Utils.makeCRCTable = function(){ - var c; - var crcTable = []; - for(var n =0; n < 256; n++){ - c = n; - for(var k =0; k < 8; k++){ - c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - crcTable[n] = c; - } - return crcTable; -}; - - - -nifti.Utils.crc32 = function(dataView) { - var crcTable = nifti.Utils.crcTable || (nifti.Utils.crcTable = nifti.Utils.makeCRCTable()); - var crc = 0 ^ (-1); - - for (var i = 0; i < dataView.byteLength; i++ ) { - crc = (crc >>> 8) ^ crcTable[(crc ^ dataView.getUint8(i)) & 0xFF]; - } - - return (crc ^ (-1)) >>> 0; -}; - - - -/*** Exports ***/ - -var moduleType = typeof module; -if ((moduleType !== 'undefined') && module.exports) { - module.exports = nifti.Utils; -} - -},{}]},{},[17])(17) -}); \ No newline at end of file diff --git a/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/group1-shard1of1.bin b/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/group1-shard1of1.bin deleted file mode 100644 index cc2e568..0000000 Binary files a/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/group1-shard1of1.bin and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/model2.json b/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/model2.json deleted file mode 100644 index ab072c0..0000000 --- a/v3/experimental_mode/CanvasVersion/meshnet_dropout/mnm_dropout/model2.json +++ /dev/null @@ -1,766 +0,0 @@ -{ - "format": "layers-model", - "generatedBy": "keras v2.5.0", - "convertedBy": "TensorFlow.js Converter v3.7.0", - "modelTopology": { - "keras_version": "2.5.0", - "backend": "tensorflow", - "model_config": { - "class_name": "Functional", - "config": { - "name": "model", - "layers": [ - { - "class_name": "InputLayer", - "config": { - "batch_input_shape": [ - null, - 38, - 38, - 38, - 1 - ], - "dtype": "float32", - "sparse": false, - "ragged": false, - "name": "input" - }, - "name": "input", - "inbound_nodes": [] - }, - { - "class_name": "Conv3D", - "config": { - "name": "17", - "trainable": false, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "17", - "inbound_nodes": [ - [ - [ - "input", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "18", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "18", - "inbound_nodes": [ - [ - [ - "17", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "19", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "19", - "inbound_nodes": [ - [ - [ - "18", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "20", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "20", - "inbound_nodes": [ - [ - [ - "19", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "21", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "21", - "inbound_nodes": [ - [ - [ - "20", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "22", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "22", - "inbound_nodes": [ - [ - [ - "21", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "23", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 2, - 2, - 2 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "23", - "inbound_nodes": [ - [ - [ - "22", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "24", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "24", - "inbound_nodes": [ - [ - [ - "23", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "25", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 4, - 4, - 4 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "25", - "inbound_nodes": [ - [ - [ - "24", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "26", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "26", - "inbound_nodes": [ - [ - [ - "25", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "27", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 8, - 8, - 8 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "27", - "inbound_nodes": [ - [ - [ - "26", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "28", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "28", - "inbound_nodes": [ - [ - [ - "27", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "29", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "29", - "inbound_nodes": [ - [ - [ - "28", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "30", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "30", - "inbound_nodes": [ - [ - [ - "29", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "output", - "trainable": true, - "dtype": "float32", - "filters": 3, - "kernel_size": [ - 1, - 1, - 1 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "output", - "inbound_nodes": [ - [ - [ - "30", - 0, - 0, - {} - ] - ] - ] - } - ], - "input_layers": [ - [ - "input", - 0, - 0 - ] - ], - "output_layers": [ - [ - "output", - 0, - 0 - ] - ] - } - } - }, - "weightsManifest": [ - { - "paths": [ - "group1-shard1of1.bin" - ], - "weights": [ - { - "name": "17/kernel", - "shape": [ - 3, - 3, - 3, - 1, - 21 - ], - "dtype": "float32" - }, - { - "name": "17/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "19/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "19/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "21/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "21/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "23/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "23/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "25/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "25/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "27/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "27/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "29/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "29/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "output/kernel", - "shape": [ - 1, - 1, - 1, - 21, - 3 - ], - "dtype": "float32" - }, - { - "name": "output/bias", - "shape": [ - 3 - ], - "dtype": "float32" - } - ] - } - ] -} diff --git a/v3/experimental_mode/CanvasVersion/mnm_dropout/group1-shard1of1.bin b/v3/experimental_mode/CanvasVersion/mnm_dropout/group1-shard1of1.bin deleted file mode 100644 index cc2e568..0000000 Binary files a/v3/experimental_mode/CanvasVersion/mnm_dropout/group1-shard1of1.bin and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/mnm_dropout/model.json b/v3/experimental_mode/CanvasVersion/mnm_dropout/model.json deleted file mode 100644 index ab072c0..0000000 --- a/v3/experimental_mode/CanvasVersion/mnm_dropout/model.json +++ /dev/null @@ -1,766 +0,0 @@ -{ - "format": "layers-model", - "generatedBy": "keras v2.5.0", - "convertedBy": "TensorFlow.js Converter v3.7.0", - "modelTopology": { - "keras_version": "2.5.0", - "backend": "tensorflow", - "model_config": { - "class_name": "Functional", - "config": { - "name": "model", - "layers": [ - { - "class_name": "InputLayer", - "config": { - "batch_input_shape": [ - null, - 38, - 38, - 38, - 1 - ], - "dtype": "float32", - "sparse": false, - "ragged": false, - "name": "input" - }, - "name": "input", - "inbound_nodes": [] - }, - { - "class_name": "Conv3D", - "config": { - "name": "17", - "trainable": false, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "17", - "inbound_nodes": [ - [ - [ - "input", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "18", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "18", - "inbound_nodes": [ - [ - [ - "17", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "19", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "19", - "inbound_nodes": [ - [ - [ - "18", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "20", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "20", - "inbound_nodes": [ - [ - [ - "19", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "21", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "21", - "inbound_nodes": [ - [ - [ - "20", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "22", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "22", - "inbound_nodes": [ - [ - [ - "21", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "23", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 2, - 2, - 2 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "23", - "inbound_nodes": [ - [ - [ - "22", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "24", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "24", - "inbound_nodes": [ - [ - [ - "23", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "25", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 4, - 4, - 4 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "25", - "inbound_nodes": [ - [ - [ - "24", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "26", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "26", - "inbound_nodes": [ - [ - [ - "25", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "27", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 8, - 8, - 8 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "27", - "inbound_nodes": [ - [ - [ - "26", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "28", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "28", - "inbound_nodes": [ - [ - [ - "27", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "29", - "trainable": true, - "dtype": "float32", - "filters": 21, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "29", - "inbound_nodes": [ - [ - [ - "28", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "30", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "30", - "inbound_nodes": [ - [ - [ - "29", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "output", - "trainable": true, - "dtype": "float32", - "filters": 3, - "kernel_size": [ - 1, - 1, - 1 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "output", - "inbound_nodes": [ - [ - [ - "30", - 0, - 0, - {} - ] - ] - ] - } - ], - "input_layers": [ - [ - "input", - 0, - 0 - ] - ], - "output_layers": [ - [ - "output", - 0, - 0 - ] - ] - } - } - }, - "weightsManifest": [ - { - "paths": [ - "group1-shard1of1.bin" - ], - "weights": [ - { - "name": "17/kernel", - "shape": [ - 3, - 3, - 3, - 1, - 21 - ], - "dtype": "float32" - }, - { - "name": "17/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "19/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "19/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "21/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "21/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "23/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "23/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "25/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "25/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "27/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "27/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "29/kernel", - "shape": [ - 3, - 3, - 3, - 21, - 21 - ], - "dtype": "float32" - }, - { - "name": "29/bias", - "shape": [ - 21 - ], - "dtype": "float32" - }, - { - "name": "output/kernel", - "shape": [ - 1, - 1, - 1, - 21, - 3 - ], - "dtype": "float32" - }, - { - "name": "output/bias", - "shape": [ - 3 - ], - "dtype": "float32" - } - ] - } - ] -} diff --git a/v3/experimental_mode/CanvasVersion/mnm_large/group1-shard1of1.bin b/v3/experimental_mode/CanvasVersion/mnm_large/group1-shard1of1.bin deleted file mode 100644 index ced022c..0000000 Binary files a/v3/experimental_mode/CanvasVersion/mnm_large/group1-shard1of1.bin and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/mnm_large/model.json b/v3/experimental_mode/CanvasVersion/mnm_large/model.json deleted file mode 100644 index 980dc51..0000000 --- a/v3/experimental_mode/CanvasVersion/mnm_large/model.json +++ /dev/null @@ -1,766 +0,0 @@ -{ - "format": "layers-model", - "generatedBy": "keras v2.5.0", - "convertedBy": "TensorFlow.js Converter v3.7.0", - "modelTopology": { - "keras_version": "2.5.0", - "backend": "tensorflow", - "model_config": { - "class_name": "Functional", - "config": { - "name": "model", - "layers": [ - { - "class_name": "InputLayer", - "config": { - "batch_input_shape": [ - null, - 38, - 38, - 38, - 1 - ], - "dtype": "float32", - "sparse": false, - "ragged": false, - "name": "input" - }, - "name": "input", - "inbound_nodes": [] - }, - { - "class_name": "Conv3D", - "config": { - "name": "17", - "trainable": false, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "17", - "inbound_nodes": [ - [ - [ - "input", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "18", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "18", - "inbound_nodes": [ - [ - [ - "17", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "19", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "19", - "inbound_nodes": [ - [ - [ - "18", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "20", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "20", - "inbound_nodes": [ - [ - [ - "19", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "21", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "21", - "inbound_nodes": [ - [ - [ - "20", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "22", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "22", - "inbound_nodes": [ - [ - [ - "21", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "23", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 2, - 2, - 2 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "23", - "inbound_nodes": [ - [ - [ - "22", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "24", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "24", - "inbound_nodes": [ - [ - [ - "23", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "25", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 4, - 4, - 4 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "25", - "inbound_nodes": [ - [ - [ - "24", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "26", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "26", - "inbound_nodes": [ - [ - [ - "25", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "27", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 8, - 8, - 8 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "27", - "inbound_nodes": [ - [ - [ - "26", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "28", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "28", - "inbound_nodes": [ - [ - [ - "27", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "29", - "trainable": true, - "dtype": "float32", - "filters": 71, - "kernel_size": [ - 3, - 3, - 3 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "29", - "inbound_nodes": [ - [ - [ - "28", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Activation", - "config": { - "name": "30", - "trainable": true, - "dtype": "float32", - "activation": "relu" - }, - "name": "30", - "inbound_nodes": [ - [ - [ - "29", - 0, - 0, - {} - ] - ] - ] - }, - { - "class_name": "Conv3D", - "config": { - "name": "output", - "trainable": true, - "dtype": "float32", - "filters": 3, - "kernel_size": [ - 1, - 1, - 1 - ], - "strides": [ - 1, - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1, - 1 - ], - "groups": 1, - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "Zeros", - "config": {} - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "name": "output", - "inbound_nodes": [ - [ - [ - "30", - 0, - 0, - {} - ] - ] - ] - } - ], - "input_layers": [ - [ - "input", - 0, - 0 - ] - ], - "output_layers": [ - [ - "output", - 0, - 0 - ] - ] - } - } - }, - "weightsManifest": [ - { - "paths": [ - "group1-shard1of1.bin" - ], - "weights": [ - { - "name": "17/kernel", - "shape": [ - 3, - 3, - 3, - 1, - 71 - ], - "dtype": "float32" - }, - { - "name": "17/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "19/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "19/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "21/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "21/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "23/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "23/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "25/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "25/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "27/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "27/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "29/kernel", - "shape": [ - 3, - 3, - 3, - 71, - 71 - ], - "dtype": "float32" - }, - { - "name": "29/bias", - "shape": [ - 71 - ], - "dtype": "float32" - }, - { - "name": "output/kernel", - "shape": [ - 1, - 1, - 1, - 71, - 3 - ], - "dtype": "float32" - }, - { - "name": "output/bias", - "shape": [ - 3 - ], - "dtype": "float32" - } - ] - } - ] -} diff --git a/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/group1-shard1of1.bin b/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/group1-shard1of1.bin deleted file mode 100644 index 210906a..0000000 Binary files a/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/group1-shard1of1.bin and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/model.json b/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/model.json deleted file mode 100644 index e288b78..0000000 --- a/v3/experimental_mode/CanvasVersion/mnm_tfjs_filters21/model.json +++ /dev/null @@ -1 +0,0 @@ -{"format": "layers-model", "generatedBy": "keras v2.4.0", "convertedBy": "TensorFlow.js Converter v3.2.0", "modelTopology": {"keras_version": "2.4.0", "backend": "tensorflow", "model_config": {"class_name": "Functional", "config": {"name": "model", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": [null, 38, 38, 38, 1], "dtype": "float32", "sparse": false, "ragged": false, "name": "input"}, "name": "input", "inbound_nodes": []}, {"class_name": "Conv3D", "config": {"name": "17", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "17", "inbound_nodes": [[["input", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "18", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "18", "inbound_nodes": [[["17", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "19", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "19", "inbound_nodes": [[["18", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "20", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "20", "inbound_nodes": [[["19", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "21", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "21", "inbound_nodes": [[["20", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "22", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "22", "inbound_nodes": [[["21", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "23", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [2, 2, 2], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "23", "inbound_nodes": [[["22", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "24", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "24", "inbound_nodes": [[["23", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "25", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [4, 4, 4], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "25", "inbound_nodes": [[["24", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "26", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "26", "inbound_nodes": [[["25", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "27", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [8, 8, 8], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "27", "inbound_nodes": [[["26", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "28", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "28", "inbound_nodes": [[["27", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "29", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "29", "inbound_nodes": [[["28", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "30", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "30", "inbound_nodes": [[["29", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "output", "trainable": true, "dtype": "float32", "filters": 3, "kernel_size": [1, 1, 1], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "output", "inbound_nodes": [[["30", 0, 0, {}]]]}], "input_layers": [["input", 0, 0]], "output_layers": [["output", 0, 0]]}}}, "weightsManifest": [{"paths": ["group1-shard1of1.bin"], "weights": [{"name": "17/kernel", "shape": [3, 3, 3, 1, 21], "dtype": "float32"}, {"name": "17/bias", "shape": [21], "dtype": "float32"}, {"name": "19/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "19/bias", "shape": [21], "dtype": "float32"}, {"name": "21/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "21/bias", "shape": [21], "dtype": "float32"}, {"name": "23/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "23/bias", "shape": [21], "dtype": "float32"}, {"name": "25/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "25/bias", "shape": [21], "dtype": "float32"}, {"name": "27/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "27/bias", "shape": [21], "dtype": "float32"}, {"name": "29/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "29/bias", "shape": [21], "dtype": "float32"}, {"name": "output/kernel", "shape": [1, 1, 1, 21, 3], "dtype": "float32"}, {"name": "output/bias", "shape": [3], "dtype": "float32"}]}]} \ No newline at end of file diff --git a/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/group1-shard1of1.bin b/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/group1-shard1of1.bin deleted file mode 100644 index 210906a..0000000 Binary files a/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/group1-shard1of1.bin and /dev/null differ diff --git a/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/model.json b/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/model.json deleted file mode 100644 index e288b78..0000000 --- a/v3/experimental_mode/CanvasVersion/mnm_tfjs_me_test/model.json +++ /dev/null @@ -1 +0,0 @@ -{"format": "layers-model", "generatedBy": "keras v2.4.0", "convertedBy": "TensorFlow.js Converter v3.2.0", "modelTopology": {"keras_version": "2.4.0", "backend": "tensorflow", "model_config": {"class_name": "Functional", "config": {"name": "model", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": [null, 38, 38, 38, 1], "dtype": "float32", "sparse": false, "ragged": false, "name": "input"}, "name": "input", "inbound_nodes": []}, {"class_name": "Conv3D", "config": {"name": "17", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "17", "inbound_nodes": [[["input", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "18", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "18", "inbound_nodes": [[["17", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "19", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "19", "inbound_nodes": [[["18", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "20", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "20", "inbound_nodes": [[["19", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "21", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "21", "inbound_nodes": [[["20", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "22", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "22", "inbound_nodes": [[["21", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "23", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [2, 2, 2], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "23", "inbound_nodes": [[["22", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "24", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "24", "inbound_nodes": [[["23", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "25", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [4, 4, 4], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "25", "inbound_nodes": [[["24", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "26", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "26", "inbound_nodes": [[["25", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "27", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [8, 8, 8], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "27", "inbound_nodes": [[["26", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "28", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "28", "inbound_nodes": [[["27", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "29", "trainable": true, "dtype": "float32", "filters": 21, "kernel_size": [3, 3, 3], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "29", "inbound_nodes": [[["28", 0, 0, {}]]]}, {"class_name": "Activation", "config": {"name": "30", "trainable": true, "dtype": "float32", "activation": "relu"}, "name": "30", "inbound_nodes": [[["29", 0, 0, {}]]]}, {"class_name": "Conv3D", "config": {"name": "output", "trainable": true, "dtype": "float32", "filters": 3, "kernel_size": [1, 1, 1], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "Zeros", "config": {}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "name": "output", "inbound_nodes": [[["30", 0, 0, {}]]]}], "input_layers": [["input", 0, 0]], "output_layers": [["output", 0, 0]]}}}, "weightsManifest": [{"paths": ["group1-shard1of1.bin"], "weights": [{"name": "17/kernel", "shape": [3, 3, 3, 1, 21], "dtype": "float32"}, {"name": "17/bias", "shape": [21], "dtype": "float32"}, {"name": "19/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "19/bias", "shape": [21], "dtype": "float32"}, {"name": "21/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "21/bias", "shape": [21], "dtype": "float32"}, {"name": "23/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "23/bias", "shape": [21], "dtype": "float32"}, {"name": "25/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "25/bias", "shape": [21], "dtype": "float32"}, {"name": "27/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "27/bias", "shape": [21], "dtype": "float32"}, {"name": "29/kernel", "shape": [3, 3, 3, 21, 21], "dtype": "float32"}, {"name": "29/bias", "shape": [21], "dtype": "float32"}, {"name": "output/kernel", "shape": [1, 1, 1, 21, 3], "dtype": "float32"}, {"name": "output/bias", "shape": [3], "dtype": "float32"}]}]} \ No newline at end of file diff --git a/v3/index.html b/v3/index.html deleted file mode 100644 index c9f14f5..0000000 --- a/v3/index.html +++ /dev/null @@ -1,2355 +0,0 @@ - - - - - - Brainchop - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-
- -
-
-
-
-
- -
-
- - -
- -
-
-
-
- - - -
- -
- - - - - - - - diff --git a/v3/js/brainchop/BCBrain3D.js b/v3/js/brainchop/BCBrain3D.js deleted file mode 100644 index 1c7f56f..0000000 --- a/v3/js/brainchop/BCBrain3D.js +++ /dev/null @@ -1,1097 +0,0 @@ -/* -========================================================= -* Brainchop - v2.0.1 -========================================================= - -* Discription: A user interface for whole brain 3D segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 256, 256, 256, 1] -* Model : Meshnet or similar -* -* Author: Mohamed Masoud (2022) -========================================================= - - - -========================================================= - Brain 3D (ThreeJS) -=========================================================*/ - -/** -* create ThreeJS Renderer -* -* @since 1.4.0 -* @param {string} webixWinId -* @param {string} containerId -* @return {Object} renderer -*/ - -getRenderer = (webixWinId, containerId) => { - let container = document.getElementById(containerId); - const renderer = new THREE.WebGLRenderer(); - renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize($$(webixWinId).config.width, $$(webixWinId).config.height); - renderer.antialias = true; - - container.appendChild(renderer.domElement); - - return renderer; - -} - - -/** -* Get input volume dim -* -* @since 1.4.0 -* @param {Array} vol3dValuesArr, 3D array -* @return {object} -* @example -* -* getVol3dDim( tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ).arraySync() ) -* -* // =>object { "height": 2, "width": 2, "depth": 2 } -* -*/ - -getVol3dDim = (vol3dValuesArr) => { - return tf.tidy(() => { - // Convert to tensor - let vol3dTensor = tf.tensor(vol3dValuesArr); - let height = vol3dTensor.shape[1], width = vol3dTensor.shape[2], depth = vol3dTensor.shape[0]; - vol3dTensor.dispose(); - - return { "height": height, "width": width, "depth": depth }; - }); -} - -/** -* Create ThreeJS camera -* -* @since 1.4.0 -* @param {string} webixWinId -* @parm {number} fovVal -* @parm {number} zDim, distance of Object from camera -* @return {object} camera -* -*/ - -createCamera = (webixWinId, fovVal = 50, zDim = 600) => { - // Create camera - const fov = fovVal; - const aspect = $$(webixWinId).config.width/$$(webixWinId).config.height; - const camera = new THREE.PerspectiveCamera( fov, aspect ); - camera.position.z = zDim; - - return camera; -} - - -/** -* Create scene -* -* @since 1.4.0 -* @param {string/hex} bgColor e.g: 0x050505, "rgb(255, 0, 0)" -* @return {Object} scene -*/ - -createScene = (bgColor = 0x050505) => { - const scene = new THREE.Scene(); - scene.background = new THREE.Color( bgColor ); - scene.fog = new THREE.Fog( bgColor, 2000, 3500 ); - return scene; -} - -/** -* create Oribit Controls -* -* @since 1.4.0 -* @param {object} vol3dDim - e.g. { "height": 256, "width": 256, "depth": 256 }; -* @param {object} camera -* @param {string} renderer -* @param {number} minDist -* @param {number} maxDist -* @return {Object} controls -*/ - -createOrbitControls = (vol3dDim, camera, renderer, minDist = 200, maxDist = 1000 ) => { - const controls = new THREE.OrbitControls( camera, renderer.domElement ); - controls.target.set( vol3dDim.height / 2, vol3dDim.width / 3, vol3dDim.depth / 2 ); - controls.update(); - controls.minDistance = minDist; - controls.maxDistance = maxDist; - - return controls - -} - - -/** -* Init threeJS for drawing the brain 3D -* -* @since 1.3.0 -* @param {Array} vol3dValuesArr- Brain 3D values -* @param {string} webixWinId -* @param {string} containerId -* @param {string} guiContainerId -* @param {string} iconId -* @param {ArrayBuffer} rawNiftiData -* @param {ArrayBuffer} niftiImage -* @param {object} colorLutObj- e.g. {"0": "rgb(0,0,0)", "1": "rgb(245,245,245)", "2": "rgb(196,58,250)", "3": "rgb(220,248,164)", ... } -* @param {object} labelsObj- { "0": "BG", "1": "Cerebral-White-Matter", "2": "Ventricle", "3": "Cerebellum-WM", "4": "Cerebellum",..} -* -*/ - - - - -initInput = (vol3dValuesArr, webixWinId , containerId , guiContainerId, iconId, rawNiftiData, niftiImage, colorLutObj = null, labelsObj = null) => { - - // Create renderer and append it to parent body. - const renderer = getRenderer(webixWinId, containerId); - - // Get volumen Dim - const vol3dDim = getVol3dDim(vol3dValuesArr); - - // Get camera - const camera = createCamera(webixWinId, 50, 600); - - - // Create Scene - inputScene = createScene(0xefeaea); - - // For camera to orbit around a brain target - // let orbitControls call for renderer.domElement to prevent gui dropdown list inactive bug. - const controls = createOrbitControls(vol3dDim, camera, renderer, 200, 1000 ); - - - //-- Get all labels - let allLabelsTxtArr = ["All"], swapNumAndLabels = {}; - - if( labelsObj != null) { - swapNumAndLabels = Object.fromEntries(Object.entries(labelsObj).map(([k, v]) => [v, k])); - Object.keys(labelsObj).forEach((labelNum, idx) => { - if(parseInt(labelNum) != 0) { - allLabelsTxtArr.push(labelsObj[labelNum]); - } - }) - } - - - - - - changeVoxelsOpacity = (opacityVal) => { - // show all voxels - inputScene.traverse(function(child) { - if(parseInt(child.name) > 0 || child.type === "Points") { - child.material.opacity = opacityVal; - } - }); - } - - // Any value less than or equal threVal will be zero - applyThreshold = (vol3dArr, threVal, height, width, depth) => { - return tf.tidy(() => { - let tsr = tf.tensor1d( tf.util.flatten(vol3dArr)); - //-- tsr = tsr.sub(threVal); // to make any noisy voxel < threVal negative - //-- tsr = tsr.prelu(0); // to make all voxels below 0 = 0 - let mask = tf.greater(tsr, tf.onesLike(tsr).mul(threVal)); - tsr = tf.mul(tsr, tf.cast(mask, tsr.dtype)); - mask.dispose(); - - let thresholdTsr3D =tf.reshape(tsr, [ vol3dDim.depth, vol3dDim.height, vol3dDim.width]); - tsr.dispose(); - - return thresholdTsr3D; - }); - - } - - - applyNoiseRemoveByCC = (thresholdTsr3D, height, width, depth) => { - - return tf.tidy(() => { - - let unstackThresholdTsr3D = tf.unstack(thresholdTsr3D); - let thresholdArrSlices3DCC = []; - - // dataSync() using to flatten array. Takes around 1.5 s - for(let sliceTensorIdx = 0; sliceTensorIdx < unstackThresholdTsr3D.length; sliceTensorIdx++ ) { - thresholdArrSlices3DCC[sliceTensorIdx] = Array.from(unstackThresholdTsr3D[sliceTensorIdx].dataSync()); - } - - - thresholdArrSlices3DCC = tf.tidy(() => { - // Remove noisy regions using 3d CC - return postProcessSlices3D(thresholdArrSlices3DCC, vol3dDim.height, vol3dDim.width ); - }) - - return tf.reshape(thresholdArrSlices3DCC, thresholdTsr3D.shape); - - - }); - } - - - applyHistoEq = (thresholdTsr3D) => { - - return tf.tidy(() => { - let imageFlattened = tf.util.flatten(thresholdTsr3D.arraySync()); - //-- e.g. imageFlattened = [1, 1, 1, 4, 1, 4, 4, 1, 4, 0, 1, 4, 1, 4, 0, 0, 0, 1, 0, 0, 0, 4, 4, 1, 0, 1, 1] - - // Desired Max voxel intensity value - let outMaxIntensityVal = 255; - - let imgMaxVal = findArrayMax(imageFlattened); - - let histNumBins = imgMaxVal + 1; - //-- e.g histNumBins = 5 - - let imageHist = new Array(histNumBins).fill(0); - - - // frequency count of each pixel - imageFlattened.forEach(voxelVal => { - imageHist[voxelVal] += 1; - }) - - // Convert to tensor math ( tf.min.js ) - let imageHistTensor = array2Tensor(imageHist); - let histCumSum = imageHistTensor.cumsum(); - - let norm = histCumSum.sub(histCumSum.min()).mul(outMaxIntensityVal); - - // normalize the voxel values - let uniformNorm = norm.div(histCumSum.max().sub( histCumSum.min() ) ); - uniformNorm = uniformNorm.floor(); - //--e.g uniformNorm.print() -> [0, 147, 147, 147, 255] - - // Create mapping function between old and new values - let mapObj = {}; - for(let i = 0; i <= imgMaxVal; i++) { - mapObj[i] = uniformNorm.arraySync()[i]; - } - //-- mapObj: Object { 0: 0, 1: 147, 2: 147, 3: 147, 4: 255 } - - // flat histogram - let imageEq = []; - imageFlattened.forEach((voxelVal, idx) => { - imageEq[idx] = mapObj[voxelVal]; - }) - - return tf.tensor(imageEq, thresholdTsr3D.shape ) - - }); - - }; - - - - - /****************************************************************/ - /****************************************************************/ - - //-- Init the gui - let gui_controls = new function () { - this.bg_color = 0xefeaea; - this.height = vol3dDim.height; - this.width = vol3dDim.width; - this.depth = vol3dDim.depth; - this.opacity = 0.5; - this.thresholding = 0; - this.lrg_con_comp = false; - this.histogram_eq = false; - }; - - - initGUI = () => { - - //-- gui = new dat.GUI(); - input_gui = new dat.GUI({ autoPlace: false }); - input_gui.domElement.id = 'input_gui'; - guiContainer = document.getElementById(guiContainerId); - guiContainer.appendChild(input_gui.domElement); - - let height = vol3dDim.height, width = vol3dDim.width, depth = vol3dDim.depth; - let thresholdTsr3D = tf.clone(vol3dValuesArr); - let enahncedArr3D = Array.from( thresholdTsr3D.arraySync() ); // thresholdTsr3D cloned vol3dValuesArr - - - let heightSlider = input_gui.add( gui_controls, 'height', 0, vol3dDim.height, 1 ).onChange( function ( value ) { - height = parseInt( value ); - - showLoadingIcon(iconId).then(res => { - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - - //-- if(value != this.initialValue) { - //-- FOR FUTURE USE - //-- } - - } ); - - let widthSlider = input_gui.add( gui_controls, 'width', 0, vol3dDim.width, 1 ).onChange( function ( value ) { - width = parseInt( value ); - - showLoadingIcon(iconId).then(res => { - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - - - } ); - - let depthSlider = input_gui.add( gui_controls, 'depth', 0, vol3dDim.depth, 1 ).onChange( function ( value ) { - depth = parseInt( value ); - - showLoadingIcon(iconId).then(res => { - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - - - } ); - - let sceneColor = input_gui.addColor(gui_controls, "bg_color").onChange(function( value ) { - inputScene.background.set( value ); - }); - - let opacitySlider = input_gui.add( gui_controls, 'opacity', 0, 0.9, 0.1 ).onChange( function ( value ) { - changeVoxelsOpacity(value); - } ); - - - const noiseRemovefolder = input_gui.addFolder( '3D Noise Remove' ); - - let thresholdSlider = noiseRemovefolder.add( gui_controls, 'thresholding', 0, 50, 5 ).onChange( function ( value ) { - showLoadingIcon(iconId).then(res => { - thresholdTsr3D = applyThreshold( enahncedArr3D, value, height, width, depth); - - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj ); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - - $$("inputApplyId").enable(); - $$("inputSaveId").enable(); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - } ); - - let connectCompCheck = noiseRemovefolder.add( gui_controls, 'lrg_con_comp' ).onChange( function ( value ) { - showLoadingIcon(iconId).then(res => { - if(value) { - thresholdTsr3D = applyNoiseRemoveByCC(thresholdTsr3D, height, width, depth); - enahncedArr3D = Array.from( thresholdTsr3D.arraySync() ); - } else { - thresholdTsr3D = applyThreshold(vol3dValuesArr, thresholdSlider.getValue(), height, width, depth); - // Restore prev state - if(histoEqCheck.getValue()) { - thresholdTsr3D = applyHistoEq(thresholdTsr3D); - enahncedArr3D = Array.from( thresholdTsr3D.arraySync() ); - } else { - enahncedArr3D = Array.from( tf.clone(vol3dValuesArr).arraySync() ); - } - - } - - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj ); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - - $$("inputApplyId").enable(); - $$("inputSaveId").enable(); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - } ); - - noiseRemovefolder.open(); - - const enhancementfolder = input_gui.addFolder( '3D Enhancement' ); - - let histoEqCheck = enhancementfolder.add( gui_controls, 'histogram_eq' ).onChange( function ( value ) { - showLoadingIcon(iconId).then(res => { - if(value) { - thresholdTsr3D = applyHistoEq(thresholdTsr3D); - enahncedArr3D = Array.from( thresholdTsr3D.arraySync() ); - } else { - thresholdTsr3D = applyThreshold(vol3dValuesArr, thresholdSlider.getValue(), height, width, depth); - // Restore prev state - if(connectCompCheck.getValue()) { - thresholdTsr3D = applyNoiseRemoveByCC(thresholdTsr3D, height, width, depth); - enahncedArr3D = Array.from( thresholdTsr3D.arraySync() ); - } else { - enahncedArr3D = Array.from( tf.clone(vol3dValuesArr).arraySync() ); - } - } - - let pointsObj = getAllPoints(height, width, depth, inputScene, Array.from( thresholdTsr3D.arraySync() ), colorLutObj ); - clearScene(inputScene); - inputScene.add( pointsObj.points ); - disposeHierarchy (pointsObj.points , disposeNode); - - $$("inputApplyId").enable(); - $$("inputSaveId").enable(); - document.getElementById(iconId).style.display = "none"; - changeVoxelsOpacity(opacitySlider.getValue()); - }); - } ); - - enhancementfolder.open(); - - //-- if(allLabelsTxtArr.length <= 1) { // if no labels.json - //-- } - - - $$("inputApplyId").attachEvent("onItemClick", function() { - - $$("inputApplyId").disable(); - - let inputBrainEnhanced = tf.util.flatten( thresholdTsr3D.reverse(1).arraySync() ); - //-- Array(16777216) [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, … ] - // thresholdTsr3D.dispose(); - - let newArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, inputBrainEnhanced); - //-- ArrayBuffer { byteLength: 16777568 } - - $$("inputThreejsWinId").hide(); - - let blob = new Blob([newArrayBuffer], {type: "application/octet-binary;charset=utf-8"}); - let file = new File([blob], "temp.nii"); - let params_mri_enhanced = []; - params_mri_enhanced["files"] = [file]; - params_mri_enhanced[file["name"]] = {lut: "Grayscale", interpolation: false}; - - niftiImage = inputBrainEnhanced; - - // Reset main parameters and clear output threejs win - newRunInferencePrepare().then(res => { - $$("segmentBtn").enable(); - // reset viewer should come after - papaya.Container.resetViewer(0, params_mri_enhanced); - }) - - // -- numOfOverlays = 0; - - - }); - - $$("inputSaveId").attachEvent("onItemClick", function() { - $$("inputSaveId").disable(); - let fileName = refFileName == "" ? opts.uiSampleName: refFileName; - let dataArr1D = tensor2FlattenArray( thresholdTsr3D.reverse(1) ); - if(fileName.search(".nii") < 0) { - // if nii extension doesn't exist, then search will return -1 - fileName = fileName + ".nii"; - } - downloadNifti(dataArr1D, rawNiftiData, "enhanced_" + fileName); - }); - - - } - - - - - - function animate() { - requestAnimationFrame( animate ); - render() - } - - function render() { - // render using requestAnimationFrame - requestAnimationFrame(render); - renderer.render( inputScene, camera ); - - } - - - $$(webixWinId).attachEvent( 'onViewResize', onWindowResize ); - - function onWindowResize() { - camera.aspect = $$(webixWinId).config.width/$$(webixWinId).config.height; - camera.updateProjectionMatrix(); - renderer.setSize( $$(webixWinId).config.width, $$(webixWinId).config.height); - } - - - //-- Create points group for each label - let totalLabelsNum = Object.keys(colorLutObj).length; - // label index initiation - let idx = 0; - - let timer = window.setInterval(function() { - let labelNum = Object.keys(colorLutObj)[idx]; - - if(parseInt(labelNum) != 0) { - let points = getPointsByLabel(vol3dDim.height, vol3dDim.width, vol3dDim.depth, inputScene, vol3dValuesArr, labelNum, colorLutObj); - points.name = labelNum; - inputScene.add( points ); - disposeHierarchy (points, disposeNode); - } - - document.getElementById("progressLine").style.width = (idx * 100 / totalLabelsNum) + "%"; - - if(idx == (totalLabelsNum -1) ){ - window.clearInterval(timer); - document.getElementById("progressLine").style.width = "0%"; - render(); - initGUI(); - changeVoxelsOpacity(0.5); - document.getElementById("inputLoadingIconDiv").style.display = "none"; - - $$("modelTooltip").show(); - let info = "To fast remove noisy regions around the brain, try first with thresholding slide." - document.getElementById("tooltipDiv").innerHTML = - "   "+ info +" " - - } - - idx++; - - }, 10); - - -} - - - - -init = (vol3dValuesArr, webixWinId , containerId , guiContainerId, colorLutObj = null, labelsObj = null) => { - - // Create renderer and append it to parent body. - const renderer = getRenderer(webixWinId, containerId); - - // Get volumen Dim - const vol3dDim = getVol3dDim(vol3dValuesArr); - - // Get camera - const camera = createCamera(webixWinId, 50, 400); - - - // Create Scene - outputScene = createScene(); - - // For camera to orbit around a brain target - // let orbitControls call for renderer.domElement to prevent gui dropdown list inactive bug. - const controls = createOrbitControls(vol3dDim, camera, renderer, 200, 1000 ); - - - // let stats = initStats(); - - // For future use - //-- var light = new THREE.SpotLight( 0xffffff ); - //-- light.position.set( 1, 1, 1 ); - //-- outputScene.add( light ); - - //-- Get all labels - let allLabelsTxtArr = ["All"], swapNumAndLabels = {}; - - if( labelsObj != null) { - swapNumAndLabels = Object.fromEntries(Object.entries(labelsObj).map(([k, v]) => [v, k])); - Object.keys(labelsObj).forEach((labelNum, idx) => { - if(parseInt(labelNum) != 0) { - allLabelsTxtArr.push(labelsObj[labelNum]); - } - }) - } - - - //-- Create points group for each label - Object.keys(colorLutObj).forEach(labelNum => { - if(parseInt(labelNum) != 0) { - let points = getPointsByLabel(vol3dDim.height, vol3dDim.width, vol3dDim.depth, outputScene, vol3dValuesArr, labelNum, colorLutObj); - points.name = labelNum; - outputScene.add( points ); - disposeHierarchy (points, disposeNode); - } - }) - - - - changeVoxelsOpacity = (opacityVal) => { - // show all voxels - outputScene.traverse(function(child) { - if(parseInt(child.name) > 0 || child.type === "Points") { - child.material.opacity = opacityVal; - } - }); - } - - - - filterVoxelsByLabel = (roiLabel) => { - if(roiLabel === "All") { - // show all voxels - outputScene.traverse(function(child){ child.visible = true; } ); - - } else { - outputScene.traverse(function(child) { - if(parseInt(child.name) > 0 || child.type === "Points"){ - if( parseInt(child.name) == parseInt(swapNumAndLabels[roiLabel]) ) { - child.visible = true; - } else { - child.visible = false; - } - } - }); - } - } - - - filterVoxelsByMultiLabel = (roiLabelArr) => { - - let selectedRoiNumArr = []; - roiLabelArr.forEach(roiLabel => { - selectedRoiNumArr.push(parseInt( swapNumAndLabels[roiLabel]) ); - }) - - outputScene.traverse(function(child) { - if(parseInt(child.name) > 0 || child.type === "Points"){ - if( selectedRoiNumArr.indexOf( parseInt(child.name) ) != -1 ) { // -1 means not exist - child.visible = true; - } else { - child.visible = false; - } - } - }); - - - } - - /****************************************************************/ - /************************ Roi CheckList Ext *********************/ - /****************************************************************/ - - let roiSelectionArr = []; - let roiList = document.getElementById('roiList'); - let roiItems = document.getElementById('roiItems'); - roiItems.innerHTML = ''; - - roiList.getElementsByClassName('anchor')[0].onclick = function (evt) { - if (roiItems.classList.contains('visible')){ - roiItems.classList.remove('visible'); - roiItems.style.display = "none"; - } - else{ - roiItems.classList.add('visible'); - roiItems.style.display = "block"; - } - } - - roiItems.onblur = function(evt) { - roiItems.classList.remove('visible'); - } - - removeArrayItem = (arr, item) => { - let index = arr.indexOf(item); - - if (index > -1) { - arr.splice(index, 1); - } - - return arr; - } - - preSelectRoi = (roi) => { - roiSelectionArr.push(roi); - document.getElementById(roi).checked = true; - } - - updateRoiSelectArr = (inputElem) => { - - if(inputElem.checked) { - roiSelectionArr.push(inputElem.id); - } else { - removeArrayItem(roiSelectionArr, inputElem.id) - } - - filterVoxelsByMultiLabel(roiSelectionArr); - } - - creatRoiList = (roiLabelArray) => { - let nodes = ""; - roiSelectionArr = []; - - roiLabelArray.forEach (roi => { - if(roi !== "All") { - nodes += '
  • ' - } - }) - return nodes; - } - - /****************************************************************/ - /****************************************************************/ - - //-- Init the gui - let gui_controls = new function () { - this.opacity = 0.5; - this.roi_label = "All"; - this.multi_roi = false; - }; - - - initGUI = () => { - - //-- gui = new dat.GUI(); - output_gui = new dat.GUI({ autoPlace: false }); - output_gui.domElement.id = 'gui'; - guiContainer = document.getElementById(guiContainerId); - guiContainer.appendChild(output_gui.domElement); - - let opacitySlider = output_gui.add( gui_controls, 'opacity', 0, 0.9, 0.1 ).onChange( function ( value ) { - changeVoxelsOpacity(value); - } ); - - let roiSelector = output_gui.add( gui_controls, 'roi_label', allLabelsTxtArr).onChange(function (value) { - filterVoxelsByLabel(value); - }) - - let multiRoi = output_gui.add( gui_controls, 'multi_roi').onChange(function (value) { - - roiItems.innerHTML = creatRoiList(allLabelsTxtArr); - - if(value == true) { - if(roiSelector.getValue() !== "All") { - preSelectRoi(roiSelector.getValue()); - } - - roiList.style.display = "inline-block"; - roiSelector.domElement.style.pointerEvents = "none"; - roiSelector.domElement.style.opacity = .5; - } else { - filterVoxelsByLabel(roiSelector.getValue()); - roiList.style.display = "none"; - roiSelector.domElement.style.pointerEvents = ""; - roiSelector.domElement.style.opacity = 1; - } - - }) - - if(allLabelsTxtArr.length <= 1) { // if no labels.json - roiSelector.domElement.style.pointerEvents = "none"; - roiSelector.domElement.style.opacity = .5; - multiRoi.remove(); - } - } - - - render(); - initGUI(); - changeVoxelsOpacity(0.5); - - function animate() { - - requestAnimationFrame( animate ); - render() - } - - function render() { - // render using requestAnimationFrame - requestAnimationFrame(render); - renderer.render( outputScene, camera ); - - } - - - $$(webixWinId).attachEvent( 'onViewResize', onWindowResize ); - - function onWindowResize() { - camera.aspect = $$(webixWinId).config.width/$$(webixWinId).config.height; - camera.updateProjectionMatrix(); - renderer.setSize( $$(webixWinId).config.width, $$(webixWinId).config.height); - } - - -} - - -/** -* Show loading icon -* -* @since 1.4.0 -* @param {String} iconId -* @return {Object} defer -* -*/ - - showLoadingIcon = (iconId) => { - let defer = $.Deferred(); - document.getElementById(iconId).style.display = ""; - - setTimeout(function() { - defer.resolve(); // When this fires, the code in a().then(/..../); is executed. - }, 100); - - return defer; - } - -/** -* Create THREE.points for all voxels -* -* @since 1.3.0 -* @param {number} height -* @param {number} width -* @param {number} depth -* @param {Object} scene, threejs scene. -* @param {Array} voxelsVal, 3D array -* @param {Object} colorLutObj e.g. {"0": "rgb(0,0,0)", "1": "rgb(245,245,245)", "2": "rgb(196,58,250)", "3": "rgb(220,248,164)", ... } -* @return {Object} points, THREE.Points. -* -*/ - - getAllPoints = (height, width, depth, scene, voxelsVal, colorLutObj) => { - - - - const geometry = new THREE.BufferGeometry(); - const positions = []; - const colors = []; - let max_x = 0, max_y = 0, max_z = 0; - let min_x = Infinity, min_y = Infinity, min_z = Infinity; - - for (let x = 0; x < depth; x+=1) { - for (let y = 0; y < height; y+=1) { - for (let z = 0; z < width; z+=1) { - if(voxelsVal[x][y][z] != 0) { - - positions.push( x, y, z ); - let labelColor = getRgbObject(colorLutObj[voxelsVal[x][y][z]]); - colors.push(labelColor.r/255, labelColor.g/255, labelColor.b/255 ); - - - if(max_x > x) max_x = x; - if(max_y > y) max_y = y; - if(max_z > z) max_z = z; - - if(min_x < x) min_x = x; - if(min_y < y) min_y = y; - if(min_z < z) min_z = z; - - - } - } - } - } - - geometry.addAttribute( 'position', new THREE.Uint16BufferAttribute( positions, 3 ) ); - geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 )); - - const material = new THREE.PointsMaterial( { size: 5, vertexColors: true, transparent: true } ); - - let points = new THREE.Points( geometry, material ); - - geometry.dispose(); - material.dispose(); - - - - return {"points": points} ; - -} - - -/** -* Create THREE.points for each group of voxels with same label -* -* @since 1.3.0 -* @param {number} height -* @param {number} width -* @param {number} depth -* @param {Object} scene, threejs scene. -* @param {Array} voxelsVal, 3D array -* @param {number} labelNum, Segmenation label number -* @param {Object} colorLutObj e.g. {"0": "rgb(0,0,0)", "1": "rgb(245,245,245)", "2": "rgb(196,58,250)", "3": "rgb(220,248,164)", ... } -* @return {Object} points, THREE.Points. -* -*/ - -getPointsByLabel = (height, width, depth, scene, voxelsVal, labelNum, colorLutObj) => { - - const geometry = new THREE.BufferGeometry(); - const positions = []; - const colors = []; - - for (let x = 0; x < depth; x+=1) { - for (let y = 0; y < height; y+=1) { - for (let z = 0; z < width; z+=1) { - if(voxelsVal[x][y][z] == labelNum) { - positions.push( x, y, z ); - let labelColor = getRgbObject(colorLutObj[labelNum]); - colors.push(labelColor.r/255, labelColor.g/255, labelColor.b/255 ); - } - } - } - } - - geometry.addAttribute( 'position', new THREE.Uint16BufferAttribute( positions, 3 ) ); - geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 )); - - const material = new THREE.PointsMaterial( { size: 5, vertexColors: true, transparent: true } ); - - let points = new THREE.Points( geometry, material ); - - geometry.dispose(); - material.dispose(); - - return points; - -} - - -/** -* Clear threejs scene -* @since 1.3.0 -* @param {Object} scene, threejs scene. -* -*/ - -clearScene = (scene) => { - while(scene.children.length > 0) { - //-- disposeHierarchy (scene, disposeNode); - let node = scene.children[0]; - disposeNode(node); - scene.remove(node); - } - } - - -/** -* Clear threejs material. -* Credit: trusktr (https://discourse.threejs.org/u/trusktr) -* @since 1.3.0 -* @param {Object} material, e.g. THREE.PointsMaterial. -* -*/ - -cleanMaterial = (material) => { - material.dispose(); - // dispose textures - for (const key of Object.keys(material)) { - const value = material[key]; - if (value && typeof value === 'object' && 'minFilter' in value) { - value.dispose(); - } - } -} - - - -/** -* Dispose all scene childrens. -* Credit: https://stackoverflow.com/users/1980846/gaitat -* @since 1.3.0 -* @param {Object} node, e.g. THREE.Points. -* @example -* -* disposeNode (scene.children[0]) -*/ - -disposeNode = (node) => { - if (node instanceof THREE.Mesh || node instanceof THREE.Points) - { - if (node.geometry) - { - node.geometry.dispose(); - } - - if (node.material) - { - if (node.material instanceof THREE.MeshFaceMaterial) - { - $.each (node.material.materials, function (idx, mtrl) - { - if (mtrl.map) mtrl.map.dispose (); - if (mtrl.lightMap) mtrl.lightMap.dispose (); - if (mtrl.bumpMap) mtrl.bumpMap.dispose (); - if (mtrl.normalMap) mtrl.normalMap.dispose (); - if (mtrl.specularMap) mtrl.specularMap.dispose (); - if (mtrl.envMap) mtrl.envMap.dispose (); - if (mtrl.alphaMap) mtrl.alphaMap.dispose(); - if (mtrl.aoMap) mtrl.aoMap.dispose(); - if (mtrl.displacementMap) mtrl.displacementMap.dispose(); - if (mtrl.emissiveMap) mtrl.emissiveMap.dispose(); - if (mtrl.gradientMap) mtrl.gradientMap.dispose(); - if (mtrl.metalnessMap) mtrl.metalnessMap.dispose(); - if (mtrl.roughnessMap) mtrl.roughnessMap.dispose(); - - mtrl.dispose (); // disposes any programs associated with the material - }); - } - else if(node.material instanceof THREE.PointsMaterial) - { - // disposes any programs associated with the material - if (node.material.map) node.material.map.dispose(); - cleanMaterial(node.material); - - } - else - { - if (node.material.map) node.material.map.dispose (); - if (node.material.lightMap) node.material.lightMap.dispose (); - if (node.material.bumpMap) node.material.bumpMap.dispose (); - if (node.material.normalMap) node.material.normalMap.dispose (); - if (node.material.specularMap) node.material.specularMap.dispose (); - if (node.material.envMap) node.material.envMap.dispose (); - if (node.material.alphaMap) node.material.alphaMap.dispose(); - if (node.material.aoMap) node.material.aoMap.dispose(); - if (node.material.displacementMap) node.material.displacementMap.dispose(); - if (node.material.emissiveMap) node.material.emissiveMap.dispose(); - if (node.material.gradientMap) node.material.gradientMap.dispose(); - if (node.material.metalnessMap) node.material.metalnessMap.dispose(); - if (node.material.roughnessMap) node.material.roughnessMap.dispose(); - - node.material.dispose (); // disposes any programs associated with the material - } - } - } -} - -/** -* Dispose all scene childrens. -* Credit: https://stackoverflow.com/users/1980846/gaitat -* @since 1.3.0 -* @param {Object} node, e.g. THREE.scene. -* @example -* -* disposeHierarchy (scene, disposeNode) -*/ -disposeHierarchy = (node, callback) => { - for (let i = node.children.length - 1; i >= 0; i--) { - let child = node.children[i]; - disposeHierarchy (child, callback); - callback (child); - } -} - - - - - - - - - - - - - - diff --git a/v3/js/brainchop/Niivue.js b/v3/js/brainchop/Niivue.js deleted file mode 100644 index 555d5f8..0000000 --- a/v3/js/brainchop/Niivue.js +++ /dev/null @@ -1,276 +0,0 @@ -export class Niivue { - // port of https://github.com/rordenlab/niimath/blob/master/src/bwlabel.c - // return voxel address given row A, column B, and slice C - idx(A, B, C, DIM) { - return C * DIM[0] * DIM[1] + B * DIM[0] + A; - } // idx() - // determine if voxels below candidate voxel have already been assigned a label - check_previous_slice(bw, il, r, c, sl, dim, conn, tt) { - // const nabo: number[] = []; - const nabo = new Uint32Array(27); - let nr_set = 0; - if (!sl) { - return 0; - } - const val = bw[this.idx(r, c, sl, dim)]; - if (conn >= 6) { - const idx = this.idx(r, c, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (conn >= 18) { - if (r) { - const idx = this.idx(r - 1, c, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (c) { - const idx = this.idx(r, c - 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (r < dim[0] - 1) { - const idx = this.idx(r + 1, c, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (c < dim[1] - 1) { - const idx = this.idx(r, c + 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - } - if (conn === 26) { - if (r && c) { - const idx = this.idx(r - 1, c - 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (r < dim[0] - 1 && c) { - const idx = this.idx(r + 1, c - 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (r && c < dim[1] - 1) { - const idx = this.idx(r - 1, c + 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (r < dim[0] - 1 && c < dim[1] - 1) { - const idx = this.idx(r + 1, c + 1, sl - 1, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - } - if (nr_set) { - this.fill_tratab(tt, nabo, nr_set); - return nabo[0]; - } - else { - return 0; - } - } // check_previous_slice() - // provisionally label all voxels in volume - do_initial_labelling(bw, dim, conn) { - let label = 1; - const kGrowArrayBy = 8192; - let ttn = kGrowArrayBy; - let tt = new Uint32Array(ttn).fill(0); - const il = new Uint32Array(dim[0] * dim[1] * dim[2]).fill(0); - const nabo = new Uint32Array(27); - for (let sl = 0; sl < dim[2]; sl++) { - for (let c = 0; c < dim[1]; c++) { - for (let r = 0; r < dim[0]; r++) { - let nr_set = 0; - const val = bw[this.idx(r, c, sl, dim)]; - if (val === 0) { - continue; - } - nabo[0] = this.check_previous_slice(bw, il, r, c, sl, dim, conn, tt); - if (nabo[0]) { - nr_set += 1; - } - if (conn >= 6) { - if (r) { - const idx = this.idx(r - 1, c, sl, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (c) { - const idx = this.idx(r, c - 1, sl, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - } - if (conn >= 18) { - if (c && r) { - const idx = this.idx(r - 1, c - 1, sl, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - if (c && r < dim[0] - 1) { - const idx = this.idx(r + 1, c - 1, sl, dim); - if (val === bw[idx]) { - nabo[nr_set++] = il[idx]; - } - } - } - if (nr_set) { - il[this.idx(r, c, sl, dim)] = nabo[0]; - this.fill_tratab(tt, nabo, nr_set); - } - else { - il[this.idx(r, c, sl, dim)] = label; - if (label >= ttn) { - ttn += kGrowArrayBy; - const ext = new Uint32Array(ttn); - ext.set(tt); - tt = ext; - } - tt[label - 1] = label; - label++; - } - } - } - } - for (let i = 0; i < label - 1; i++) { - let j = i; - while (tt[j] !== j + 1) { - j = tt[j] - 1; - } - tt[i] = j + 1; - } - return [label - 1, tt, il]; - } // do_initial_labelling() - // translation table unifies a region that has been assigned multiple classes - fill_tratab(tt, nabo, nr_set) { - let cntr = 0; - const tn = new Uint32Array(nr_set + 5).fill(0); - const INT_MAX = 2147483647; - let ltn = INT_MAX; - for (let i = 0; i < nr_set; i++) { - let j = nabo[i]; - cntr = 0; - while (tt[j - 1] !== j) { - j = tt[j - 1]; - cntr++; - if (cntr > 100) { - console.log('\nOoh no!!'); - break; - } - } - tn[i] = j; - ltn = Math.min(ltn, j); - } - for (let i = 0; i < nr_set; i++) { - tt[tn[i] - 1] = ltn; - } - } // fill_tratab() - // remove any residual gaps so label numbers are dense rather than sparse - translate_labels(il, dim, tt, ttn) { - const nvox = dim[0] * dim[1] * dim[2]; - let ml = 0; - const l = new Uint32Array(nvox).fill(0); - for (let i = 0; i < ttn; i++) { - ml = Math.max(ml, tt[i]); - } - const fl = new Uint32Array(ml).fill(0); - let cl = 0; - for (let i = 0; i < nvox; i++) { - if (il[i]) { - if (!fl[tt[il[i] - 1] - 1]) { - cl += 1; - fl[tt[il[i] - 1] - 1] = cl; - } - l[i] = fl[tt[il[i] - 1] - 1]; - } - } - return [cl, l]; - } // translate_labels() - // retain only the largest cluster for each region - largest_original_cluster_labels(bw, cl, ls) { - const nvox = bw.length; - const ls2bw = new Uint32Array(cl + 1).fill(0); - const sumls = new Uint32Array(cl + 1).fill(0); - for (let i = 0; i < nvox; i++) { - const bwVal = bw[i]; - const lsVal = ls[i]; - ls2bw[lsVal] = bwVal; - sumls[lsVal]++; - } - let mxbw = 0; - for (let i = 0; i < cl + 1; i++) { - const bwVal = ls2bw[i]; - mxbw = Math.max(mxbw, bwVal); - // see if this is largest cluster of this bw-value - for (let j = 0; j < cl + 1; j++) { - if (j === i) { - continue; - } - if (bwVal !== ls2bw[j]) { - continue; - } - if (sumls[i] < sumls[j]) { - ls2bw[i] = 0; - } - else if (sumls[i] === sumls[j] && i < j) { - ls2bw[i] = 0; - } // ties: arbitrary winner - } - } - const vxs = new Uint32Array(nvox).fill(0); - for (let i = 0; i < nvox; i++) { - vxs[i] = ls2bw[ls[i]]; - } - return [mxbw, vxs]; - } - // given a 3D image, return a clustered label map - // for an explanation and optimized C code see - // https://github.com/seung-lab/connected-components-3d - bwlabel(img, dim, conn = 26, binarize = false, onlyLargestClusterPerClass = false) { - const start = Date.now(); - const nvox = dim[0] * dim[1] * dim[2]; - const bw = new Uint32Array(nvox).fill(0); - if (![6, 18, 26].includes(conn)) { - console.log('bwlabel: conn must be 6, 18 or 26.'); - return [0, bw]; - } - if (dim[0] < 2 || dim[1] < 2 || dim[2] < 1) { - console.log('bwlabel: img must be 2 or 3-dimensional'); - return [0, bw]; - } - if (binarize) { - for (let i = 0; i < nvox; i++) { - if (img[i] !== 0.0) { - bw[i] = 1; - } - } - } - else { - bw.set(img); - } - let [ttn, tt, il] = this.do_initial_labelling(bw, dim, conn); - if (tt === undefined) { - tt = new Uint32Array(0); - } - const [cl, ls] = this.translate_labels(il, dim, tt, ttn); - console.log(conn + ' neighbor clustering into ' + cl + ' regions in ' + (Date.now() - start) + 'ms'); - if (onlyLargestClusterPerClass) { - const [nbw, bwMx] = this.largest_original_cluster_labels(bw, cl, ls); - return [nbw, bwMx]; - } - return [cl, ls]; - } // bwlabel() -} diff --git a/v3/js/brainchop/checkCompatibility.js b/v3/js/brainchop/checkCompatibility.js deleted file mode 100644 index 32c3304..0000000 --- a/v3/js/brainchop/checkCompatibility.js +++ /dev/null @@ -1,2 +0,0 @@ - // Test browser support ES6 classes - try { eval('"use strict"; class foo {};'); } catch (e) { console.log(e); alert("Your browser does not support JS ES6 version, for supported browsers please refer to https://caniuse.com/es6"); } \ No newline at end of file diff --git a/v3/js/brainchop/connectedComponents3DAll.js b/v3/js/brainchop/connectedComponents3DAll.js deleted file mode 100644 index 66539bf..0000000 --- a/v3/js/brainchop/connectedComponents3DAll.js +++ /dev/null @@ -1,760 +0,0 @@ -/* -========================================================= -* Brainchop - v2.0.1 -========================================================= - -* Discription: Pure Javascript code for 3D and 2D connected components -* -* -* -* Authors: Mohamed Masoud and Sergey Plis - 2022 -========================================================= - - - -========================================================= - 3D/2D Connected Components -=========================================================*/ - - /** - * Get binary mask of slice data - * - * @since 1.0.0 - * @param {Array} sliceData- The array represents slice pixel values in 1D - * @returns {Array} Returns binary mask in 1D - * @example - * - * getBinaryMaskData1D([0,100,100, 0,255,255, .. ]) - * // => [0,1,1, 0,1,1, .. ] - * - */ - - getBinaryMaskData1D = (sliceData) => { // greyImage is one channel 2D image with values 0-255 - - let maskBinaryData1D = []; - for (let idx = 0; idx < sliceData.length; idx++) { - - if(sliceData[idx] > 0) { - maskBinaryData1D[idx] = 1; - } else { - maskBinaryData1D[idx] = 0; - } - } - - return maskBinaryData1D; - } - - - - /** - * Convert 1D binary data to 2D - * - * @since 1.0.0 - * @param {Array} binaryData1D- The array represents slice binary mask values in 1D - * @param {number} imgHeight- Slice Height - * @param {number} imgWidth - Slice Width - * @returns {Array} Returns binary mask in 2D - * @example - * - * convertBinaryDataTo2D([0,1,1, 0,0,1 ], 2, 3) - * - * // => [ [0,1,1], - * [0,0,1], - * ] - * - */ - - function convertBinaryDataTo2D(binaryData1D, imgHeight, imgWidth) { - let arr2D = []; - for (let i = 0; i < binaryData1D.length; i += imgWidth) { - let row = binaryData1D.slice(i, i + imgWidth); - arr2D.push(row); - } - return arr2D; - } - - /** - * Add zero padding to 2D array e.g label2D - * pad([[1,1],[1,1]]) means: 1 row of zeros befor, 1 row of zeros after, - * 1 col of zeros befor, 1 col of zeros after, - * Ref: https://js.tensorflow.org/api/3.6.0/#pad - * - * @since 1.0.0 - * @param {Array} arr2d- The array can represents slice binary mask values in 2D - * @returns {Array} Returns same input array with zero padding edges - * @example - * - * addZeroPaddingTo2dArray( [ [1,0,1], - * [0,0,1] ]) - * - * // => [ [0,0,0,0,0], - * [0,1,0,1,0], - * [0,0,0,1,0], - * [0,0,0,0,0], - * ] - * - */ - - function addZeroPaddingTo2dArray(arr2d) { - let paddedArray = []; - let width = arr2d[0].length; - - // Add a row of zeros at the top - paddedArray.push(new Array(width + 2).fill(0)); - - // Add a column of zeros at the start and end of each row - for (let row of arr2d) { - paddedArray.push([0, ...row, 0]); - } - - // Add a row of zeros at the bottom - paddedArray.push(new Array(width + 2).fill(0)); - - return paddedArray; - } - - - /** - * remove zero padding from 2D array e.g label2D - * pad([[1,1],[1,1]]) means: 1 row of zeros befor, 1 row of zeros after, - * 1 col of zeros befor, 1 col of zeros after, - * - * @since 1.0.0 - * @param {Array} arr2d- The array can represents slice binary mask values in 2D - * @returns {Array} Returns same input array without zero padding edges - * @example - * - * removeZeroPaddingFrom2dArray( [ [0,0,0,0,0], - * [0,1,0,1,0], - * [0,0,0,1,0], - * [0,0,0,0,0], - * ]) - * - * // => [ [1,0,1], - * [0,0,1], - * ] - * - */ - - function removeZeroPaddingFrom2dArray(arr2d) { - // Slice the array to remove the first and last rows and columns - let unPaddedArray = arr2d.slice(1, arr2d.length - 1).map(row => { - return row.slice(1, row.length - 1); - }); - return unPaddedArray; - } - - ////////////////////////////////////////////////////////////////////////////////////////////////// - ///////////// 2D Connected Components ///////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - - // class ConnectCompFor2D extends basicImageProcessing { - class ConnectCompFor2D { - constructor () { - this._equivalenceTabel = []; - this._equivalenceTabel[0] = 0; - this._maxLabel = 0; - } - - _updateEquivalenceTable = (label, newLabel) => { - this._equivalenceTabel[label] = newLabel; - } - - _resetEquivalenceTable = () => { - this._equivalenceTabel = []; - this._equivalenceTabel[0] = 0; - } - - - /** - * Adjust equivalence table for connected components finding. Recursive call -- (refine) - * adjust Equivalence table labels such that if eqvTabel[3] = 2 && eqvTabel[2] = 1 then eqvTabel[3] = 1 - * - * @since 1.0.0 - * @param {number} labelIdx - * - */ - - _adjustEquivalenceTable = (labelIdx) => { - - if(this._equivalenceTabel[labelIdx] != labelIdx) { - this._equivalenceTabel[labelIdx] = this._adjustEquivalenceTable(this._equivalenceTabel[labelIdx]); - } - - return this._equivalenceTabel[labelIdx]; - } - - - /** - * Check neighbors of each pixel to assign proper label to current pixel in 2D slice --(refine) - * - * @since 1.0.0 - * @param {Array} label- The 2D array represents slice labels, e.g label[row][col] - * @param {number} row- Slice Height - * @param {number} col - Slice Width - * @param {number} maxLabel - Max label assginged to connected components task till this call - * @returns {number} Returns smallest neighbor label or new incremental label if there is no neighbors with label - * @example - * - * _checkNeighbors2D( [ [0,0,0,0], - * [0,0,4,0], - * [0,5,0,0], - * [0,0,0,0] ], 2, 2, 5) - * // => 4 - * - */ - - _checkNeighbors2D = (label, row, col, maxLabel) => { - - if ( label[row][col - 1] && label[row - 1][col]) { - - if(label[row][col - 1] == label[row - 1][col]) { - return label[row ][col - 1]; - - } else { - - let smallerLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - this._updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row ][col - 1] ) { - return label[row ][col - 1] ; - } else if ( label[row - 1][col] ) { - return label[row - 1][col]; - } else { - this._updateEquivalenceTable(maxLabel+1, maxLabel+1); - return maxLabel+1 ; - } - - } - - - - /** - * Get connected components For 2D slice -- (refine) - * - * @since 1.0.0 - * @param {Array} binaryMaskData2D- The array represents slice binary mask values in 2D, zero padding is needed. - * @param {number} imgHeight- Slice Height - * @param {number} imgWidth - Slice Width - * @returns {Array} Returns Connected Components labels in 2D - * @example - * - * getConComponentsFor2D( [ [0,0,0,0,0],[0,1,0,1,0],[0,0,0,0,0] ], 3, 5) - * - * // => [ [0,0,0,0,0], - * [0,1,0,2,0], - * [0,0,0,0,0] - * ] - * - */ - - getConComponentsFor2D = (binaryMaskData2D, imgHeight, imgWidth) => { - // initiat label - let label1D = []; - this._resetEquivalenceTable(); - for(let idx = 0; idx < imgHeight * imgWidth; idx++) { - label1D[idx] = 0; - } - - let label2D = convertBinaryDataTo2D(label1D, imgHeight, imgWidth); - - let label2DwithPad = addZeroPaddingTo2dArray(label2D); - let binaryMaskData2DwithPad = addZeroPaddingTo2dArray(binaryMaskData2D); - - - // maxLabel initiation to zero, starting label for 2d and 3d labeling - this._maxLabel = 0; - - // 1st pass - for(let row = 1; row <= imgHeight; row++) { - for(let col = 1; col <= imgWidth; col++) { - - if( binaryMaskData2DwithPad[row][col] != 0) { - label2DwithPad[row][col] = this._checkNeighbors2D(label2DwithPad, row, col, this._maxLabel) - if(this._maxLabel < label2DwithPad[row][col]) { - this._maxLabel = label2DwithPad[row][col]; - } - - } - } - } - - - label2D = removeZeroPaddingFrom2dArray(label2DwithPad); - - // adjust Equivalence table labels such that eqvTabel[3] = 2 && eqvTabel[2] = 1 => eqvTabel[3] = 1 - for(let labelIdx = this._equivalenceTabel.length - 1; labelIdx > 0; labelIdx = labelIdx-1 ) { - this._adjustEquivalenceTable (labelIdx); - } - - // 2nd pass : relabeling the slice after eqvTable adjustment - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( label2D[row][col] != 0) { - label2D[row][col] = this._equivalenceTabel[label2D[row][col]]; - } - } - } - - return label2D; - } - - - - -} - - - - ////////////////////////////////////////////////////////////////////////////////////////////////// - ///////////// 3D Connected Components ///////////////// -////////////////////////////////////////////////////////////////////////////////////////////////// - -class ConnectCompFor3D extends ConnectCompFor2D { - - /** - * Find Max label resulted from applying 3D connected components. - * - * @since 1.0.0 - * @param {Array} label3D- The 3D array represents slices labels, e.g label3D[sliceIdx][row][col] - * @param {number} sliceHeight- Slice Height - * @param {number} sliceWidth - Slice Width - * @param {number} numSlices - Total Number of slices (a.k.a z-dim) - * @returns {number} Returns Maximum label found - * @example - * - * getMaxLabelFor3D( [ [[0,0,0],[0,1,0]],[[0,2,0],[0,0,3]] ], 2, 3, 2) - * // => 3 - * - */ - - getMaxLabelFor3D = (label3D, sliceHeight, sliceWidth, numSlices) => { - - let maxLabelFor3D = 0; - - for(let sliceIdx = 0; sliceIdx < numSlices; sliceIdx++ ) { - for(let row = 0; row < sliceHeight; row++) { - for(let col = 0; col < sliceWidth; col++) { - - if( label3D[sliceIdx][row][col] > maxLabelFor3D) { - maxLabelFor3D = label3D[sliceIdx][row][col]; - } - } - } - } - - return maxLabelFor3D; - } - - - /** - * Find largest volume region with the label that has the maximum number of voxels resulted from applying 3D connected components. - * - * @since 1.0.0 - * @param {Array} label3D- The 3D array represents slices labels, e.g label3D[sliceIdx][row][col] - * @param {number} sliceHeight- Slice Height - * @param {number} sliceWidth - Slice Width - * @param {number} numSlices - Total Number of slices aka z-dim - * @returns {number} Returns Volume label that has maximum number of voxels - * @example - * - * getMostFreqVolumeLabel3D( [ [[0,1,0],[0,1,0]],[[0,2,0],[0,0,3]] ], 2, 3, 2) - * // => 1 - * - */ - - getMostFreqVolumeLabel3D = (label3D, sliceHeight, sliceWidth, numSlices) => { - - // Initiat connected component volumes to zeros - let ccVolume = []; - let maxCCLabel3D = this.getMaxLabelFor3D(label3D, sliceHeight, sliceWidth, numSlices) - - for( let idx = 0; idx < maxCCLabel3D; idx ++) { - ccVolume[idx] = 0; - } - - for(let sliceIdx = 0; sliceIdx < numSlices; sliceIdx++ ) { - for(let row = 0; row < sliceHeight; row++) { - for(let col = 0; col < sliceWidth; col++) { - ccVolume[label3D[sliceIdx][row][col]] = ccVolume[label3D[sliceIdx][row][col]] +1; - } - } - } - - let maxCcVolume = 0; - let maxCcVolumeLabel = -1; - - for( let idx = 1; idx < maxCCLabel3D; idx ++) { - - if( maxCcVolume < ccVolume[idx] ) { - maxCcVolume = ccVolume[idx]; - maxCcVolumeLabel = idx; - } - } - - return maxCcVolumeLabel; - } - - - - /** - * Check neighbors of each voxel to assign proper label to current voxel in two consecutive slices -- (refine) - * - * @since 1.0.0 - * @param {Array} label- The 2D array represents slice labels, e.g label[row][col] - * @param {number} z_1PixelLabel- Previous slice pixel label value at same row and col - * @param {number} row- Slice Height - * @param {number} col - Slice Width - * @param {number} maxLabel - Max label assginged to connected components task till this call - * @returns {number} Returns smallest neighbor label or new incremental label if there is no neighbors with label - * @example - * - * _checkNeighbors3D( [ [0,0, 0,0], - * [0,0,17,0], - * [0,18,0,0], - * [0,0 ,0,0] ], 16 , 2, 2, 18) - * // => 16 - * - */ - - - _checkNeighbors3D = (label, z_1PixelLabel, row, col, maxLabel) => { //z_1PixelLabel same x,y pixel label of z-1 prev slice - if ( label[row][col - 1] && label[row - 1][col] && z_1PixelLabel) { - - if( (label[row][col - 1] == label[row - 1][col]) && (label[row][col - 1] == z_1PixelLabel) ) { - return z_1PixelLabel; - - } else { - - let smallLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let smallestLabel = ( z_1PixelLabel < smallLabel ) ? z_1PixelLabel : smallLabel; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - this._updateEquivalenceTable(largerLabel, smallestLabel); - this._updateEquivalenceTable(smallLabel, smallestLabel); - return smallestLabel; - } - - } else if ( label[row][col - 1] && label[row - 1][col] ) { - - if(label[row][col - 1] == label[row - 1][col]) { - return label[row ][col - 1]; - - } else { - - let smallerLabel = ( label[row][col - 1] < label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - let largerLabel = ( label[row][col - 1] > label[row - 1][col] ) ? label[row][col - 1] : label[row - 1][col]; - this._updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - - } else if ( label[row - 1][col] && z_1PixelLabel ) { - - if(label[row - 1][col] == z_1PixelLabel) { - return z_1PixelLabel; - - } else { - - let smallerLabel = ( z_1PixelLabel < label[row - 1][col] ) ? z_1PixelLabel : label[row - 1][col]; - let largerLabel = ( z_1PixelLabel > label[row - 1][col] ) ? z_1PixelLabel : label[row - 1][col]; - this._updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row][col - 1] && z_1PixelLabel ) { - - if( label[row][col - 1] == z_1PixelLabel ) { - return z_1PixelLabel; - - } else { - - let smallerLabel = ( label[row][col - 1] < z_1PixelLabel ) ? label[row][col - 1] : z_1PixelLabel; - let largerLabel = ( label[row][col - 1] > z_1PixelLabel ) ? label[row][col - 1] : z_1PixelLabel; - this._updateEquivalenceTable(largerLabel, smallerLabel); - return smallerLabel; - } - - } else if ( label[row ][col - 1] ) { - return label[row ][col - 1] ; - } else if ( label[row - 1][col] ) { - return label[row - 1][col]; - } else if ( z_1PixelLabel) { - return z_1PixelLabel; - } else { - this._updateEquivalenceTable(maxLabel+1, maxLabel+1); - return maxLabel+1 ; - } - } - - - /** - * Get connected components For 3D Volume --(refine) - * - * @since 1.0.0 - * @param {Array} volumeSlices- 2D array[sliceIdx][sliceHeight*sliceWidth] such that volumeSlices[i] gives slice data as 1d Array - * @param {number} sliceHeight- Slice Height - * @param {number} sliceWidth - Slice Width - * @returns {Array} Returns 3D labels e.g. label3D[sliceIdx][row][col] - * @example - * - * getConComponentsFor3DVolume( [ [0,0,0,0,0,128, 50 , 0 ,0,0,0,0], - * [0,0,0,0,0, 0 , 90 , 0 ,0,0,0,0], - * [0,0,0,0,0, 0 , 240,100,0,100,0,0] ], 3, 4) - * - * // => [ [ [0,0,0,0], - * [0,1,1,0], - * [0,0,0,0]], - * - * [ [0,0,0,0], - * [0,0,1,0], - * [0,0,0,0]], - * - * [ [0,0,0,0], - * [0,0,1,1], - * [0,2,0,0]], - * ] - * - */ - getConComponentsFor3DVolume = (volumeSlices, sliceHeight, sliceWidth) => { - - let binaryMaskData1D = []; - let binaryMaskData2D = []; - let label3D = []; - - for(let sliceIdx = 0; sliceIdx < volumeSlices.length; sliceIdx++) { - - binaryMaskData1D[sliceIdx] = getBinaryMaskData1D(volumeSlices[sliceIdx]); // binaryMaskData1D has values 0 or 1 - - binaryMaskData2D[sliceIdx] = convertBinaryDataTo2D(binaryMaskData1D[sliceIdx], sliceHeight, sliceWidth); - - if(sliceIdx == 0) { - //Only called for once at begining with first slice - label3D[sliceIdx] = this.getConComponentsFor2D(binaryMaskData2D[sliceIdx], sliceHeight, sliceWidth); - - } else { - label3D[sliceIdx] = this._getConComponentsFor2Slices(binaryMaskData2D[sliceIdx], label3D[sliceIdx - 1], sliceHeight, sliceWidth); - } - - } - - // 3d connected components third pass - for(let sliceIdx = 0; sliceIdx < volumeSlices.length; sliceIdx++) { - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - - if( label3D[sliceIdx][row][col] != 0) { - label3D[sliceIdx][row][col] = this._equivalenceTabel[label3D[sliceIdx][row][col]]; - } - } - } - } - - return label3D; - } - - // For future use - - getConComponentsFor3DVolumeWithTimer = async(volumeSlices, sliceHeight, sliceWidth) => { - - const self = this; - - return new Promise((resolve, reject) => { - document.getElementById("progressBarChild").parentElement.style.visibility = "visible"; - document.getElementById("progressBarChild").style.width = 0; - - let binaryMaskData1D = []; - let binaryMaskData2D = []; - let label3D = []; - - let sliceIdx = 0; - - let ccTimer = window.setInterval(function() { - - binaryMaskData1D[sliceIdx] = getBinaryMaskData1D(volumeSlices[sliceIdx]); // binaryMaskData1D has values 0 or 1 - binaryMaskData2D[sliceIdx] = convertBinaryDataTo2D(binaryMaskData1D[sliceIdx], sliceHeight, sliceWidth); - - if(sliceIdx == 0) { - //Only called for once at begining with first slice - label3D[sliceIdx] = self.getConComponentsFor2D(binaryMaskData2D[sliceIdx], sliceHeight, sliceWidth); - - } else { - label3D[sliceIdx] = self._getConComponentsFor2Slices(binaryMaskData2D[sliceIdx], label3D[sliceIdx - 1], sliceHeight, sliceWidth); - } - - - if(sliceIdx == (volumeSlices.length -1)) { - document.getElementById("progressBarChild").style.width = 0; - window.clearInterval( ccTimer ); - - // 3d connected components third pass - for(let sliceIdx = 0; sliceIdx < volumeSlices.length; sliceIdx++) { - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - - if( label3D[sliceIdx][row][col] != 0) { - label3D[sliceIdx][row][col] = self._equivalenceTabel[label3D[sliceIdx][row][col]]; - } - } - } - } - - resolve(label3D); - } - - sliceIdx++; - document.getElementById("progressBarChild").style.width = (sliceIdx + 1)*100/volumeSlices.length + "%"; - - }, 10); // timer delay - - }) - } - - - - /** - * Get connected components For a Volume of 2 slices, current slice and previous slice.-- (refine) - * - * @since 1.0.0 - * @param {Array} binaryMaskData2D- 2D array[row][col] has the mask {0,1} values of the current selected slice - * @param {Array} preSliceLabels- 2D array[row][col] has the previous slice labels - * @param {number} imgHeight- Slice Height - * @param {number} imgWidth - Slice Width - * @returns {Array} Returns 2D labels e.g. label2D[row][col] of the current slice - * @example - * - * equivalenceTabel = []; - * equivalenceTabel[1] = 1; - * _getConComponentsFor2Slices( [[0,0,0,0,0], - * [0,1,0,1,0], - * [0,0,0,0,0]] , [[0,0,0,0,0], - * [0,0,0,1,0], - * [0,0,0,0,0]] , 3, 5); - * - * // => [ [0,0,0,0,0], - * [0,2,0,1,0], - * [0,0,0,0,0] - * ] - * - */ - - - _getConComponentsFor2Slices = (binaryMaskData2D, preSliceLabels, imgHeight, imgWidth) => { - let label1D = []; - // resetEquivalenceTable(); - for(let idx = 0; idx < imgHeight * imgWidth; idx++) { - label1D[idx] = 0; - } - - let label2D = convertBinaryDataTo2D(label1D, imgHeight, imgWidth); - - // Add zero padding for cases where image has pixel value > 0 on borders - // e.g. MRI is touching borders or there is noisy pixel with value > 0 at row 0 or column 0 - - let label2DwithPad = addZeroPaddingTo2dArray(label2D); - let binaryMaskData2DwithPad = addZeroPaddingTo2dArray(binaryMaskData2D); - let preSliceLabelsWithPad = addZeroPaddingTo2dArray(preSliceLabels); - - for(let row = 1; row <= imgHeight; row++) { - for(let col = 1; col <= imgWidth; col++) { - - if( binaryMaskData2DwithPad[row][col] != 0) { - label2DwithPad[row][col] = this._checkNeighbors3D(label2DwithPad, preSliceLabelsWithPad[row][col], row, col, this._maxLabel) - if(this._maxLabel < label2DwithPad[row][col]) { - this._maxLabel = label2DwithPad[row][col]; - } - - } - } - } - - label2D = removeZeroPaddingFrom2dArray(label2DwithPad); - - // console.log("First pass label2D :", label2D); - for(let labelIdx = this._equivalenceTabel.length - 1; labelIdx > 0; labelIdx = labelIdx-1 ) { - this._adjustEquivalenceTable (labelIdx); - } - - for(let row = 0; row < imgHeight; row++) { - for(let col = 0; col < imgWidth; col++) { - - if( label2D[row][col] != 0) { - label2D[row][col] = this._equivalenceTabel[label2D[row][col]]; - } - } - } - - return label2D; - } - - - /** - * Find largest 3d region - * Can be used for post processing the resulted labels from the inference model by removing noisy 3D regions, and keep only - * - * @since 1.0.0 - * @param {Array} volumeSlices- 2D array[sliceIdx][sliceHeight*sliceWidth] such that volumeSlices[i] gives slice data as 1d Array - * @param {number} sliceHeight- Slice Height - * @param {number} sliceWidth - Slice Width - * @returns {Array} Returns 2D labels array volumeSlices[sliceIdx][sliceHeight*sliceWidth] after filtering noisy 3d regions - * @example - * - * findLargest3dRegion( [ [0,0,0,0, 0,1,1,0, 0,0,0,0], - * [0,0,0,0, 0,0,1,1, 0,0,0,0], - * [0,0,0,0, 0,0,0,1, 0,1,1,0] ], 3, 4) - * - * // => [ [0,0,0,0, 0,1,1,0, 0,0,0,0], - * [0,0,0,0, 0,0,1,1, 0,0,0,0], - * [0,0,0,0, 0,0,0,1, 0,0,0,0] - * ] - * - */ - - findLargest3dRegion = (volumeSlices, sliceHeight, sliceWidth) => { - - let label3D = []; - - label3D = this.getConComponentsFor3DVolume(volumeSlices, sliceHeight, sliceWidth); - //-- label3D = await this.getConComponentsFor3DVolumeWithTimer(volumeSlices, sliceHeight, sliceWidth); - - // Filter only largest volumetric 3d region with the most voxels of same label and remove noisy smaller 3d regions - let maxVolumeLabel = this.getMostFreqVolumeLabel3D(label3D, sliceHeight, sliceWidth, volumeSlices.length); - - for(let sliceIdx = 0; sliceIdx < volumeSlices.length; sliceIdx++) { - //Get max volume mask - let row, col; - for(row = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++) { - // remove nosiy smaller regions - if(label3D[sliceIdx][row][col] != maxVolumeLabel) { - label3D[sliceIdx][row][col] = 0; - } else { - //mask largest 3d volumatic region - label3D[sliceIdx][row][col] = 255; - } - } - } - - let pixelIdx; - - for(row = 0, pixelIdx = 0; row < sliceHeight; row++) { - for(col = 0; col < sliceWidth; col++, pixelIdx++) { - //Filter smaller regions original MRI data - if(label3D[sliceIdx][row][col] == 0) { - volumeSlices[sliceIdx][pixelIdx] = 0; - } - - } - } - } - - //-- Postprocess volumeSlices after remove noisy regions or smaller regions - return volumeSlices; - } - -} diff --git a/v3/js/brainchop/mainMeshNetFunctions.js b/v3/js/brainchop/mainMeshNetFunctions.js deleted file mode 100644 index 435ad98..0000000 --- a/v3/js/brainchop/mainMeshNetFunctions.js +++ /dev/null @@ -1,7324 +0,0 @@ -/* -========================================================= -* Brainchop - v3.0.0 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 38, 38, 38, 1] -* Model : Meshnet or similar -* -* Authors: Mohamed Masoud and Sergey Plis - 2022 -========================================================= - - - -========================================================= - Brainchop for 3D Brain Segmentation -=========================================================*/ - -(function() { - - -/** -* Return 1-Dim Array of the slice pixels value, this 1 dim represents one channel -* -* @since 1.0.0 -* @param {number} sliceIdx- The slice index. -* @param {object} niftiHeader- The header of nifti file. -* @param {ArrayBuffer} niftiImage- The image data of nifti file. -* @returns {Array} Returns the slice data as 1 dim array- Array length = slice width * slice height -* @example -* -* getSliceData1D(100, niftiHeader, niftiImage) -* // => [0, 0, ...]; -* -*/ - - - getSliceData1D = (sliceIdx, niftiHeader, niftiImage) => { - // Get nifti dimensions - let cols = niftiHeader.dims[1]; // Slice width - let rows = niftiHeader.dims[2]; // Slice height - - let typedData; - - if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedData = new Uint8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedData = new Int16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedData = new Int32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedData = new Float32Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedData = new Float64Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedData = new Int8Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedData = new Uint16Array(niftiImage); - } else if (niftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedData = new Uint32Array(niftiImage); - } else { - return; - } - - // offset to specified slice - let sliceSize = cols * rows; - - let sliceOffset = sliceSize * sliceIdx; - - let data1DimArr = []; - - // Draw pixels - for (let row = 0; row < rows; row++) { - let rowOffset = row * cols; - - for (let col = 0; col < cols; col++) { - let offset = sliceOffset + rowOffset + col; - let value = typedData[offset]; - // Create 1Dim Array of pixel value, this 1 dim represents one channel - data1DimArr[(rowOffset + col)] = value & 0xFF; - - } - } - - return data1DimArr; - } - - -/** -* Check if string -* -* @since 3.0.0 -* @param {Any} variable -* @returns {bool} -* @example -* -* isString("someString") -* // => true -* -* isString({ "0": "BG", "1": "Cerebral-White-Matter", "2": "Ventricle"}) -* // => false -* -* isString("") -* // => false -*/ - - isString = (variable) => { - return (typeof variable === 'string' || variable instanceof String) && - (variable !== null) && variable.length ? true : false; - } - - -/** -* Check if object -* -* @since 3.0.0 -* @param {Any} variable -* @returns {bool} -* @example -* -* isObject({ "0": "BG", "1": "Cerebral-White-Matter", "2": "Ventricle"}) -* // => true -* -* isObject("someString") -* // => false -* -*/ - - isObject = (variable) => { - return (typeof variable === 'object') && (variable !== null) ? true : false; - } - - - /** - * Find if two arrays are identical. - * - * @function - * @since 3.0.0 - * @version 3.0.0 - * @category Array - * @param {Array} array1 - The array of values. - * @param {Array} array2 - The array of values. - * @returns {boolean} - * @example - * - * areArraysEquals( [1, 1, 2, 3], [1, 1, 2, 5]) - * - * => false - */ - - areArraysEquals = (array1, array2) => { - return JSON.stringify(array1) === JSON.stringify(array2) ? true : false; - } - - - /** - * Verify if parent object has all keys of child object - * e.g. child object: labelsHistoObj, parent object: colorLutObj or labelsObj - * - * - * @function - * @since 1.0.0 - * @version 3.0.0 - * @param {object} childObj - The child object e.g. labelsHistoObj - * @param {object} parentObj - The parent object e.g. colorLutObj or labelsObj - * @returns {boolean} - * @example - * - * verifyChildParentObjects( {"x": 1, "y": 2}, {"y": 2, "z": 3, "x": 4}) - * - * => true - */ - - verifyChildParentObjects = (childObj, parentObj) => { - - Object.keys(childObj).forEach((childKey, idx) => { - - if ( ! parentObj.hasOwnProperty(childKey)) { - return false; - } - }) - - return true; - } - -/** -* Generates number of colors using HSL wheel hsl(hue, saturation, lightness). -* -* @since 1.0.0 -* @param {number} s- The saturation number. -* @param {number} l- The lightness number. -* @param {number} num_colors- The number of colors to generate. -* @returns {Array} Returns colors array of string -* @example -* -* generateColors(100, 50, 3) -* // => [ "hsla(0,100%,50%)", "hsla(120,100%,50%)", "hsla(240,100%,50%)" ] -* -*/ - - generateColors = (s, l, num_colors) => { - let colors = [] - let delta = Math.trunc(360 / num_colors) - - for (let i = 0; i < num_colors; i++) { - let h = i * delta - colors.push("hsla("+ h + "," + s +"%," + l+ "%" + ")") - } - - return colors - } - -/** -* Convert rgb string to rgb object. -* -* @since 1.0.0 -* @param {string} rgbString- The rgb string. -* @returns {Object} Returns RGB as object -* @example -* -* getRgbObject( "rgb(255,0,0)" ) -* // => { r: 255, g: 0, b: 0 } -* -*/ - - getRgbObject = (rgbString) => { - let RGB = {}; - let rgbArray = rgbString; - rgbArray = rgbArray.replace(/[^\d,]/g, '').split(','); - let rgbKeys=["r","g","b"]; - RGB=rgbKeys.reduce((obj, key, index) => ({ ...obj, [key]:parseInt(rgbArray[index]) }), {}); - return RGB; - } - -/** -* Convert "hsl(hue, saturation, lightness)" string to "rgb(rValue,gValue,bValue)" string. -* -* @since 1.0.0 -* @param {string} hsl- The hsl string. -* @returns {string} Returns RGB as string -* @example -* -* hslToRgb( "hsla(0,100%,50%)" ) -* // => "rgb(255,0,0)" -* -*/ - - hslToRgb = (hsl) => { - let sep = hsl.indexOf(",") > -1 ? "," : " "; - hsl = hsl.substr(5).split(")")[0].split(sep); - - if (hsl.indexOf("/") > -1) { - hsl.splice(3,1); - } - - let h = hsl[0], - s = hsl[1].substr(0,hsl[1].length - 1) / 100, - l = hsl[2].substr(0,hsl[2].length - 1) / 100; - - - - let c = (1 - Math.abs(2 * l - 1)) * s, - x = c * (1 - Math.abs((h / 60) % 2 - 1)), - m = l - c/2, - r = 0, - g = 0, - b = 0; - if (0 <= h && h < 60) { - r = c; g = x; b = 0; - } else if (60 <= h && h < 120) { - r = x; g = c; b = 0; - } else if (120 <= h && h < 180) { - r = 0; g = c; b = x; - } else if (180 <= h && h < 240) { - r = 0; g = x; b = c; - } else if (240 <= h && h < 300) { - r = x; g = 0; b = c; - } else if (300 <= h && h < 360) { - r = c; g = 0; b = x; - } - r = Math.round((r + m) * 255); - g = Math.round((g + m) * 255); - b = Math.round((b + m) * 255); - - - return "rgb(" + r + "," + g + "," + b + ")"; - } - - -/** -* Convert rgb object to hex string. -* Credit: https://stackoverflow.com/questions/5623838/ -* -* @since 1.2.0 -* @param {object} rgbOb -* @returns {string} Returns hex as string -* @example -* -* rgbToHex( { r: 255, g: 0, b: 0 } ) -* // => "#" -* -*/ - -rgbToHex = (rgbObj) => { - chToHex = (ch) => { - let hex = ch.toString(16); - return hex.length == 1 ? "0" + hex : hex; - } - - return "#" + chToHex(rgbObj.r) + chToHex(rgbObj.g) + chToHex(rgbObj.b); -} - - -/** -* For Dice calculations- find the intersection -* -* @since 1.0.0 -* @param {Array} ar1- The array represents output labels in 1D -* @param {Array} ar2- The array represents GT data in 1D -* @returns {Array} Returns intersection 1D array -* @example -* -* intersect([0,1,1], [0,2,2]) -* // => [0] -* -*/ - - intersect = (ar1, ar2) => { - const intersection = []; - for(let i = 0; i < ar1.length ; i++) { - if(ar1[i] == ar2[i]) { - intersection.push(ar1[i]); - } - } - - return intersection; - } - - -/** -* For Dice calculations- diceCoefficient -* -* @since 1.0.0 -* @param {Array} ar1- The array represents output labels in 1D -* @param {Array} ar2- The array represents GT data in 1D -* @returns {number} Returns dice Coefficient number -* @example -* -* diceCoefficient([0,1,1], [0,2,2]) -* // => 0.333 -* -*/ - - diceCoefficient = (ar1, ar2) => { - return ( 2 * intersect(ar1, ar2).length ) / ( ar1.length + ar2.length ); - } - - -/** -* Get maximum region mask using contour method to filter 2D slice smaller regions -* -* @since 1.0.0 -* @param {ImageData} canvasImageData- The imageData object represents slice canvas data e.g. ImageData { width: 100, height: 100, data: Uint8ClampedArray(40000) } -* @returns {TypedArray} Returns contour pixels value {0, 255} -* @example -* -* ctx = papayaContainers[0].viewer.canvas.getContext("2d") -* -* getMaxRegionMaskByContour( ctx.getImageData(0, 0, 255, 255) ) -* // => Uint8Array(65025) [ 0, 0, 0, 0, 0, 0, 0,... , 255, 255, 255, … ] -* -*/ - - getMaxRegionMaskByContour= (canvasImageData) => { // slice matrix - - let mat = cv.matFromImageData(canvasImageData); - - let mask = cv.Mat.zeros(mat.cols, mat.rows, cv.CV_8UC3); - - let mask_gray = new cv.Mat (); - let mask_binary = new cv.Mat (); - let contours = new cv.MatVector(); - let hierarchy = new cv.Mat(); - - // Grayscale conversion - cv.cvtColor (mat, mask_gray, cv.COLOR_RGBA2GRAY, 0); - - cv.findContours(mask_gray, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE); // cv.CHAIN_APPROX_SIMPLE - - let maxContourArea = 0 - let maxContourAreaIdx = -1 - for (let i = 0; i < contours.size(); ++i) { - let cnt = contours.get(i); - let area = cv.contourArea(cnt, false); - - if(maxContourArea < area) { - maxContourArea = area; - maxContourAreaIdx = i; - } - - cnt.delete(); - } - - let color = new cv.Scalar(255, 255, 255); - cv.drawContours(mask, contours, maxContourAreaIdx, color, -1); //cv.LINE_8 - - cv.cvtColor (mask, mask_gray, cv.COLOR_RGBA2GRAY, 0); - cv.threshold (mask_gray, mask_binary, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU); - - - mat.delete(); - mask.delete(); - mask_gray.delete(); - - contours.delete(); - hierarchy.delete(); - - return mask_binary.data; - } - - -/////////////******************* 3D Connected Components**************************///////////////// -/** -* Post processing the resulted labels from the inference model by removing noisy 3D regions, and keep only largest region -* -* @since 1.0.0 -* @param {Array} outputSlices- 2D array[sliceIdx][sliceHeight*sliceWidth] such that outputSlices[i] gives slice data as 1d Array -* @param {number} sliceHeight- Slice Height -* @param {number} sliceWidth - Slice Width -* @returns {Array} Returns 2D labels array outputSlices[sliceIdx][sliceHeight*sliceWidth] after filtering noisy 3d regions -* @example -* -* await postProcessSlices3D( [ [0,0,0,0, 0,1,1,0, 0,0,0,0], - [0,0,0,0, 0,0,1,1, 0,0,0,0], - [0,0,0,0, 0,0,0,1, 0,1,1,0] ], 3, 4) -* -* // => [ [0,0,0,0, 0,1,1,0, 0,0,0,0], -* [0,0,0,0, 0,0,1,1, 0,0,0,0], -* [0,0,0,0, 0,0,0,1, 0,0,0,0] -* ] -* -*/ - - postProcessSlices3D = (outputSlices, sliceHeight, sliceWidth) => { - - let cc3d = new ConnectCompFor3D(); - - outputSlices = cc3d.findLargest3dRegion(outputSlices, sliceHeight, sliceWidth); - - // postprocess outputSlices after remove noisy regions or smaller regions - delete cc3d; - return outputSlices; - } - - - -///////////////************************3D Contours*********************************//////////////////// - - getSliceContoursMaskByLabel = (imgDataForLabel, mask, color) => { - - let mat = cv.matFromImageData( imgDataForLabel ); - let mask_gray = new cv.Mat (); - let mask_binary = new cv.Mat (); - let contours = new cv.MatVector(); - let hierarchy = new cv.Mat(); - - // Grayscale conversion - cv.cvtColor (mat, mask_gray, cv.COLOR_RGBA2GRAY, 0); - cv.findContours(mask_gray, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE); //cv.RETR_CCOMP cv.CHAIN_APPROX_SIMPLE cv.CHAIN_APPROX_NONE cv.RETR_EXTERNAL - - // Draw contours on mask - for (let i = 0; i < contours.size(); ++i) { - cv.drawContours(mask, contours, i, color, 1, cv.LINE_8, hierarchy, 100); - } - - mask_gray.delete(); contours.delete(); hierarchy.delete(); - - return mask; - } - - - - getCustomContoursColor = (numSegClasses) => { - let colors = generateColors(100, 50, numSegClasses); - let colorsRgbObj = []; - //Find the color object for each class - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - colorsRgbObj[classIdx] = getRgbObject(hslToRgb(colors[classIdx])); - } - - return colorsRgbObj; - } - - - getCanvasImageDataForImgRegion = (sliceData1D, imgHeight, imgWidth, regionLabel) => { - let canvas = document.createElement("CANVAS"); - - // Set canvas dimensions to nifti slice dimensions - canvas.width = imgWidth; - canvas.height = imgHeight; - - // make canvas image data - let ctx = canvas.getContext("2d"); - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - for (let idx = 0; idx < sliceData1D.length; idx++) { - - if(sliceData1D[idx] == regionLabel) { - value = 255; - } else { - value = 0; - } - - canvasImageData.data[idx * 4] = value; - canvasImageData.data[idx * 4 + 1] = value; - canvasImageData.data[idx * 4 + 2] = value; - canvasImageData.data[idx * 4 + 3] = 255; // Alpha channel - } - - return canvasImageData; - - } - - - getSliceContours = ( sliceData1D, sliceHeight, sliceWidth, numSegClasses, isRGBA = false) => { - - let sliceContoursMask = cv.Mat.zeros(sliceWidth, sliceHeight, cv.CV_8UC3); - - let allLabelColors = getCustomContoursColor(numSegClasses); - //-- e.g. allLabelColors : [ { r: 255, g: 0, b: 0 }, { r: 0, g: 255, b: 0 }, { r: 0, g: 0, b: 255 } ] - - // For future use - let bgLabel = 0; - - // For each labeled region find its contours - for(let label = 0; label < numSegClasses; label++) { - - if(label != bgLabel) { - - let labelColor; - - if(isRGBA) { - labelColor = { 0: allLabelColors[label].r , 1: allLabelColors[label].g, 2: allLabelColors[label].b, 3: 0, length: 4 }; - } else { // is Gray - let grayValue = Math.ceil(label*255/(numSegClasses - 1)) - labelColor = { 0: grayValue , 1: grayValue, 2: grayValue, 3: 0, length: 4 }; - } - sliceContoursMask = getSliceContoursMaskByLabel( getCanvasImageDataForImgRegion( [...sliceData1D], sliceHeight, sliceWidth, label ), sliceContoursMask, labelColor ); - } - } - - if(isRGBA) { - // Convert output contours mask to RGBA to make background areas transparent. - cv.cvtColor (sliceContoursMask, sliceContoursMask, cv.COLOR_RGB2RGBA, 0); - - // Make background areas transparent and keep only edges - let slicePixelsRGBA1D = sliceContoursMask.data; - - for (let i = 0; i < slicePixelsRGBA1D.length; i += 4) { - - if( (slicePixelsRGBA1D[i] == 0) && (slicePixelsRGBA1D[i+1] == 0) && (slicePixelsRGBA1D[i+2] == 0) ) { - slicePixelsRGBA1D[i+3] = 0; - } - } - - sliceContoursMask.delete(); - - return slicePixelsRGBA1D - - } else { // Gray is needed. - - cv.cvtColor (sliceContoursMask, sliceContoursMask, cv.COLOR_RGBA2GRAY, 0); - return sliceContoursMask.data - } - - } - - - findVolumeContours = (volumeSlices, sliceHeight, sliceWidth, numSegClasses) => { - let volumeSlicesWithContours = []; - - for(let sliceIdx = 0; sliceIdx < volumeSlices.length; sliceIdx++) { - volumeSlicesWithContours[sliceIdx] = getSliceContours( [...volumeSlices[sliceIdx]], sliceHeight, sliceWidth, numSegClasses) - } - - return volumeSlicesWithContours; - } - - -////////////*******************************************************************//////////////////// -/** -* Standard Normal variate using Box-Muller transformation. -* The transformation simply takes two variables that are uniformly distributed -* and sends them to two independent random variables with a standard normal distribution. -* -* @since 1.0.0 -* @returns {number} Returns -* @example -* -* randn_bm() -* -* // => 0.2175 -* -*/ - - randn_bm = () => { - //u and v are random variables, they are uniformly distributed in the interval (0,1) - let u = 0, v = 0; - while(u === 0) u = Math.random(); //Converting [0,1) to (0,1) - while(v === 0) v = Math.random(); - // returns independent, random variable that has a standard normal distribution - return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ); - } - - -/** -* Check whether the proposed subvolumes coords are feasible -* -* @since 1.0.0 -* @param {Array} DHW- Generated Coordinates e.g. [100,150,100] -* @param {Array} cubeSides - MRI volume sides e.g.[256,256,256] -* @param {Array} subCubeSides -Batch size e.g. [38,38,38] -* @returns {boolean} Returns - true or false -* @example -* -* checkInside([100,150,100], [256,256,256], [38,38,38]) ) -* -* // => true -* -*/ - - - checkInside = (DHW, cubeSides, subCubeSides) => { - for (let i = 0; i < 3; i++) { - if ( (Math.sign(DHW[i]) < 0) || ( (DHW[i] + subCubeSides[i]) > cubeSides[i]) ) { - return false; - } - } - - return true; - } - - - -/** -* Generate feasible overlap coordinates for inference -* -* @since 1.0.0 -* @param {number} numOfSubCubes- Number of ovelap subCubes to generate e.g. 200 -* @param {Array} mean - MRI mean voxel coordinate e.g. [ 123, 145, 127 ] -* @param {Array} sigma - Variance -* @param {Array} cubeSides - MRI volume sides e.g.[256,256,256] -* @param {Array} subCubeSides -Batch size e.g. [38,38,38] -* @returns {Array} Returns all generated coords -* @example -* -* findCoordsOfAddBrainBatches(200, [ 123, 145, 127 ], [ 1454.45, 1099.23, 1178.78 ], [256,256,256], [38,38,38]) -* -* // => [[ 187, 132, 56 ], [ 109, 103, 208 ], ... , [ 54, 97, 29 ]] -* -*/ - - findCoordsOfAddBrainBatches = (numOfSubCubes, mean, sigma, cubeSides, subCubeSides ) => { - - const allCoords = []; - let coord; - - for (let i = 0; i < numOfSubCubes; i++) { - coord = Array(Math.round(mean[0]+randn_bm()*sigma[0]), - Math.round(mean[1]+randn_bm()*sigma[1]), - Math.round(mean[2]+randn_bm()*sigma[2]) ); - if( !checkInside(coord, cubeSides, subCubeSides) ) { - i--; - // console.log(coord); - } else { - allCoords[i] = coord; - } - } - - return allCoords; - } - -/** -* Return Tensor with binary 3D volume data 0 or 1 -* Element-wise: (x > 0 ? 1 : alpha * x ); e.g. Tenosr [0, 0.9, 0.8, -3] => Tensor [0, 1, 1, 0] -* -* @since 1.0.0 -* @param {tf.Tensor|TypedArray|Array} volumeDataTensor- e.g. tf.tensor1d([0, 0.2, 0.1, 0.3]) -* @returns {tf.Tensor} Returns Binary value tensor {0,1} -* @example -* -* binarizeVolumeDataTensor(tf.tensor1d([0, 2, -1, -3]) -* -* // => Tensor [0, 1, 0, 0] -* -*/ - - - binarizeVolumeDataTensor = (volumeDataTensor) => { - - let alpha = 0; - // element-wise: (x > 0 ? 1 : alpha * x ); e.g. Tenosr [0, 0.9, 0.8, -3] => Tensor [0, 1, 1, 0] - return volumeDataTensor.step(alpha); - - } - - -/** -* Convert tensor to buffer so immutable tensor can be mutable buffer with get() and set() -* To convert buffer to tensor use bufferObj.toTensor() -* -* @since 1.0.0 -* @param {tf.Tensor|TypedArray|Array} tensor- e.g. tf.tensor1d([0, 0.2, 0.1, 0.3]) -* @returns {tf.buffer} Returns mutable tf.buffer object -* @example -* -* tensor2Buffer( tf.tensor1d( [0, 2, -1, -3] ) ) -* -* // => tf.buffer: Object { dtype: "float32", shape: (1) […], size: 4, values: Float32Array(4), strides: [] } -* -*/ - - tensor2Buffer = (tensor) => { - return tf.buffer(tensor.shape, tensor.dtype, tensor.dataSync()); - } - - - - tensor2LightBuffer = (tensor, dtype) => { - return new Buffer(tensor.shape, dtype, Array.from(tensor.dataSync()) ); - } - - -/** -* Convert single/multi dimensional tensor to single/multi dimensional Array -* -* @since 1.0.0 -* @param {tf.Tensor} tensor- e.g. tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ) -* @returns {Array} Returns mutable single/multi dimensional Array -* @example -* -* tensor2Array( tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ) ) -* -* // =>Array [ [ [1,2], -* [3,4] ], -* -* [ [5,6], -* [7,8] ] -* ] -* -*/ - - tensor2Array = (tensor) => { - return tensor.arraySync(); - } - - -/** -* Convert single/multi dimensional array to single/multi tensor -* -* @since 1.0.0 -* @param {Array} array- e.g. [1,2,3,4,5,6,7,8] -* @returns {tf.tensor} Returns tf.tensor -* @example -* -* t = array2Tensor([[ [1,2], -* [3,4] ], -* -* [ [5,6], -* [7,8] ] -* ]) -* -* t.print() -* // =>tensor [ [ [1,2], -* [3,4] ], -* -* [ [5,6], -* [7,8] ] -* ] -* -*/ - - array2Tensor = (array) => { - return tf.tensor(array); - } - - -/** -* Convert single/multi dimensional tensor to flatten 1d dimensional Array -* -* @since 1.0.0 -* @param {tf.Tensor} tensor- e.g. tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ) -* @returns {Array} Returns mutable flatten 1d dimensional Array -* @example -* -* tensor2FlattenArray( tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ) ) -* -* // =>Array [ 1, 2, 3, 4, 5, 6, 7, 8 ] -* -*/ - - tensor2FlattenArray = (tensor) => { - return Array.from(tensor.dataSync()); - } - - - - -/** -* Calculate the mements of the MRI volume to find mean and variance -* -* @since 1.0.0 -* @param {tf.Tensor} cube3d- e.g slice3d repesents the MRI volume -* @param {number} threshold- filter voxels based on the threshold value -* @returns {Array} Returns [meanArray, varArray] -* @example -* -* cubeMoments( tf.tensor( [1,2,3,4,5,6,7,8], [2, 2, 2] ) , 0.5) -* -* // => Array [[0, 0, 0],[0.25, 0.25, 0.25]] -* -*/ - - - cubeMoments = (cube3d, threshold) => { - // mean and variance of a normalized cube data [0, 1] - let cube = tensor2Buffer(cube3d); - let coords = []; - for(let i = 0; i < cube3d.shape[0]; i++) { - for(let j = 0; j < cube3d.shape[1]; j++) { - for(let k = 0; k < cube3d.shape[2]; k++) { - if (cube.get(i,j,k) > threshold) { - coords.push([i, j, k]); - } - } - } - } - let coordsTensor = tf.tensor2d(coords); - let moments = tf.moments(coordsTensor, 0, false); - let meanArray = Array.from(tf.round(moments['mean']).dataSync()); - let varArray = Array.from(moments['variance'].dataSync()); - coordsTensor.dispose(); - return [meanArray, varArray]; - }; - - - - -/** -* For all MRI volume values > 0 , find the Centroid voxel Array [d, h, w] -* -* @since 1.0.0 -* @param {tf.Tensor} slices_3d - e.g slice_3d repesents the MRI volume slices as tensor -* @param {number} num_of_slices- Total Number of slices aka z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @returns {Array} Returns centroid voxel Array [d, h, w] -* @example -* -* [0.. 7] <==> Array.from({length: 8}, (x, i) => i) -* -* findHeadCentroid( tf.tensor( Array.from({length: 27}, (x, i) => i) , [3, 3, 3] ), 3, 3, 3 ) -* -* // => Array [ 1, 1, 1 ] -* -*/ - - - findHeadCentroid = (slices_3d, num_of_slices, slice_height, slice_width) => { - // Threshold tensor volume values to 0 or 1 such that if (voxelVal > 0 ? 1 : 0 ) - let binarizeVolumeTensor = binarizeVolumeDataTensor(slices_3d); - let binarizeVolumeBuffer = tensor2Buffer(binarizeVolumeTensor); - - const grid_coords = []; - let counter = 0; - - - // Find coordinates of nonzero voxels as (x_i, y_i, z_i) vectors - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - - let voxelValue = binarizeVolumeBuffer.get(depthIdx, rowIdx, colIdx); - if(voxelValue == 1) { - grid_coords[counter] = Array(depthIdx, rowIdx, colIdx); - counter += 1; - } - } - } - } - - // Create 2D Tesnor with three columns for depth, row, col index - let gridCoordsTensor = tf.tensor2d(grid_coords); - let axis = 0; - - let headCentroidTensor = tf.round(gridCoordsTensor.mean(axis)); - - // Find the Centroid voxel Array [d, h, w] - let headCentroidArray = Array.from(headCentroidTensor.dataSync()); - tf.dispose(gridCoordsTensor); - tf.dispose(headCentroidTensor); - - return headCentroidArray; - - } - - -/** -* Creates batches with the volume of slices each of D,H,W sub_volume and -* focus on brain area for the additional sub_volumes -* -* @since 1.0.0 -* @param {tf.Tensor} slices_3d - e.g slice_3d repesents the MRI volume slices as tensor -* @param {number} num_of_slices- Total Number of slices aka z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} batch_D- batch depth-dim a.k.a z-dim -* @param {number} batch_H- batch height -* @param {number} batch_W- batch width -* @param {Array} headSubCubesCoords - coordinates of overlap batches centered around the head -* @returns {Array} Returns Array of objects for all Slices Batch e.g. {id: number, coordinates:[Array], data: tf.Tensor } -* -* [0.. 7] <==> Array.from({length: 8}, (x, i) => i) -* headSubCubesCoords = [] -* -* sliceVolumeIntoOverlappedBatches( tf.tensor( Array.from({length: 27}, (x, i) => i) , [3, 3, 3] ), 3, 3, 3, 2, 2, 2, [] ) -* -* // => Array [ {id:1, coordinates:[0,0,0], data:{ kept: false, isDisposedInternal: false, dtype: "float32", … } }, {...}, ... ] -* -*/ - - sliceVolumeIntoOverlappedBatches = (slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W, headSubCubesCoords ) => { - - let allSlicedBatches = []; - let batch_id = 1; - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += batch_D) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += batch_H) { - for(let colIdx = 0; colIdx < slice_width; colIdx += batch_W) { - // for overlap calculations of last batches - let depthIdxDiff = 0; - let rowIdxDiff = 0; - let colIdxDiff = 0; - - if((depthIdx + batch_D) > num_of_slices) { - depthIdxDiff = (depthIdx + batch_D) - num_of_slices; - } - - if((rowIdx + batch_H) > slice_height) { - rowIdxDiff = (rowIdx + batch_H) - slice_height; - } - - if((colIdx + batch_W) > slice_width) { - colIdxDiff = (colIdx + batch_W) - slice_width; - } - - let startIndex = [depthIdx - depthIdxDiff, rowIdx - rowIdxDiff, colIdx - colIdxDiff]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - } - } - - // Additional sub_volumes or batches focus around the head centroid - for(let cubeIdx = 0; cubeIdx < headSubCubesCoords.length; cubeIdx++) { - - let startIndex = [headSubCubesCoords[cubeIdx][0], headSubCubesCoords[cubeIdx][1], headSubCubesCoords[cubeIdx][2]]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - - - return allSlicedBatches; - } - -/** -* Try to create batches with the volume of slices each of D,H,W sub_volume with minimum overlap option -* -* @since 1.0.0 -* @param {tf.Tensor} slices_3d - e.g slice_3d repesents the MRI volume slices as tensor -* @param {number} num_of_slices- Total Number of slices aka z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} batch_D- batch depth-dim a.k.a z-dim -* @param {number} batch_H- batch height -* @param {number} batch_W- batch width -* @returns {Array} Returns Array of objects for all Slices Batch e.g. {id: number, coordinates:[Array], data: tf.Tensor } -* -* [0.. 7] <==> Array.from({length: 8}, (x, i) => i) -* -* sliceVolumeIntoBatches( tf.tensor( Array.from({length: 27}, (x, i) => i) , [3, 3, 3] ), 3, 3, 3, 2, 2, 2 ) -* -* // => Array [ {id:1, coordinates:[0,0,0], data:{ kept: false, isDisposedInternal: false, dtype: "float32", … } }, {...}, ... ] -* -*/ - - - sliceVolumeIntoBatches = (slices_3d, num_of_slices, slice_height, slice_width, batch_D, batch_H, batch_W ) => { - let allSlicedBatches = []; - let batch_id = 1; - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += batch_D) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += batch_H) { - for(let colIdx = 0; colIdx < slice_width; colIdx += batch_W) { - // for overlap calculations of last batches - let depthIdxDiff = 0; - let rowIdxDiff = 0; - let colIdxDiff = 0; - - if((depthIdx + batch_D) > num_of_slices) { - depthIdxDiff = (depthIdx + batch_D) - num_of_slices; - } - - if((rowIdx + batch_H) > slice_height) { - rowIdxDiff = (rowIdx + batch_H) - slice_height; - } - - if((colIdx + batch_W) > slice_width) { - colIdxDiff = (colIdx + batch_W) - slice_width; - } - - let startIndex = [depthIdx - depthIdxDiff, rowIdx - rowIdxDiff, colIdx - colIdxDiff]; - let batch = slices_3d.slice(startIndex, [batch_D, batch_H, batch_W]); - - allSlicedBatches.push({id: batch_id , coordinates: startIndex, data: batch}); - batch_id += 1; - } - } - } - - return allSlicedBatches; - } - - -/** -* Return 2-Dim Array of the all slices data where each slice data is a 1d array -* -* @since 1.0.0 -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {object} niftiHeader- The header of nifti file. -* @param {ArrayBuffer} niftiImage- The image data of nifti file. -* @returns {Array} Returns All slices data where each slice data is a 1-dim array- e.g. allSlices[0] = [0,0,0, ..., 0] -* @example -* -* getAllSlicesData1D(256, niftiHeader, niftiImage) -* // => [ [0,0, 0, ...], [0,0,...,0], ... ]; -* -*/ - - - getAllSlicesData1D = (num_of_slices, niftiHeader, niftiImage) => { - let allSlices = []; - for(let sliceIdx = 0; sliceIdx < num_of_slices; sliceIdx++) { - let slice = getSliceData1D(sliceIdx, niftiHeader, niftiImage); - allSlices.push(slice); - } - - return allSlices; - } - - -/** -* Return 2-Dim Array of the all slices data where each slice data is a 2d array -* -* @since 1.0.0 -* @param {Array} allSlices- Array of all slices data as 1D -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @returns {Array} Returns All slices data where each slice data is tensor2d. e.g. allSlices[0] = Tensor [[0,0,0, ..., 0], .. [..]] -* @example -* -* arrSlices2d = getAllSlices2D([ [0,0,0,255,255,255,255,255,255,0,0,0], [0,0,0,255,255,255,255,255,255,0,0,0] ], 4, 3) -* // => Array of tensors [ { kept: false, isDisposedInternal: false, dtype: "float32", … }, {...}]; -* -* arrSlices2d[0].print() -* // => Tensor -* [[0 , 0 , 0 ], -* [255, 255, 255], -* [255, 255, 255], -* [0 , 0 , 0 ]] -* -*/ - - getAllSlices2D = (allSlices, slice_height, slice_width) => { - let allSlices_2D = []; - for(let sliceIdx = 0; sliceIdx < allSlices.length; sliceIdx ++){ - allSlices_2D.push(tf.tensor(allSlices[sliceIdx], [slice_height, slice_width])); - } - - return allSlices_2D; - } - - -/** -* Return volumatric 3-Dim tensor of all slices data -* @since 1.0.0 -* @param {Array} allSlices_2D- Array of 2d tensors of all slices data -* @returns {tf.Tensor} Returns Tensor of all slices data -* @example -* -* slices3d = getSlices3D([ tf.tensor( Array.from({length: 16}, (x, i) => i) , [4, 4]), -* tf.tensor( Array.from({length: 16}, (x, i) => i) , [4, 4]) -* ]) -* -* // => object { kept: false, isDisposedInternal: false, shape: (3) […], dtype: "float32", -* size: 32, strides: (2) […], dataId: {…}, id: 355, rankType: "3", scopeId: 29 -* } -* -* slices3d.print() -* // => Tensor -* [[[0 , 1 , 2 , 3 ], -* [4 , 5 , 6 , 7 ], -* [8 , 9 , 10, 11], -* [12, 13, 14, 15]], -* -* [[0 , 1 , 2 , 3 ], -* [4 , 5 , 6 , 7 ], -* [8 , 9 , 10, 11], -* [12, 13, 14, 15]]] -* -*/ - - getSlices3D = (allSlices_2D) => { - return tf.stack(allSlices_2D); - } - - -/** -* Normalize the tensor data to the range 0 - 1 using min-max scaling -* @since 1.0.0 -* @param {tf.Tensor} volumeData- Tensor1d/Tensor2d/Tensor3d, e.g. Tensor3d of all MRI volume data -* @returns {tf.Tensor} Returns Tensor of all normalized data -* @example -* -* normSlices = minMaxNormalizeVolumeData ( tf.tensor( Array.from({length: 8}, (x, i) => i) , [2, 2, 2]) ) -* -* // => Object { kept: false, isDisposedInternal: false, shape: (3) […], dtype: "float32", -* size: 8, strides: (2) […], dataId: {…}, id: 369, rankType: "3", scopeId: 39 } -* -* normSlices.print() -* // => Tensor -* [[[0 , 0.1428571], -* [0.2857143, 0.4285715]], -* -* [[0.5714286, 0.7142857], -* [0.8571429, 1 ]]] -* -*/ - - minMaxNormalizeVolumeData = (volumeData) => { - //Normalize the data to the range 0 - 1 using min-max scaling - const volumeData_Max = volumeData.max(); - const volumeData_Min = volumeData.min(); - const normalizedSlices_3d = volumeData.sub(volumeData_Min).div(volumeData_Max.sub(volumeData_Min)); - return normalizedSlices_3d; - } - -/** -* For future use -* Calculate the tensor data quantiles -* @since 3.0.0 -* @param {tf.Tensor} tensor - Tensor1d/Tensor2d/Tensor3d, e.g. Tensor3d of all MRI volume data -* @param {number} lowerQuantile -* @param {number} upperQuantile -* @returns {object} -* @example -* -* await calculateQuantiles ( tf.tensor( Array.from({length: 8}, (x, i) => i) , [2, 2, 2]) ) -* -* // => Object { qmin: 0, qmax: 7 } -* -*/ - - - calculateQuantiles = async(tensor, lowerQuantile = 0.01, upperQuantile = 0.99) => { - // Flatten the tensor - const flatTensor = tensor.flatten(); - - // Convert the flattened tensor to an array to sort it - const flatArray = await flatTensor.array(); - flatArray.sort((a, b) => a - b); // Sort the array in ascending order - - // Convert the sorted array back to a tensor - const sortedTensor = tf.tensor1d(flatArray); - - // Calculate the indices for the quantiles - const numElements = sortedTensor.shape[0]; - const lowIndex = Math.floor(numElements * lowerQuantile); - const highIndex = Math.ceil(numElements * upperQuantile) - 1; // Subtract 1 because indices are 0-based - - // Slice the sorted tensor to get qmin and qmax - const qmin = sortedTensor.slice(lowIndex, 1); // Get the value at the low index - const qmax = sortedTensor.slice(highIndex, 1); // Get the value at the high index - - // Get the actual values from the tensors - const qminValue = (await qmin.array())[0]; - const qmaxValue = (await qmax.array())[0]; - - // Clean up tensors to free memory - flatTensor.dispose(); - sortedTensor.dispose(); - qmin.dispose(); - qmax.dispose(); - - return { qmin: qminValue, qmax: qmaxValue }; - } - - -/** -* For future use -* Normalize the tensor data using quantiles -* @since 3.0.0 -* @param {tf.Tensor} tensor - Tensor1d/Tensor2d/Tensor3d, e.g. Tensor3d of all MRI volume data -* @param {number} lowerQuantile -* @param {number} upperQuantile -* @returns {tf.Tensor} -* @example -* -* normTensor = await quantileNormalizeVolumeData ( tf.tensor( Array.from({length: 8}, (x, i) => i) , [2, 2, 2]) ) -* -* // => Object Object { kept: false, isDisposedInternal: false, shape: (3) […], dtype: "float32", size: 8, -* strides: (2) […], dataId: {…}, id: 9, rankType: "3", scopeId: 5 } -* -* normTensor.print() -* -* //=> Tensor -* [[[0 , 0.1428571], -* [0.2857143, 0.4285715]], -* -* [[0.5714286, 0.7142857], -* [0.8571429, 1 ]]] -*/ - - - quantileNormalizeVolumeData = async (tensor, lowerQuantile = 0.05, upperQuantile = 0.95) => { - // Call calculateQuantiles and wait for the result - const { qmin, qmax } = await calculateQuantiles(tensor, lowerQuantile, upperQuantile); - - // Convert qmin and qmax back to scalars - const qminScalar = tf.scalar(qmin); - const qmaxScalar = tf.scalar(qmax); - - // Perform the operation: (tensor - qmin) / (qmax - qmin) - const resultTensor = tensor.sub(qminScalar).div(qmaxScalar.sub(qminScalar)); - - // Dispose of the created scalars to free memory - qminScalar.dispose(); - qmaxScalar.dispose(); - - // Return the resulting tensor - return resultTensor; - } - - - -/** -* Get MRI mask after threshold noisy voxels around the brain for better cropping later -* @since 3.0.0 -* @param {tf.Tensor} tensor - Tensor3d, e.g. Tensor3d of all MRI volume data -* @param {number} percentage - Threshold percentage is just a number between 0 and 1 -* @returns {tf.Tensor} -* -*/ - - -applyMriThreshold = async(tensor, percentage) => { - // Perform asynchronous operations outside of tf.tidy - const maxTensor = tensor.max(); - const thresholdTensor = maxTensor.mul(percentage); - const threshold = await thresholdTensor.data(); // Extracts the threshold value - - // Dispose tensors not needed anymore - maxTensor.dispose(); - thresholdTensor.dispose(); - - // Use tf.tidy for synchronous operations - return tf.tidy(() => { - const dataForProcessing = tensor.clone(); - - // Thresholding (assuming background has very low values compared to the head) - const mask = dataForProcessing.greater(threshold[0]); - //-- const denoisedMriData = dataForProcessing.mul(mask); - - // No need to manually dispose dataForProcessing and mask, as tf.tidy() will dispose them auto. - return mask; - }); - - //-- return denoisedMriData; -} - - - - -/** -* Get MRI copping coordinates after threshold -* @since 3.0.0 -* @param {tf.Tensor} tensor - Tensor3d, e.g. Tensor3d of all MRI volume data -* @param {number} percentage - Threshold percentage is just a number between 0 and 1 -* @returns {Array} -* @example -* -* arr = Array.from({length: 27}, (x, i) => i/10) -* => Array(27) [ 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, … , 2.6] -* -* cropped = await cropTensorWithThreshold ( tf.tensor( Array.from({length: 27}, (x, i) => i/10) , [3, 3, 3]), 0.2 ) -* -* => Array [ {…}, {…} ] -* -* cropped[0].print() -* -*/ - - - cropTensorWithThreshold = async(tensor, percentage) => { - - // Find the maximum value of the tensor - const maxTensor = tensor.max(); - - // Multiply the maximum value by the thresholdRatio to get % of the max - const thresholdTensor = maxTensor.mul(percentage); - - // Extract the value from the tensor - const threshold = await thresholdTensor.data(); - - const dataForProcessing = tensor.clone(); - - // Thresholding (assuming background has very low values compared to the head) - const mask = dataForProcessing.greater(threshold[0]); - const masked_data = dataForProcessing.mul(mask); - - // Find the bounding box around the head (non-zero region) in the filtered data - const indices = await tf.whereAsync(masked_data.greater(0)); - dataForProcessing.dispose(); - mask.dispose(); - masked_data.dispose(); - - // Extract z, y, x coordinates from the indices - const zs = indices.slice([0, 0], [indices.shape[0], 1]); // z coordinates - const ys = indices.slice([0, 1], [indices.shape[0], 1]); // y coordinates - const xs = indices.slice([0, 2], [indices.shape[0], 1]); // x coordinates - - // Compute min and max indices for each dimension - const min_z = zs.min().arraySync(); - const max_z = zs.max().arraySync(); - const min_y = ys.min().arraySync(); - const max_y = ys.max().arraySync(); - const min_x = xs.min().arraySync(); - const max_x = xs.max().arraySync(); - - // Crop the original tensor using the bounding box from the filtered data - const cropped_tensor = tensor.slice([min_z, min_y, min_x], [max_z - min_z + 1, max_y - min_y + 1, max_x - min_x + 1]); - - // Clean up tensors to free memory - indices.dispose(); - zs.dispose(); - ys.dispose(); - xs.dispose(); - - // Return the cropped tensor along with the min and max indices - return [cropped_tensor, { - minZ: min_z, - maxZ: max_z, - minY: min_y, - maxY: max_y, - minX: min_x, - maxX: max_x - }]; - - } - - - -/** -* load pre-trained model from local drive -* -* @since 1.0.0 -* @param {string} modelUrl - the model URL e.g. "./models/mnm_tfjs_me_test/model.json" -* @returns {promise} Promise object represents the model to load -* @example -* -* load_model("./models/mnm_tfjs_me_test/model.json") -* // => Promise { : "fulfilled", : {…} } -* -*/ - - load_model = async( modelUrl) => { - return await tf.loadLayersModel(modelUrl); - } - -/** -* load uploaded pre-trained model from local drive -* -* @since 1.0.0 -* @param {File} modelFile - the model File e.g. { name: "model.json", lastModified: 1625122369308, webkitRelativePath: "", size: 250, type: "" } -* @param {File} weightFile - the weight File e.g. { name: "weight.bin", lastModified: 1625122369308, webkitRelativePath: "", size: 250, type: "" } -* @returns {promise} Promise object represents the model to load -* @example -* -* load_browser_model(uploadJSONInput.files[0], uploadWeightsInput.files[0]) -* // => Promise { : "fulfilled", : {…} } -* -*/ - - load_browser_model = async( modelFile, weightFile) => { - return await tf.loadLayersModel(tf.io.browserFiles( [ modelFile, weightFile ])); - } - -/** -* Generates range of colors for Segmentation classes -- (refine) -* -* @since 1.0.0 -* @param {number} numSegClasses - The number of segmentation classes. -* @returns {function} Returns custom color table function -* @example -* -* getCustomColorTable(3) -* // => function customColorTable() -* -*/ -getCustomColorTable = (numSegClasses) => { - - var customColorTable = function() { }; - let colors = []; - - if( (!opts.isAutoColors) && (numSegClasses <= manualColorsRange.length) ) { - //Manual coloring - colors = manualColorsRange; - } else { - //Auto coloring - colors = generateColors(100, 50, numSegClasses); - - if(!opts.isAutoColors) { // if manual coloring was requested but failed - if(numSegClasses > manualColorsRange.length) { - console.log("number of Segmentation classes > manualColorsRange --> Auto coloring enabled"); - webix.message("number of Segmentation classes > manualColorsRange --> Auto coloring enabled"); - } - - } - } - - - let colorsRgbObj = []; - - // Array of threshold grey value of each class - let classGreyValue = []; - - if(opts.isColorEnable) { - - //Find the threshold grey value of each class - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - classGreyValue[classIdx] = Math.ceil(classIdx*255/(numSegClasses - 1)); - colorsRgbObj[classIdx] = getRgbObject(hslToRgb(colors[classIdx])); - - } - - - customColorTable.prototype.lookupRed = function (screenVal, imageVal) { - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].r; - } - - } - - }; - - customColorTable.prototype.lookupGreen = function (screenVal, imageVal) { - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].g; - } - - } - }; - - customColorTable.prototype.lookupBlue = function (screenVal, imageVal) { - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].b; - } - - } - }; - - } - - return customColorTable; -} - - - -/** -* Get object from external json file -* -* @since 1.2.0 -* @param {string} jsonURL - External file URL -* @returns {object} -* @example -* -* getExternalJSON("colorLUT.json") -* // => {0: "rgb(0,0,0)", 1: "rgb(0,255,0)", 2: "rgb(255,0,255)"} -* -*/ - -getExternalJSON = (jsonURL) => { - let jsonObj; - // read json file in sync mode - $.ajax({ - url: jsonURL, - async: false, - dataType: 'json', - success: function (response) { - jsonObj = response - //-- colors : {0: "rgb(0,0,0)", 1: "rgb(0,255,0)", 2: "rgb(255,0,255)"} - } - }); - - return jsonObj; - -} - - -getCustomColorTableFromUrl = (numSegClasses, colorURL ) => { - - - var customColorTable = function() { }; - - let colors; - // read json file in sync mode - $.ajax({ - url: colorURL, - async: false, - dataType: 'json', - success: function (response) { - colors = response - //-- colors : {0: "rgb(0,0,0)", 1: "rgb(0,255,0)", 2: "rgb(255,0,255)"} - } - }); - - - - // console.log("colors: ", colors); - - - let colorsRgbObj = []; - - // Array of threshold grey value of each class - let classGreyValue = []; - - - // Find the threshold grey value of each class - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - classGreyValue[classIdx] = Math.round(classIdx*255/(numSegClasses - 1)); - // if file exist - colorsRgbObj[classIdx] = getRgbObject(colors[classIdx]); - //console.log(" colorsRgbObj[classIdx] ", colorsRgbObj[classIdx]) - } - - - customColorTable.prototype.lookupRed = function (screenVal, imageVal) { - - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].r; - } - - } - - }; - - customColorTable.prototype.lookupGreen = function (screenVal, imageVal) { - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].g; - } - - } - }; - - customColorTable.prototype.lookupBlue = function (screenVal, imageVal) { - for(let classIdx = 0; classIdx < numSegClasses; classIdx ++ ) { - - if (screenVal == 0) { - return 0; - } else if (screenVal == classGreyValue[classIdx]) { - return colorsRgbObj[classIdx].b; - } - - } - }; - - return customColorTable; -} - - -/** -* Fetch Labels data from labels.json file -- (refine) -* -* @since 1.0.0 -* @param {string} labelsURL - label url e.g. "./models/meshnet_dropout/mnm_dropout/labels.json". -* @example -* -* fetchLabelStructure("./models/meshnet_dropout/mnm_dropout/labels.json") -* -*/ - -fetchLabelStructure = (labelsURL) => { - - if(labelsURL !== null) { - - let labelsDataObj; - $.ajax({ - url: labelsURL, - async: false, - dataType: 'json', - success: function (response) { - labelsDataObj = response - //-- labelsDataObj { 0: "background", 1: "Grey Matter", 2: "White Matter" } - } - }); - - - var customAtlas = function() { }; - - customAtlas.prototype.getLabelAtCoordinate = function (xWorld, yWorld, zWorld, xIndex, yIndex, zIndex ) { - let labels = labelsDataObj; - let voxelValue = papayaContainers[1].viewer.getCurrentValueAt(xIndex,yIndex,zIndex); - return [labels[voxelValue]]; //-- [labels[0]] = "background" - - }; - - papaya.Container.atlas = new customAtlas(); - - } else { - console.log(" No labels file found for this model") - } -} - - - -/** -* Fetch Labels data from labels.json file and annotate while mouse moving --(refine) -* -* @since 1.0.0 -* @param {string} labelsURL - label url e.g. "./models/meshnet_dropout/mnm_dropout/labels.json". -* @param {number} papayaContainerIdx - 0 for MRI viewer and 1 for laber viewer. -* @example -* -* addMouseMoveHandler("./models/meshnet_dropout/mnm_dropout/labels.json", 0) -* -*/ - -addMouseMoveHandler = (labelsURL, papayaContainerIdx = 1) => { - - if(labelsURL !== null) { - - let labelsDataObj; //-- labelsDataObj { 0: "background", 1: "Grey Matter", 2: "White Matter" } - $.ajax({ - url: labelsURL, - async: false, - dataType: 'json', - success: function (response) { - labelsDataObj = response - } - }); - - let canvasMain = papayaContainers[papayaContainerIdx].viewer.canvas; - - mouseMoveHandler = () => { - let curVoxelPosition = papayaContainers[papayaContainerIdx].viewer.cursorPosition; - let xIndex = curVoxelPosition["x"]; - let yIndex = curVoxelPosition["y"]; - let zIndex = curVoxelPosition["z"]; - - try { - - let voxelValue = papayaContainers[papayaContainerIdx].viewer.getCurrentValueAt(xIndex,yIndex,zIndex); - document.getElementById("annotOfContainer_" + papayaContainerIdx).value = labelsDataObj[voxelValue]; - - } catch(err) { - console.log("Wait loading") - } - } - - canvasMain.addEventListener('mousemove', mouseMoveHandler); - - mouseOutHandler = () => { - document.getElementById("annotOfContainer_" + papayaContainerIdx).value = ""; - } - - canvasMain.addEventListener('mouseout', mouseOutHandler); - - - } else { - console.log(" No labels file found for this model") - } -} - - - -/** -* remove mouse handler after reset the label viewer -* -* @since 1.0.0 -* @param {number} papayaContainerIdx - 0 for MRI viewer and 1 for laber viewer. -* @example -* -* removeMouseMoveHandler( 1 ) -* -*/ - -removeMouseMoveHandler = ( papayaContainerIdx ) => { - - let canvasMain = papayaContainers[papayaContainerIdx].viewer.canvas; - - canvasMain.removeEventListener('mousemove', mouseMoveHandler); - - mouseOutHandler = () => { - document.getElementById("annotOfContainer_" + papayaContainerIdx).value = ""; - } - - canvasMain.removeEventListener('mouseout', mouseOutHandler); - -} - - - -/** -* Remove any existing overlay from MRI Viewer on the left -* -* @since 1.0.0 -* @param {number} overlayIdx- papaya viewer overlay index -* -*/ - - -resetMriViewerOverlay = ( overlayIdx = 1) => { - - if(numOfOverlays == 1) { - papaya.Container.removeImage(0, overlayIdx); - removeMouseMoveHandler(0); - numOfOverlays = numOfOverlays -1; - } - -} - -/** -* Reset label viewer (1) -* -* @since 1.0.0 -* -*/ - - -resetLabelViewer = () => { - removeMouseMoveHandler(1); - papayaContainers[1].viewer.resetViewer(); -} - - -/** -* argMax large to find final labels by looping to overcome tf.argMax limitations -* -* @since 1.2.0 -* @param {buffer} outVolumeBuffer- resulted buffer e.g. shape: [ 1, 256, 256, 256, 3 ] -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} numSegClasses- The number of segmentation classes -* @returns {tf.Tensor} Returns prediction_argmax -* -*/ - -argMaxLarge = (outVolumeBuffer, num_of_slices, slice_height, slice_width, numOfClasses, dtype = 'float32') => { - - if( findMinNumOfArrBufs(num_of_slices, slice_height, slice_width, numOfClasses, dtype) == 1) { - - // console.log("Convert output tensor to buffer"); - // reshape modelOutTensor.shape : [ 1, 256, 256, 256, 3 ] to [ 256, 256, 256, 3 ] - //-- let outVolumeBuffer = tensor2Buffer(modelOutTensor.relu().reshape([num_of_slices, slice_height, slice_width, numOfClasses])); - - //-- let outVolumeBuffer = tensor2Buffer(modelOutTensor.reshape([num_of_slices, slice_height, slice_width, numOfClasses])); - //-- let outVolumeBuffer = tensor2LightBuffer(modelOutTensor.reshape([num_of_slices, slice_height, slice_width, numOfClasses]), dtype); - - console.log("Start argMaxLarge for buffer with last axis -1") - - let outBuffer = tf.buffer([num_of_slices, slice_height, slice_width ], dtype=tf.float32); - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - // index of buffer with max Freq or max number so the index of that buffer is the right concensus label - let indexOfMaxVotedBuffer = -1; - // let maxVoxelValue = -Infinity; - let maxVoxelValue = -1000000; - - for(let bufferIdx = 0; bufferIdx < numOfClasses; bufferIdx += 1) { - //Requested out of range element at 1,0,0,0. Buffer shape=1,256,256,256,3 - let voxelValue = outVolumeBuffer.get(depthIdx, rowIdx, colIdx, bufferIdx ); - - if(maxVoxelValue <= voxelValue) { - maxVoxelValue = voxelValue; - indexOfMaxVotedBuffer = bufferIdx; - } - } - - outBuffer.set(indexOfMaxVotedBuffer, depthIdx, rowIdx, colIdx); - - } - } - } - - console.log("argMaxLarge for buffer ..Done"); - - return outBuffer.toTensor(); - - } else { - webix.alert(" Terminated due to browser memory limitation"); - console.log("argMaxLarge needs buffer division .. "); - return 0; - } -} - - -//-- buffersThresholds [ 3, 6] --> 0-1-2, 3-4-5 -//- findBufferThreBinIdx( buffersThresholds = [3, 6], value=3) ==> return index 1 or bin-1, because bin-0 range 0-> 2 - -/** -* Find which buffer have the label value -* -* @since 1.0.0 -* @param {Array} buffersThresholds - Array of buffers threshold values e.g. [ 3, 6]--> 0-1-2, 3-4-5 -* @param {number} labelValue- Total Number of slices a.k.a z-dim -* @returns {number} Returns buffer index that has label value -* @example -* -* findBufferThreBinIdx( buffersThresholds = [3, 6], value=3) -* -* //==> 1 // or bin-1, because bin-0 range 0-> 2( 1 )) -* -*/ - -findBufferThreBinIdx = (buffersThresholds, labelValue) => { - - let binIdx = 0; - - for(let bin = 1; bin < buffersThresholds.length; bin ++) { - if( (labelValue >= buffersThresholds[bin-1]) && (labelValue < buffersThresholds[bin]) ) { - binIdx = bin; - } - } - - return binIdx; -} - - -/** -* Create 3D tf.buffer from large 4D segmenation model -* -* -* @since 1.0.0 -* @param {Array} allPredictions - Array of objects {"id": number, "coordinates": Array, "data":1dArray }) -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} numSegLabels- The number of segmentation classes/labels -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} batch_D- batch depth-dim a.k.a z-dim -* @param {number} batch_H- batch height -* @param {number} batch_W- batch width -* @returns {tf.buffer} Returns 3D buffer of ouput volume -*/ - -bufferLarge = (allPredictions, num_of_slices, slice_height, slice_width, numSegLabels, batch_D, batch_H, batch_W ) => { - - console.log(" Start buffer large ..."); - let bufferNumLabels = findSubArrBufSizes(num_of_slices, slice_height, slice_width, numSegLabels); // [25, 25, 25, 25] each buffer represent range of segmentation labels - let numArrBufPartitions = bufferNumLabels.length; - console.log(" Num of sub buffers : ", numArrBufPartitions); - let buffersThresholds = accumulateArrBufSizes(bufferNumLabels); // => [ 25, 50, 75, 100 ] - - //-- Create sub-buffers - let outVolumeBuffer = []; - for(let arrBufIdx = 0; arrBufIdx < numArrBufPartitions; arrBufIdx ++) { - outVolumeBuffer[arrBufIdx] = tf.buffer([num_of_slices, slice_height, slice_width, bufferNumLabels[arrBufIdx] ], dtype=tf.float32); - //labels : 0-49 - console.log("outVolumeBuffer-" + (arrBufIdx + 1) + " created"); - } - - console.log(" Num of created buffers : ", outVolumeBuffer.length); - - //Convert to buffer - for(let batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"]; - let pixelValues = allPredictions[batchIdx]["data"]; - let pixelValuesCounter = 0; - - for(let depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(let rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(let colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - // Find current voxel value of the related seg class buffer - // if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 - let binIdx = findBufferThreBinIdx(buffersThresholds, pixelValues[pixelValuesCounter]); - - if(binIdx == 0) { - let voxelValue = outVolumeBuffer[ binIdx ].get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - outVolumeBuffer[ binIdx ].set(voxelValue + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - - } else { - // maping to higher labels to range 0 to (numSegClasses - Buffer1NumLabels) - let voxelValue = outVolumeBuffer[ binIdx ].get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] - buffersThresholds[ binIdx-1 ] ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer[ binIdx ].set(voxelValue + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] - buffersThresholds[ binIdx-1 ] ); - } - - pixelValuesCounter += 1; - } - } - } - } - - - let outBuffer = []; - for(let arrBufIdx = 0; arrBufIdx < numArrBufPartitions; arrBufIdx ++) { - console.log("Start argMax for buffer-" + (arrBufIdx + 1) + " with last axis -1"); - outBuffer[arrBufIdx] = tf.buffer([num_of_slices, slice_height, slice_width ], dtype=tf.float32); - // convert output buffer to tensor - // let axis = -1; // last axis - // Set for each voxel the value of the index of the buffer that has the max voxel value, e.g. third buffer with index = 2 (cont..) - // has max voxel value = 10 then the related voxel in outVolumeTensor will have value of 2 - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - // index of buffer with max Freq or max number so the index of that buffer is the right concensus label - let indexOfMaxVotedBuffer = -1; - let maxVoxelValue = -1; - // Move through all buffers for the same voxel location and find which buffer indx has that max voxel value - for(let bufferIdx = 0; bufferIdx < bufferNumLabels[ arrBufIdx ] ; bufferIdx += 1) { - - let voxelValue = outVolumeBuffer[ arrBufIdx ].get(depthIdx, rowIdx, colIdx, bufferIdx ); - if(maxVoxelValue < voxelValue) { - maxVoxelValue = voxelValue; - indexOfMaxVotedBuffer = bufferIdx; - } - } - - outBuffer[ arrBufIdx ].set(indexOfMaxVotedBuffer, depthIdx, rowIdx, colIdx); - - } - } - } - - console.log("argMax in buffer-" + ( arrBufIdx +1) + " ..Done") - } - - - let outFinaleBuffer = tf.buffer([num_of_slices, slice_height, slice_width], dtype=tf.float32); - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - let voxelValue = []; - let voxel_histoMax = []; - - for(let arrBufIdx = 0; arrBufIdx < numArrBufPartitions; arrBufIdx ++) { - - voxelValue[ arrBufIdx ] = outBuffer[ arrBufIdx ].get(depthIdx, rowIdx, colIdx); - voxel_histoMax[ arrBufIdx ] = outVolumeBuffer[arrBufIdx].get(depthIdx, rowIdx, colIdx, voxelValue[ arrBufIdx ] ); - } - - idxMaxVal = voxel_histoMax.indexOf(voxel_histoMax.reduce((a, b) => { return Math.max(a, b) })); - - if(idxMaxVal == 0) { - outFinaleBuffer.set(voxelValue[idxMaxVal], depthIdx, rowIdx, colIdx); - } else { - outFinaleBuffer.set(voxelValue[idxMaxVal] + buffersThresholds[ idxMaxVal-1 ], depthIdx, rowIdx, colIdx); - } - } - } - } - - return outFinaleBuffer; -} - - - -/** -* Merge all subvolumes output from the inference model -* -* -* @since 1.0.0 -* @param {Array} allPredictions - Array of objects {"id": number, "coordinates": Array, "data":1dArray }) -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} numSegClasses- The number of segmentation classes -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} batch_D- batch depth-dim a.k.a z-dim -* @param {number} batch_H- batch height -* @param {number} batch_W- batch width -* @param {number} axis- -* @returns {tf.Tensor} Returns Tensor of ouput volume -*/ - -mergeSubVolumesV2 = (allPredictions, num_of_slices, slice_height, slice_width, numSegClasses, batch_D, batch_H, batch_W, axis) => { - - console.log("Wait while generate output labels... "); - - let outVolumeTensor; - - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - - let isValidBuf = isArrBufSizeValid(num_of_slices, slice_height, slice_width, numSegClasses, 'uint16'); - - console.log("Buffer is uint16 Valid ..") - - // buffer set ( depth, H, W) in order - // -- if(numSegClasses <= opts.browserArrayBufferMaxZDim ) { - if( isValidBuf ) { - let outVolumeBuffer = new Buffer([num_of_slices, slice_height, slice_width, numSegClasses ], 'uint16'); - console.log("New uint16 buffer called ..") - - //Convert to buffer - for(let batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"]; - let pixelValues = allPredictions[batchIdx]["data"]; - let pixelValuesCounter = 0; - - for(depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - // Find current voxel value of the related seg class buffer - // if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 - let voxelValue = outVolumeBuffer.get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer.set(voxelValue + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - - pixelValuesCounter += 1; - } - } - } - } - - // convert output buffer to tensor - - // Set for each voxel the value of the index of the buffer that has the max voxel value, e.g. third buffer with index = 2 (cont..) - // has max voxel value = 10 then the related voxel in outVolumeTensor will have value of 2 - - - - try { - console.log(" Try for merging with tf.argMax .."); - //-- outVolumeBuffer.toTensor() will convert to dtype float32 - outVolumeTensor = tf.argMax(outVolumeBuffer.toTensor(), axis); - - } catch(err1) { - // -- common error message: - //-- WebGL2RenderingContext.texImage2D: Argument 9 can't be - //-- an ArrayBuffer or an ArrayBufferView larger than 2 GB - if(axis == -1) { - - try { - let argMaxLargeTime = performance.now(); - console.log(" tf.argMax failed .. try argMaxLarge .."); - outVolumeTensor = argMaxLarge(outVolumeBuffer, num_of_slices, slice_height, slice_width, numSegClasses, 'uint8'); - console.log("argMaxLarge for fullVolume takes : ", ((performance.now() - argMaxLargeTime)/1000).toFixed(4) ); - - - } catch(err2) { - - let errTxt = "Merging argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - //window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err2.message; - statData["Extra_Err_Info"] = "Merging function tf.argMax failed and argMaxLarge failed."; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - } else { - // if channel first .. - let errTxt = "Merging argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err1.message; - statData["Extra_Err_Info"] = "Merging function tf.argMax failed and argMaxLarge not support yet channel first"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - } - - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outVolumeTensor transposed"); - outVolumeTensor = outVolumeTensor.transpose(); - } - - - } else { // Can be subdivided into 2 subBuffers - - let outFinaleBuffer; - - try { - outFinaleBuffer = bufferLarge(allPredictions, num_of_slices, slice_height, slice_width, numSegClasses, batch_D, batch_H, batch_W); - } catch(err3) { - - let errTxt = "Buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err3.message; - statData["Extra_Err_Info"] = "bufferLarge couldn't be created due to limited memory resources."; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - console.log("Final merged buffer -- Done"); - outVolumeTensor = outFinaleBuffer.toTensor(); - - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("Final merged buffer transposed"); - outVolumeTensor = outVolumeTensor.transpose(); - } - - } - - return outVolumeTensor; - -} - - -mergeSubVolumes_old = (allPredictions, num_of_slices, slice_height, slice_width, numSegClasses, batch_D, batch_H, batch_W, axis) => { - - console.log("Wait while generate output labels... "); - let unstackOutVolumeTensor; - - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - - let isValidBuf = isArrBufSizeValid(num_of_slices, slice_height, slice_width, numSegClasses); - - // buffer set ( depth, H, W) in order - // -- if(numSegClasses <= opts.browserArrayBufferMaxZDim ) { - if( isValidBuf ) { - let outVolumeBuffer = tf.buffer([num_of_slices, slice_height, slice_width, numSegClasses ], dtype=tf.float32) - - - //Convert to buffer - for(let batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"]; - let pixelValues = allPredictions[batchIdx]["data"]; - let pixelValuesCounter = 0; - - for(depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - // Find current voxel value of the related seg class buffer - // if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 - let voxelValue = outVolumeBuffer.get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer.set(voxelValue + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - - pixelValuesCounter += 1; - } - } - } - } - - // convert output buffer to tensor - - // Set for each voxel the value of the index of the buffer that has the max voxel value, e.g. third buffer with index = 2 (cont..) - // has max voxel value = 10 then the related voxel in outVolumeTensor will have value of 2 - - let outVolumeTensor; - - try { - console.log(" Try for merging tf.argMax .."); - outVolumeTensor = tf.argMax(outVolumeBuffer.toTensor(), axis); - - } catch(err1) { - // -- common error message: - //-- WebGL2RenderingContext.texImage2D: Argument 9 can't be - //-- an ArrayBuffer or an ArrayBufferView larger than 2 GB - if(axis == -1) { - - try { - let argMaxLargeTime = performance.now(); - console.log(" tf.argMax failed .. try argMaxLarge .."); - outVolumeTensor = argMaxLarge(outVolumeBuffer, num_of_slices, slice_height, slice_width, numSegClasses, 'uint16'); - console.log("argMaxLarge for fullVolume takes : ", ((performance.now() - argMaxLargeTime)/1000).toFixed(4) ); - - } catch(err2) { - - let errTxt = "Merging argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - // window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err2.message; - statData["Extra_Err_Info"] = "Merging function tf.argMax failed and argMaxLarge failed."; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - } else { - // if channel first .. - let errTxt = "Merging argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err1.message; - statData["Extra_Err_Info"] = "Merging function tf.argMax failed and argMaxLarge not support yet channel first"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - } - - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outVolumeTensor transposed"); - outVolumeTensor = outVolumeTensor.transpose(); - } - - unstackOutVolumeTensor = tf.unstack(outVolumeTensor); - - outVolumeTensor.dispose(); - - - } else if( findMinNumOfArrBufs(num_of_slices, slice_height, slice_width, numSegClasses) <= 2) { // Can be subdivided into 2 subBuffers - - let Buffer1NumLabels = Math.round(numSegClasses/2); - - let outVolumeBuffer1 = tf.buffer([num_of_slices, slice_height, slice_width, Buffer1NumLabels ], dtype=tf.float32) - //labels : 0-49 - console.log("outVolumeBuffer-1 created"); - - let outVolumeBuffer2 = tf.buffer([num_of_slices, slice_height, slice_width, numSegClasses - Buffer1NumLabels ], dtype=tf.float32) - // labels : 50 - (numSegClasses-1) - console.log("outVolumeBuffer-2 created"); - - - //Convert to buffer - for(let batchIdx = 0; batchIdx < allPredictions.length; batchIdx += 1) { - - let coord = allPredictions[batchIdx]["coordinates"]; - let pixelValues = allPredictions[batchIdx]["data"]; - let pixelValuesCounter = 0; - - for(let depthIdx = coord[0]; depthIdx < (batch_D + coord[0]); depthIdx += 1) { - for(let rowIdx = coord[1]; rowIdx < (batch_H + coord[1]); rowIdx += 1) { - for(let colIdx = coord[2]; colIdx < (batch_W + coord[2]); colIdx += 1) { - // Find current voxel value of the related seg class buffer - // if we have numSegClasses = 3 then we have 3 buffers, one for each seg classes 0, 1, 2 - if(pixelValues[pixelValuesCounter] < Buffer1NumLabels) { - let voxelValue1 = outVolumeBuffer1.get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer1.set(voxelValue1 + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] ); - } else { - // maping to higher labels to range 0 to (numSegClasses - Buffer1NumLabels) - let voxelValue2 = outVolumeBuffer2.get(depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] - Buffer1NumLabels ); - // increment current voxel value by 1 in the current class buffer - outVolumeBuffer2.set(voxelValue2 + 1, depthIdx, rowIdx, colIdx, pixelValues[pixelValuesCounter] - Buffer1NumLabels ); - - } - - pixelValuesCounter += 1; - } - } - } - } - - console.log("Start argMax for buffer-1 with last axis -1") - - let outBuffer1 = tf.buffer([num_of_slices, slice_height, slice_width ], dtype=tf.float32) ; - - - // convert output buffer to tensor - // let axis = -1; // last axis - // Set for each voxel the value of the index of the buffer that has the max voxel value, e.g. third buffer with index = 2 (cont..) - // has max voxel value = 10 then the related voxel in outVolumeTensor will have value of 2 - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - // index of buffer with max Freq or max number so the index of that buffer is the right concensus label - let indexOfMaxVotedBuffer = -1; - let maxVoxelValue = -1; - // Move through all buffers for the same voxel location and find which buffer indx has that max voxel value - for(let bufferIdx = 0; bufferIdx < Buffer1NumLabels; bufferIdx += 1) { - let voxelValue = outVolumeBuffer1.get(depthIdx, rowIdx, colIdx, bufferIdx ); - if(maxVoxelValue < voxelValue) { - maxVoxelValue = voxelValue; - indexOfMaxVotedBuffer = bufferIdx; - } - } - - outBuffer1.set(indexOfMaxVotedBuffer, depthIdx, rowIdx, colIdx); - - } - } - } - - console.log("argMax in buffer-1 ..Done") - - - console.log("Start argMax for buffer-2 with last axis -1") - - let outBuffer2 = tf.buffer([num_of_slices, slice_height, slice_width ], dtype=tf.float32) ; - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - // index of buffer with max Freq or max number so the index of that buffer is the right concensus label - let indexOfMaxVotedBuffer = -1; - let maxVoxelValue = -1; - - for(let bufferIdx = 0; bufferIdx < (numSegClasses - Buffer1NumLabels); bufferIdx += 1) { - let voxelValue = outVolumeBuffer2.get(depthIdx, rowIdx, colIdx, bufferIdx ); - if(maxVoxelValue < voxelValue) { - maxVoxelValue = voxelValue; - indexOfMaxVotedBuffer = bufferIdx; - } - } - - outBuffer2.set(indexOfMaxVotedBuffer, depthIdx, rowIdx, colIdx); - - } - } - } - - console.log("argMax in buffer-2 ..Done") - let outFinaleBuffer = tf.buffer([num_of_slices, slice_height, slice_width], dtype=tf.float32) - - for(let depthIdx = 0; depthIdx < num_of_slices; depthIdx += 1) { - for(let rowIdx = 0; rowIdx < slice_height; rowIdx += 1) { - for(let colIdx = 0; colIdx < slice_width; colIdx += 1) { - - let voxelValue1 = outBuffer1.get(depthIdx, rowIdx, colIdx); - let voxel1_histoMax = outVolumeBuffer1.get(depthIdx, rowIdx, colIdx, voxelValue1 ); - let voxelValue2 = outBuffer2.get(depthIdx, rowIdx, colIdx); - let voxel2_histoMax = outVolumeBuffer2.get(depthIdx, rowIdx, colIdx, voxelValue2 ); - if(voxel2_histoMax < voxel1_histoMax) { - outFinaleBuffer.set(voxelValue1, depthIdx, rowIdx, colIdx); - } else { - outFinaleBuffer.set(voxelValue2 + Buffer1NumLabels, depthIdx, rowIdx, colIdx); - - } - - } - } - } - - console.log("Final merged buffer -- Done"); - let outFinaleTensor = outFinaleBuffer.toTensor(); - - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("Final merged buffer transposed"); - outFinaleTensor = outFinaleTensor.transpose(); - } - - unstackOutVolumeTensor = tf.unstack(outFinaleTensor); - outFinaleTensor.dispose(); - - } else { - - let errTxt = "Merging buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - //statData["Error_Type"] = "SW Enhancement needed"; - statData["Extra_Err_Info"] = "Merging buffer needs divide into more than 2 partitions"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - return unstackOutVolumeTensor; - -} - - - - - -/** -* Generate output labels of all slices. (refine) -* Find current voxel value of the related seg class buffer, if we have numSegClasses = 3 then we have 3 buffers, -* one for each seg classes 0, 1, 2 -* -* @since 1.0.0 -* @param {tf.Tensor} unstackOutVolumeTensor -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} numSegClasses- The number of segmentation classes -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - - function convertTo3DArrayAndFlip(allOutputSlices3DCC1DimArray, shape) { - const [num_of_slices, slice_height, slice_width] = shape; - // Create a 3D array (as a flattened 1D typed array for efficiency) - const size = num_of_slices * slice_height * slice_width; - const threeDArray = new allOutputSlices3DCC1DimArray.constructor(size); - - for (let slice = 0; slice < num_of_slices; slice++) { - for (let row = 0; row < slice_height; row++) { - // Calculate the starting index for this row in the source and destination arrays - const srcStartIndex = (slice * slice_height * slice_width) + (row * slice_width); - const destStartIndex = (slice * slice_height * slice_width) + ((slice_height - row - 1) * slice_width); - - // Copy a slice row into the correct position in the 3D array, flipping it in the process - threeDArray.set( - allOutputSlices3DCC1DimArray.subarray(srcStartIndex, srcStartIndex + slice_width), - destStartIndex - ); - } - } - - // Convert the flattened typed array back to a nested regular array structure - const nestedArray = []; - for (let slice = 0; slice < num_of_slices; slice++) { - const twoDArray = []; - for (let row = 0; row < slice_height; row++) { - const start = (slice * slice_height * slice_width) + (row * slice_width); - const end = start + slice_width; - twoDArray.push(Array.from(threeDArray.subarray(start, end))); - } - nestedArray.push(twoDArray); - } - - return nestedArray; -} - generateOutputSlicesV2 = (img, OutVolumeTensorShape, OutVolumeTensorType, num_of_slices, numSegClasses, slice_height, slice_width) => { - - - // Convert all slices into 1 Dim array - let allOutputSlices3DCC = []; - let allOutputSlices3DContours = []; - - - if(opts.isPostProcessEnable) { - const niivueInstance = new Niivue(); - const dim = new Uint32Array(OutVolumeTensorShape); - const conn = 26; // Example connectivity - const binarize = true; - const onlyLargestClusterPerClass = true; - - const [labelCount, labeledImage] = niivueInstance.bwlabel(img, - dim, - conn, - binarize, - onlyLargestClusterPerClass); - for (let i = 0; i < img.length; i++) { - img[i] *= labeledImage[i]; - } - }; - const typedArrayConstructor = { - 'float32': Float32Array, - 'int32': Int32Array, - // Add other cases as needed for different dtypes - }[OutVolumeTensorType]; - - // Create a new TypedArray from img with the same type as outLabelVolume - allOutputSlices3DCC1DimArray = new Uint8Array(img); - - - let maskBrainExtraction = false; - - let labelArrayBuffer; - let modelType = inferenceModelsList[$$("selectModel").getValue() - 1]["type"]; - - switch ( modelType) { - case 'Brain_Masking': - { - const brainMask = new Uint8Array(allOutputSlices3DCC1DimArray.length); - for (let i = 0; i < allOutputSlices3DCC1DimArray.length; i++) { - brainMask[i] = allOutputSlices3DCC1DimArray[i] !== 0 ? 1 : 0; - } - labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, brainMask); - allOutputSlices3DCC1DimArray = brainMask; - // --labelsHistogramMap = null; - maskBrainExtraction = true; - break; - } - case 'Brain_Extraction': - { - const maskedData = new Uint8Array(allOutputSlices3DCC1DimArray.length); - const brainData = nifti2data(rawNiftiData); - - for (let i = 0; i < allOutputSlices3DCC1DimArray.length; i++) { - // Create the mask - 1 where the value is non-zero, 0 where it is zero. - const maskValue = allOutputSlices3DCC1DimArray[i] !== 0 ? 1 : 0; - // Apply the mask to the data - multiply by the mask value. - maskedData[i] = brainData[i] * maskValue; - } - labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, maskedData); - - // Update `allOutputSlices3DCC1DimArray` if needed. - allOutputSlices3DCC1DimArray = maskedData; - - // Other operations... - maskBrainExtraction = true; - - break; - } - default: - { - labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, allOutputSlices3DCC1DimArray); - break; - } - } - - - // Find voxel values frequency - let labelsHistogramMap = arrValuesFreq(allOutputSlices3DCC1DimArray); - console.log("Output Segmentation Labels (ROI) volumes : ", labelsHistogramMap); - - // Convert map to object - let labelsHistoObj = map2Object(labelsHistogramMap); - - // to plot 3d shape - console.log("convert out1DArr to 3DArr: let keep it 1D though") - // outVolumeStatus['out3DArr'] = tf.tensor(allOutputSlices3DCC1DimArray, [num_of_slices, slice_height, slice_width]).reverse(1).arraySync(); - // outVolumeStatus['out3DArr'] = convertTo3DArrayAndFlip(allOutputSlices3DCC1DimArray, num_of_slices, slice_height, slice_width); - // Let us leave the processing to the last moment, when the user choses to view this in 3D. Let's not waste time on conversion now - outVolumeStatus['out3DArr'] = allOutputSlices3DCC1DimArray; - outVolumeStatus['out3DArrShape'] = [num_of_slices, slice_height, slice_width]; - let colorURL = inferenceModelsList[$$("selectModel").getValue() - 1]["colorsPath"]; - - if(opts.isColorEnable) { - let blob = new Blob([labelArrayBuffer], {type: "application/octet-binary;charset=utf-8"}); - let file = new File([blob], "temp.nii"); - params_label["files"] = [file]; - - switch ( modelType) { - case 'Brain_Mask': - { - params_label[file["name"]] = {lut: "Grayscale", interpolation: false}; - break; - } - case 'Brain_Extraction': - { - params_label[file["name"]] = {lut: "Grayscale", interpolation: false}; - break; - } - default: - { - if(colorURL) { // colorURL file exists - let customColorTable = getCustomColorTableFromUrl(numSegClasses, colorURL); - params_label[file["name"]] = {lut: new customColorTable(), interpolation: false}; - - } else {// No colorURL file - if(numSegClasses > 3) { - params_label[file["name"]] = {lut: opts.atlasSelectedColorTable, interpolation: false}; - - } else { - let customColorTable = getCustomColorTable(numSegClasses); - params_label[file["name"]] = {lut: new customColorTable(), interpolation: false}; - } - } - - break; - } - } - - - } else { - params_label["binaryImages"] = [labelArrayBuffer]; - } - - // Set the view of container-2 as container-1 - params_label["mainView"] = papayaContainers[0].viewer.mainImage.sliceDirection == 1? "axial" : - papayaContainers[0].viewer.mainImage.sliceDirection == 2? "coronal" : "sagittal"; - - - - //-- Remove any existing overlay - resetMriViewerOverlay(1); - - // Add new overlay to MRI viewer - var addImageParams = []; - addImageParams["binaryImages"] = {lut: "Spectrum"}; - papaya.Container.addImage(0, [labelArrayBuffer], addImageParams); - numOfOverlays += 1; - - - // Label segmenation voxels according to label file - console.log("label path: ", inferenceModelsList[$$("selectModel").getValue() - 1]["labelsPath"]) - - // set 1 for label viewer - papaya.Container.resetViewer(1, params_label); - - let labelsURL = inferenceModelsList[$$("selectModel").getValue() - 1]["labelsPath"]; - - //Activate annotation for papaya container 0 - addMouseMoveHandler(labelsURL , 0); - - //Activate annotation for papaya container 1 - addMouseMoveHandler(labelsURL, 1); - - // Activate Swap view button for MRI viewer - // This needed to deactivated because of async behave - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).disabled = false; - - - // To sync swap view button - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).addEventListener("click", function(){ - papayaContainers[1].viewer.rotateViews() - - }) - - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[1].containerIndex).addEventListener("click", function(){ - papayaContainers[0].viewer.rotateViews() - }) - - - outVolumeStatus['labelsHistoObj'] = labelsHistoObj; - - //--Remvoe background volume - delete labelsHistoObj['0']; - let totalTissueVol = 0; - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - //-- Make sure to delete labelsHistoObj['0'] before - totalTissueVol += labelsHistoObj[labelKey]; - }) - - let roiData = []; - let roiLabels = []; - let chartXaxisStep = 1; - - // console.log("labelsHistoObj Keys: ", Object.keys(labelsHistoObj)); - - - let colorLutObj = getExternalJSON(colorURL); - //--e.g. colorLutObj- e.g. {"0": "rgb(0,0,0)", "1": "rgb(245,245,245)", "2": "rgb(196,58,250)", ... } - let labelsObj = getExternalJSON(labelsURL); - //-- e.g. labelsObj- { "0": "BG", "1": "Cerebral-White-Matter", "2": "Ventricle",..} - - // Color object, check if segmenation labels less or equal colors - if ( isObject(colorLutObj) ? verifyChildParentObjects( Object.keys(labelsHistoObj).length, Object.keys(colorLutObj).length) : false ) { - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - roiData.push({y: labelsHistoObj[labelKey] * 1 / totalTissueVol, color: rgbToHex( getRgbObject( colorLutObj[labelKey] ) ) }); - }) - - } else { - colorLutObj = {}; - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - colorLutObj[labelKey] = "rgb(" + labelKey + "," + labelKey + "," + labelKey + ")"; - }) - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - roiData.push({y: labelsHistoObj[labelKey] * 1 / totalTissueVol, color: rgbToHex( getRgbObject( colorLutObj[labelKey] ) ) }); - - }) - - } - - outVolumeStatus['colorLutObj'] = colorLutObj; - - - // label object, check if segmenation classes have less or equal labels in the label json file - if ( isObject(labelsObj) ? verifyChildParentObjects( Object.keys(labelsHistoObj), Object.keys(labelsObj) ): false ) { - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - roiLabels[idx] = labelsObj[labelKey]; - }) - - outVolumeStatus['labelsObj'] = labelsObj; - - } else { - labelsObj = {}; - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - labelsObj[labelKey] = labelKey; - }) - - Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - if(idx == 0 || idx == Math.round(Object.keys(labelsHistoObj).length * opts.chartXaxisStepPercent) || idx == Object.keys(labelsHistoObj).length -1 ){ - roiLabels[idx] = labelsObj[labelKey]; - } - - }) - - chartXaxisStep = Math.round(Object.keys(labelsHistoObj).length * opts.chartXaxisStepPercent); - // To only show All make label null - outVolumeStatus['labelsObj'] = null; - } - - - - // if( (! maskBrainExtraction) && (labelsURL !== null) && (colorURL !== null) ) { // If Atlas 50, 104 or GMWM Segmenations - - // let colorLutObj = getExternalJSON(colorURL); - // outVolumeStatus['colorLutObj'] = colorLutObj; - // //--e.g. colorLutObj- e.g. {"0": "rgb(0,0,0)", "1": "rgb(245,245,245)", "2": "rgb(196,58,250)", ... } - - // let labelsObj = getExternalJSON(labelsURL); - // outVolumeStatus['labelsObj'] = labelsObj; - // //-- e.g. labelsObj- { "0": "BG", "1": "Cerebral-White-Matter", "2": "Ventricle",..} - - - // Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - // roiData.push({y: labelsHistoObj[labelKey] * 1 / totalTissueVol, color: rgbToHex( getRgbObject( colorLutObj[labelKey] ) ) }); - // roiLabels[idx] = labelsObj[labelKey]; - // }) - - // //-- roiData = [ {y: 34.4, color: 'red'}, {y: 20.1, color: '#aaff99'}]; - // //-- roiLabels = ['Roi-1','Roi-2']; - - // } else { // For mask or brain extraction models or when label/color json not provided - - // let colorLutObj = {}; - // let labelsObj = {}; - - // Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - // colorLutObj[labelKey] = "rgb(" + labelKey + "," + labelKey + "," + labelKey + ")"; - // labelsObj[labelKey] = labelKey; - // }) - - - // Object.keys(labelsHistoObj).forEach((labelKey, idx) => { - // roiData.push({y: labelsHistoObj[labelKey] * 1 / totalTissueVol, color: rgbToHex( getRgbObject( colorLutObj[labelKey] ) ) }); - // if(idx == 0 || idx == Math.round(Object.keys(labelsHistoObj).length * opts.chartXaxisStepPercent) || idx == Object.keys(labelsHistoObj).length -1 ){ - // roiLabels[idx] = labelsObj[labelKey]; - // } - - // }) - - // chartXaxisStep = Math.round(Object.keys(labelsHistoObj).length * opts.chartXaxisStepPercent); - - // outVolumeStatus['colorLutObj'] = colorLutObj; - // // To only show All make label null - // outVolumeStatus['labelsObj'] = null; - // } - - - $$("hchart").config.settings.xAxis.categories = roiLabels; - $$("hchart").config.settings.xAxis.labels.step = chartXaxisStep; - $$("hchart").config.settings.series[0].data = roiData; - $$("hchart")._render(); - - $$("out3DIcon").enable(); - $$("outChartIcon").enable(); - document.getElementById("out3D-1").style.opacity = 1; - document.getElementById("outChart-1").style.opacity = 1; - document.getElementById("out3D-1").style.filter = "alpha(opacity=100)"; - document.getElementById("outChart-1").style.filter = "alpha(opacity=100)"; - - } - - -/** -* Threshold canvas of the viewer -* -* @since 1.0.0 -* @param {CanvasRenderingContext2D } ctx - renderContext e.g. papayaContainers[0].viewer.canvas.getContext("2d") -* @param {number} Threshold - To threshold the canvas context -* @param {object} RGB - e.g. { r: 110, g: 255, b: 182 } -* -*/ - - thresholdRenderContext = (ctx,Threshold, RGB ={ r: 110, g: 255, b: 182 }) => { - - let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - let pixels = imgData.data; - for (var i = 0; i < pixels.length; i += 4) { - - if(pixels[i] <= Threshold) { - pixels[i] = pixels[i + 1] = pixels[i + 2] = 0 - } - } - - ctx.putImageData(imgData, 0, 0); - } - - - - - - refreshDiv = (divId) => { - $( "#"+ divId ).load(window.location.href + " #"+ divId ); - } - - -/** -* Function to use with checking output file name, it must start with letter a-z or A-Z -* -* @since 1.0.0 -* @param {*} ch - character to check -* @returns {boolean} Returns - true or false -* @example -* -* isLetter(3) -* // => false -* -* isLetter("A") -* // => true -* -* isLetter("$") -* // => false -*/ - - isLetter = (ch) => { - return (/[a-zA-Z]/).test(ch) - } - - -/** -* Function to find maximum array value -* -* @since 1.0.0 -* @param {Array} array - character to check -* @returns {number} -* @example -* -* findArrayMax([3, 0, 2]) -* // => 3 -*/ - - findArrayMax = (array) => { - return array.reduce( (e1, e2) => { - return ( e1 > e2 ? e1 : e2 ); - }); - } - - - - -/** -* Function to check if GPU installed -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - - checkGPU = () => { - let runActivateFlag = true; - - if( ! checkWebGl2() ) { - - document.getElementById("webGl2Status").style.backgroundColor = "red"; - - if( ! checkWebGl1() ) { - webix.alert(" WebGL2 and WebGL1 are not supported
    Run deactivated"); - runActivateFlag = false; - - } else { - webix.confirm("WebGL2 is not supported
    Run process will be very slow").then(function(result){ - runActivateFlag = true; - }).fail(function() { - runActivateFlag = false; - }); - } - } else { - console.log("webGl2Status Ok") - document.getElementById("webGl2Status").style.backgroundColor = "green"; - } - - return runActivateFlag; - } - - -/** -* Function to check if browser is chrome -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - -isChrome = () => { - return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); -} - - -/** -* Function to online connection is established -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - -isOnline= () => { - return navigator.onLine; -} - - - -/** -* Function to check if checkWebGl1 is supported -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - - checkWebGl1 = () => { - const gl = document.createElement('canvas').getContext('webgl'); - if (!gl) { - if (typeof WebGLRenderingContext !== 'undefined') { - console.log('WebGL1 may be disabled. Please try updating video card drivers'); - } else { - console.log('WebGL1 is not supported'); - } - - return false; - } else { - console.log('WebGl1 is enabled'); - return true; - } - - } - - -/** -* Function to check if WebGL context is lost -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - - isWebGL2ContextLost = () => { - const gl = document.createElement('canvas').getContext('webgl2'); - return gl.isContextLost(); - } - -/** -* Function to check if checkWebGl2 is supported -* -* @since 1.0.0 -* @returns {boolean} Returns - true or false -* -*/ - - checkWebGl2 = () => { - const gl = document.createElement('canvas').getContext('webgl2'); - if (!gl) { - if (typeof WebGL2RenderingContext !== 'undefined') { - console.log('WebGL2 may be disabled. Please try updating video card drivers'); - webix.alert("WebGL2 may be disabled. Please try updating video card drivers"); - } else { - console.log('WebGL2 is not supported'); - } - return false; - } else { - console.log('WebGl2 is enabled'); - return true; - } - } - -/** -* Function to detect GPU Vendor -* -* @since 1.0.0 -* @returns {String} Returns - e.g.: 'NVIDIA Corporation'. -* -*/ - - detectGPUVendor_v0 = () => { - let gl = document.createElement('canvas').getContext('webgl'); - - if(gl) { - let debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - return debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : null; - - } else { - return null; - } - } - - detectGPUVendor = () => { - let gl = document.createElement('canvas').getContext('webgl'); - let debugInfo; - - if(gl) { - debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - - if (debugInfo) { - let result = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); - //--e.g. : NVIDIA Corporation - - if( (result.indexOf( "(" ) > -1) && (result.indexOf( ")" ) > -1) ) { - return result.substring( result.indexOf( '(' ) + 1, result.indexOf( ')' ) ); - } - - return result; - } - } - - return null; - } - -/** -* Function to detect GPU renderer or card type -* -* @since 1.0.0 -* @returns {String} Returns - e.g.: 'GeForce'. -* -*/ - - detectGPUCardType_v0 = () => { - let gl = document.createElement('canvas').getContext('webgl'); - - if(gl) { - - if(detectBrowser() === "Firefox" ) { - //-- return e.g: "GeForce GTX 980/PCIe/SSE2" - return gl.getParameter(gl.RENDERER); - - } - - let debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - return debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null; - - } else { - return null; - } - } - - - detectGPUCardType = () => { - let gl = document.createElement('canvas').getContext('webgl'); - let debugInfo; - - if(gl) { - if(detectBrowser() === "Firefox" ) { - //-- return e.g: "GeForce GTX 980/PCIe/SSE2" - return gl.getParameter(gl.RENDERER); - - } - - debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - - if (debugInfo) { - - let result = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); - //--e.g. : ANGLE (NVIDIA Corporation, GeForce GTX 1050 Ti/PCIe/SSE2, OpenGL 4.5.0 NVIDIA 390.144) as with Chrome - // Or: GeForce GTX 1050 Ti/PCIe/SSE2 as with fireFox - - if( (result.indexOf( "(" ) > -1) && (result.indexOf( ")" ) > -1) && (result.indexOf( "(R)" ) == -1) ) { - - result = result.substring( result.indexOf( '(' ) + 1, result.indexOf( ')' ) ); - - if ( result.split(',').length == 3) { - return result.split(',')[1].trim(); - } - - } - - return result; - - } - } - - return null; - } - - - -/** -* Function to detect browser version -* -* @since 1.0.0 -* @returns {String} Returns - e.g.: 96. -* -*/ - - detectBrowserVersion = () => { - - if ( navigator.userAgent.indexOf("OPR/") > -1) { - return parseInt(navigator.userAgent.split('OPR/')[1]); - - } else if (navigator.userAgent.indexOf("Edg/") > -1) { - return parseInt(navigator.userAgent.split('Edg/')[1]); - - } else if (navigator.userAgent.indexOf("Falkon/") > -1) { - return parseInt(navigator.userAgent.split('Falkon/')[1]); - - } else if (navigator.userAgent.indexOf("Chrome/") > -1) { - return parseInt(navigator.userAgent.split('Chrome/')[1]); - - } else if (navigator.userAgent.indexOf("Firefox/") > -1) { - return parseInt(navigator.userAgent.split('Firefox/')[1]); - - } else if (navigator.userAgent.indexOf("Safari/") > -1) { - return parseInt(navigator.userAgent.split('Safari/')[1]); - - } else if (navigator.userAgent.indexOf("MSIE/") > -1 || navigator.userAgent.indexOf("rv:") > -1) { - return parseInt(navigator.userAgent.split('MSIE/')[1]); - - } else { - - return Infinity; - } - } - -/** -* Function to find browser Location Info -* -* @since 1.0.0 -* @returns {Object} Returns -* -*/ - - getBrowserLocationInfo = () => { - let LocationDataObj = {}; - - if(isOnline()){ - try { - $.ajax({ - url: 'https://api.ipregistry.co/?key=tryout', - async: false, - dataType: "json", - success: function(response) { - LocationDataObj = {Country: response.location.country.name, Region: response.location.region.name, City: response.location.city, latitude: response.location.latitude, longitude: response.location.longitude}; - }, - error: function(XMLHttpRequest, textStatus, errorThrown) { - // alert("Status: " + textStatus); alert("Error: " + errorThrown); - $.ajax({ - url: "https://geolocation-db.com/json/", - async: false, - dataType: 'json', - success: function (response) { - LocationDataObj = {Country: response.country_name, Region: response.state, City: response.city, latitude: response.latitude, longitude: response.longitude}; - }, - error: function(XMLHttpRequest, textStatus, errorThrown) { - console.log("Resource for browser info not available "); - } - }); - } - }); - } catch(err) { - console.log("Online resources for browser info currently not available "); - } - - } - - return LocationDataObj; -} - - - - -/** -* Function to detect browser -* -* @since 1.0.0 -* @returns {String} Returns - e.g.: Firefox etc. -* -*/ - - detectBrowser = () => { - - if ( navigator.userAgent.indexOf("OPR/") > -1) { - return "Opera"; - - } else if (navigator.userAgent.indexOf("Edg/") > -1) { - return "Edge"; - - } else if (navigator.userAgent.indexOf("Falkon/") > -1) { - return "Falkon"; - - } else if (navigator.userAgent.indexOf("Chrome/") > -1) { - return "Chrome"; - - } else if (navigator.userAgent.indexOf("Firefox/") > -1) { - return "Firefox"; - - } else if (navigator.userAgent.indexOf("Safari/") > -1) { - return "Safari"; - - } else if (navigator.userAgent.indexOf("MSIE/") > -1 || navigator.userAgent.indexOf("rv:") > -1) { - return "IExplorer"; - - } else { - - return "Unknown"; - } - } - -/** -* Function to detect Operating System -* -* @since 1.0.0 -* @returns {String} Returns - e.g.: Linux -* -*/ - -detectOperatingSys = () => { - - if (navigator.userAgent.indexOf("Win") > -1) { - return "Windows"; - - } else if (navigator.userAgent.indexOf("Mac") > -1) { - return "MacOS"; - - } else if (navigator.userAgent.indexOf("Linux") > -1) { - return "Linux"; - - } else if (navigator.userAgent.indexOf("UNIX") > -1) { - return "UNIX"; - - } else { - return "Unknown"; - - } -} - - -/** -* Function to detect CPU number of cores -* -* @since 1.0.0 -* @returns {number} Returns - e.g.: 12 -* -*/ - - getCPUNumCores = () => { - return navigator.hardwareConcurrency; - } - - - -/** -* Function to submit data to google sheet -* -* @since 1.0.0 -* @param {object} dataObj - e.g. { Brainchop_Ver: 1.0.0, Data_Load: 10, ... } -* -*/ - -submitTiming2GoogleSheet = (dataObj) => { - - if(isOnline()){ - - // -- Fill form with data to submit - Object.keys(dataObj).forEach(dataKey =>{ - document.getElementById(dataKey).value = dataObj[dataKey]; - }) - - //-- Settings of submission - const scriptURL = 'https://script.google.com/macros/s/AKfycbwn-Ix6IVGOwUSU1VBU8hFcABT9PqwCwN90UxfK_fXp5CEfxvIoQHZXs2XQRZQo_N8I/exec' - const form = document.forms['google-sheet'] - - //-- Add event handler to the form. - form.addEventListener('submit', e => { - e.preventDefault() - fetch(scriptURL, { method: 'POST', body: new FormData(form)}) - .then(response => console.log("------Done------")) - .catch(error => console.error('Error!', error.message)) - }) - - //-- Submit the form - document.getElementById("SubmitStatisticalData").click(); - - } else { - console.log(" Offline Mode ") - - } - -} - - -/** -* For adjust time by adding 0 -* @since 1.0.0 -* @param {number} timeValue - e.g. 0 to 59 -* @returns {String} Returns - e.g.: 00 -* @example -* -* checkZero( 2 ) -* // => 02 -*/ - -checkZero = (timeValue) => { - return timeValue < 10 ? timeValue : "0" + timeValue; -} - - -/** -* Function to check whether the model channel bin is last -* -* @since 1.0.0 -* @param {Object} modelObj - Model to check -* @returns {boolean} Returns - true or false e.g. if true: [batchSize, batch_D, batch_H, batch_W, numOfChan] -* -*/ - - isModelChnlLast = (modelObj) => { - for(let layerIdx = 0; layerIdx < modelObj.layers.length; layerIdx ++ ) { - if(modelObj.layersByDepth[layerIdx][0]["dataFormat"]) { - return modelObj.layersByDepth[layerIdx][0]["dataFormat"] === "channelsLast"? true : false; - } - } - } - - -/** -* Function to find output segmentation total number -* Can be used to test browser feasibility before run the inference, e.g: test buffer of that size -* -* @since 1.0.0 -* @param {Object} modelObj - Model to check -* @returns {number} Returns - e.g.: 3 or 50 -* -*/ - - getModelOutputNumLabels = (modelObject) => { - if(modelObject.output.shape.length >= 4) { - return isModelChnlLast(modelObject) ? modelObject.output.shape[ modelObject.output.shape.length-1 ] : - modelObject.output.shape[1]; - } - - return null; - } - -/** -* Function to calculate the model total number of parameters -* -* @since 1.0.0 -* @param {Object} modelObj - Model to check -* @returns {number} Returns - e.g.: 5000 -* -*/ - - getModelNumParameters = (modelObj) => { - let numParameters = 0; - - for(let layerIdx = 0; layerIdx < modelObj.layers.length; layerIdx ++ ) { - numParameters += modelObj.layers[layerIdx].countParams(); - } - - return numParameters; - } - - -/** -* Function to calculate the max texture size for current browser -* -* @since 1.0.0 -* @returns {number} Returns - e.g.: 8192 -* -*/ - - getMaxTextureSize = () => { - let gl = checkWebGl2() ? document.createElement('canvas').getContext('webgl2') : - checkWebGl1() ? document.createElement('canvas').getContext('webgl1') : null; - - return gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : null; - } - - - -/** -* Function to calculate the model total number of layers -* -* @since 1.0.0 -* @param {Object} modelObj - Model to check -* @returns {number} Returns - e.g.: 20 -* -*/ - - getModelNumLayers= (modelObj) => { - return modelObj.layers.length; - } - - - -/** -* Function to test tf.argMax size allocation in browser -* -* @since 1.2.0 -* @param {number} depth- Total Number of slices a.k.a z-dim -* @param {number} height- - Slice or shape Height -* @param {number} width- Slice or shape Width -* @param {number} numSegLabels - Number of segmenation labels resulted from model -* @param {String} dataType - e.g.: 'float32' , 'int32' -* @param {number} axis - e.g.: -1 -* @returns {boolean} Returns - e.g.: true/false -* @example -* -* isArgMaxValid( 256, 256, 256, 200, 'float32' ) -* // => false -* -* isArgMaxValid( 256, 256, 256, 200, 'bool' ) -* // => true -*/ - - -isArgMaxValid = (depth, height, width, numSegLabels, dataType = 'float32', axis = -1) => { - let isValid = true; - let tensorToTest; - - try { - tensorToTest = tf.argMax( tf.ones([depth, height, width, numSegLabels], dataType) , axis); - tensorToTest.dispose(); - - } catch(err) { - // console.log("Error :", err); - isValid = false; - } - - return isValid; -} - - -/** -* Function to find feasible number of tf.argMax. -* -* @since 1.2.0 -* @param {number} depth- Total Number of slices a.k.a z-dim -* @param {number} height- - Slice or shape Height -* @param {number} width- Slice or shape Width -* @param {number} numSegLabels - Number of segmenation labels resulted from model -* @param {String} dataType - e.g.: 'float32' , 'int32' -* @param {number} numArgMaxParts - Number of minimum argMax partitions needed to breakdown the original argMax. -* @param {number} axis - e.g.: -1 -* @returns {number} Returns - e.g.: 1 , 2, 4, .. -* @example -* -* findMinNumOfArgMaxs( 256, 256, 256, 3 ) -* // => 1 -* -* findMinNumOfArgMaxs( 256, 256, 256, 300, 'float32' ) -* // => 4 -* -* findMinNumOfArgMaxs( 256, 256, 256, 300, 'bool' ) -* // => 1 -*/ - -findMinNumOfArgMaxs = (depth, height, width, numSegLabels, dataType = 'float32', numArgMaxParts = 1, axis = -1) => { - - if( ! isArgMaxValid(depth, height, width, numSegLabels, dataType, axis)) { - return findMinNumOfArgMaxs(depth, height, width, Math.ceil(numSegLabels/2) , dataType, numArgMaxParts * 2, axis); - } - - return numArgMaxParts; -} - - - - -/** -* Function to test arraybuffer size allocation in browser -* -* @since 1.2.0 -* @param {number} depth- Total Number of slices a.k.a z-dim -* @param {number} height- - Slice or shape Height -* @param {number} width- Slice or shape Width -* @param {number} numSegLabels - Number of segmenation labels resulted from model -* @param {String} dataType - e.g.: 'uint8'|'int8'|'uint16'|'int16'| 'float16' -* @returns {boolean} Returns - e.g.: true/false -* @example -* -* isArrBufSizeValid( 256, 256, 256, 200, 'int16' ) -* // => false -* -* isArrBufSizeValid( 256, 256, 256, 200, 'bool' ) -* // => true -*/ - - -isArrBufSizeValid = (depth, height, width, numSegLabels, dataType = 'float32') => { - let isValid = true; - let bufferToTest; - - try { - if( dataType === 'float32' || dataType === 'int32') { - bufferToTest = tf.buffer([depth, height, width, numSegLabels], dataType); - } else { - bufferToTest = new Buffer([depth, height, width, numSegLabels], dataType); - } - } catch(err) { - console.log("Error :", err); - isValid = false; - } - - return isValid; -} - -/** -* Function to find feasible number of arraybuffers to subvolume and argMax. -* -* @since 1.2.0 -* @param {number} depth- Total Number of slices a.k.a z-dim -* @param {number} height- - Slice or shape Height -* @param {number} width- Slice or shape Width -* @param {number} numSegLabels - Number of segmenation labels resulted from model -* @param {String} dataType - e.g.: 'float32' , 'int32' -* @param {number} numBufParts - Number of minimum array buffer partitions needed to breakdown the original buffer. -* @returns {number} Returns - e.g.: 1 , 2, 4, .. -* @example -* -* findMinNumOfArrBufs( 256, 256, 256, 3 ) -* // => 1 -* -* findMinNumOfArrBufs( 256, 256, 256, 300, 'float32' ) -* // => 4 -* -* findMinNumOfArrBufs( 256, 256, 256, 300, 'uint8' ) -* // => 4 -* -* findMinNumOfArrBufs( 256, 256, 256, 300, 'bool' ) -* // => 1 -*/ - - -findMinNumOfArrBufs = (depth, height, width, numSegLabels, dataType = 'float32', numBufParts = 1) => { - - if( ! isArrBufSizeValid(depth, height, width, numSegLabels, dataType)) { - return findMinNumOfArrBufs(depth, height, width, Math.ceil(numSegLabels/2) , dataType, numBufParts * 2); - } - - return numBufParts; -} - -/** -* Function to find feasible sizes of sub arraybuffers. -* -* @since 1.2.0 -* @param {number} depth- Total Number of slices a.k.a z-dim -* @param {number} height- - Slice or shape Height -* @param {number} width- Slice or shape Width -* @param {number} numSegLabels - Number of segmenation labels resulted from model e.g. 401 -* @param {String} dataType - e.g.: 'float32' , 'int32' -* @returns {Array} Returns - e.g.: [ 101, 101, 101, 98 ] -* @example -* -* findSubArrBufSizes( 256, 256, 256, 401 ) -* // => [ 101, 101, 101, 98 ] -* -* findSubArrBufSizes( 256, 256, 256, 401, 'float32' ) -* // => [ 101, 101, 101, 98 ] -* -* findSubArrBufSizes( 256, 256, 256, 401, 'bool' ) -* // => [ 401 ] -*/ - - -findSubArrBufSizes = (depth, height, width, numSegLabels, dataType = 'float32') => { - - let numPartitions = findMinNumOfArrBufs(depth, height, width, numSegLabels, dataType); - let arrBufSizes = []; - let totalPartitionSize = 0; - - for(let idx = 0; idx < numPartitions; idx ++ ) { - - if(idx == (numPartitions -1) ) { - arrBufSizes[idx] = numSegLabels - totalPartitionSize; - - } else { - arrBufSizes[idx] = Math.ceil(numSegLabels/numPartitions) - totalPartitionSize += arrBufSizes[idx]; - } - } - - return arrBufSizes; -} - - -/** -* Function to accumulate Array Buffers Size and find segmenation labels range for each buffer -* -* @since 1.2.0 -* @param {Array} BufferNumLabelsArr - e.g. [ 100, 100, 100, 99] -* @returns {Array} Returns - e.g.: [ 100, 200, 300, 399 ] -* @example -* -* accumulateArrBufSizes( [ 100, 100, 100, 99] ) -* // => [ 100, 200, 300, 399 ] -*/ - -accumulateArrBufSizes = (bufferSizesArr) => { - - let thresholds = []; - - for(let i = 0; i < bufferSizesArr.length; i++) { - - if(i == 0) { - thresholds[i] = bufferSizesArr[i] - } else { - - thresholds[i] = thresholds[i-1] + bufferSizesArr[i] - } - } - - return thresholds; -} - - - - -/** -* Inference Function for sub-volumes -* -* In version 3.0.0 this function not used, can reuse in future versions -* -* @since 1.0.0 -* @param {promise} model -* @param {tf.Tensor} slices_3d -* @param {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] -* @param {boolen} isChannelLast- check input shape for channel position. -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @param {number} batch_D- Batch Depth -* @param {number} batch_H- Batch Height -* @param {number} batch_W- Batch Width -* -*/ - - inferenceSubVolumes = async(model, slices_3d, num_of_slices, slice_height, slice_width, pipeline1_out = null) => { - - let refVoxel = [], boundVolSizeArr = []; - let enableCrop = inferenceModelsList[$$("selectModel").getValue() - 1]["enableCrop"]; - - let quantileNorm = inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - slices_3d = minMaxNormalizeVolumeData(slices_3d); - } - - if(enableCrop) { - - //--Phase-2, After remove the skull try to allocate brain volume and make inferece - console.log(" ---- Start SubVolume inference phase-II ---- "); - - let mask_3d; - - if(pipeline1_out == null) { - // binarize original image if there is no pre-model for masking task - mask_3d = slices_3d.greater([0]).asType('bool'); - - } else { - - mask_3d = pipeline1_out.greater([0]).asType('bool'); - pipeline1_out.dispose(); - - } - - console.log(" mask_3d shape : ", mask_3d.shape); - - const coords = await tf.whereAsync(mask_3d); - //-- Get each voxel coords (x, y, z) - - mask_3d.dispose(); - - const coordsArr = coords.arraySync(); - - let row_min = slice_height, row_max = 0, col_min = slice_width, col_max = 0, depth_min = num_of_slices, depth_max = 0; - - for(let i = 0; i < coordsArr.length; i++) { - - if ( row_min > coordsArr[i][0] ) { - row_min = coordsArr[i][0]; - } else if(row_max < coordsArr[i][0]) { - row_max = coordsArr[i][0]; - } - - if ( col_min > coordsArr[i][1] ) { - col_min = coordsArr[i][1]; - } else if(col_max < coordsArr[i][1]) { - col_max = coordsArr[i][1]; - } - - if ( depth_min > coordsArr[i][2] ) { - depth_min = coordsArr[i][2]; - } else if(depth_max < coordsArr[i][2]) { - depth_max = coordsArr[i][2]; - } - } - - - console.log( "row min and max :", row_min, row_max); - console.log( "col min and max :", col_min, col_max); - console.log( "depth min and max :", depth_min, depth_max); - - //-- Reference voxel that cropped volume started slice with it - refVoxel = [row_min, col_min, depth_min]; - // -- Starting form refVoxel, size of bounding volume - boundVolSizeArr = [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1]; - - - coords.dispose(); - - //-- Extract 3d object (e.g. brain) - slices_3d = slices_3d.slice([row_min, col_min, depth_min], [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1] ) - - - //-- Padding size add to cropped brain - let pad = inferenceModelsList[$$("selectModel").getValue() - 1]["cropPadding"]; - - // Create margin around the bounding volume - slices_3d = addZeroPaddingTo3dTensor(slices_3d, [pad, pad] , [pad, pad], [pad, pad]); - console.log(" cropped slices_3d with padding shape: ", slices_3d.shape); - - - if(opts.drawBoundingVolume) { - - let testVol = removeZeroPaddingFrom3dTensor(slices_3d, pad, pad, pad); - console.log(" testVol without padding shape : ", testVol.shape); - - testVol = resizeWithZeroPadding(testVol, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" testVol final shape after resizing : ", testVol.shape); - - draw3dObjBoundingVolume(tf.unstack(testVol)); - testVol.dispose(); - - return 0; - } - - } - - - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - if(transpose) { - slices_3d = slices_3d.transpose() - console.log("Input transposed for model"); - } else { - console.log("Transpose not enabled for model"); - } - - - model.then(function (res) { - - let batch_D, batch_H, batch_W; - let input_shape; - - let modelObject = {}; - - modelObject = res; - - let isChannelLast = isModelChnlLast(modelObject); - const batchSize = opts.batchSize; - const numOfChan = opts.numOfChan; - - //-- Test and adjust model input shape dim after padding .. - for (let i = 0; i < slices_3d.rank; i++) { - if(isChannelLast) { - if(slices_3d.shape[i] < modelObject.layers[0].batchInputShape[i+1]) { - console.log(" cropped slices_3d with pad < model input shape dim "); - modelObject.layers[0].batchInputShape[i+1] = slices_3d.shape[i]; - } - - } else { - if(slices_3d.shape[i] < modelObject.layers[0].batchInputShape[i+2]) { - console.log(" cropped slices_3d with pad < model input shape dim "); - modelObject.layers[0].batchInputShape[i+2] = slices_3d.shape[i]; - } - } - } - - - // Get model input shape - if(isChannelLast) { - batch_D = modelObject.layers[0].batchInputShape[1]; - batch_H = modelObject.layers[0].batchInputShape[2]; - batch_W = modelObject.layers[0].batchInputShape[3]; - input_shape = [batchSize, batch_D, batch_H, batch_W, numOfChan]; - } else { - batch_D = modelObject.layers[0].batchInputShape[2]; - batch_H = modelObject.layers[0].batchInputShape[3]; - batch_W = modelObject.layers[0].batchInputShape[4]; - input_shape = [batchSize, numOfChan, batch_D, batch_H, batch_W]; - } - - const isBatchOverlapEnable = inferenceModelsList[$$("selectModel").getValue() - 1]["isBatchOverlapEnable"]; - - let allBatches = []; - let headSubCubesCoords = []; - - if(isBatchOverlapEnable) { - // Number of additional batches focus on the brain/head volume - let numOverlapBatches = inferenceModelsList[$$("selectModel").getValue() - 1]["numOverlapBatches"]; - console.log(" num of overlapped batches: ", numOverlapBatches); - - // Find the centroid of 3D head volume and the variance - let cent_var = cubeMoments(slices_3d, 0.5); - // Mean or centroid - const headCentroid = cent_var[0]; - console.log(" Head 3D Centroid : ", headCentroid); - // Variance - const sigma = cent_var[1]; - console.log(" Head 3D Variance : ", sigma); - - headSubCubesCoords = findCoordsOfAddBrainBatches(numOverlapBatches, - new Array(headCentroid[0], headCentroid[1], headCentroid[2]), - new Array(sigma[0], sigma[1], sigma[2]), - new Array(slices_3d.shape[0], slices_3d.shape[1], slices_3d.shape[2]), - new Array(batch_D, batch_H, batch_W)); - - allBatches = sliceVolumeIntoOverlappedBatches(slices_3d, slices_3d.shape[0], slices_3d.shape[1], slices_3d.shape[2], batch_D, batch_H, batch_W, headSubCubesCoords); - - } else { - // This option will cover all slices, some slices that are not enough to create a batch will need overlap with prevous batch slices - // e.g. slice volume = 3*5*5 DHW , and batch is 2*2*2 , 2*3*3 =18 batches will be considered - let num_of_batches = Math.ceil(slices_3d.shape[2]/batch_W) * Math.ceil(slices_3d.shape[1]/batch_H) * Math.ceil(slices_3d.shape[0]/batch_D); - console.log("Num of Batches for inference: ", num_of_batches); - - allBatches = sliceVolumeIntoBatches(slices_3d, slices_3d.shape[0], slices_3d.shape[1], slices_3d.shape[2], batch_D, batch_H, batch_W); - } - - tf.dispose(slices_3d); - - statData["No_SubVolumes"] = allBatches.length; - statData["Brainchop_Ver"] = "SubVolumes"; - - let allPredictions = []; - - try { - let startTime = performance.now(); - let inferenceStartTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - let expected_Num_labels; - - let delay = inferenceModelsList[$$("selectModel").getValue() - 1]["inferenceDelay"]; - console.log("Inference delay :", delay); - - let layersLength = res.layers.length; - console.log("res.layers.length ", layersLength); - - statData["Input_Shape"] = JSON.stringify(res.layers[0].batchInputShape); - statData["Output_Shape"] = JSON.stringify(res.output.shape); - statData["Channel_Last"] = isChannelLast; - statData["Model_Param"] = getModelNumParameters(res); - statData["Model_Layers"] = getModelNumLayers(res); - statData["Model"] = inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"]; - statData["Extra_Info"] = null; - - let curProgBar = parseInt(document.getElementById("progressBar").style.width); - - let j = 0; - let timer = window.setInterval(function() { - let curTensor = []; - curTensor[0] = tf.tensor(allBatches[j].data.dataSync(), input_shape); - - let lastIdx = 0; - - for (let i = 1; i < layersLength; i++) { - try { - curTensor[i] = res.layers[i].apply( curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - - - return 0; - } - - if( j == allBatches.length-1 ) { - console.log("layer ", i); - console.log("layer output Tensor shape : ", curTensor[i].shape); - console.log("layer count params ", res.layers[i].countParams()); - } - - curTensor[i-1].dispose(); - lastIdx += 1; - } - - - // Get axis - let axis = isChannelLast ? -1 : 1; - let prediction_argmax = tf.argMax(curTensor[lastIdx], axis); - - if( j == allBatches.length - 1 ) { - expected_Num_labels = isChannelLast ? curTensor[lastIdx].shape[4] : curTensor[lastIdx].shape[1]; - } - - tf.dispose(curTensor[lastIdx]); - - allPredictions.push({"id": allBatches[j].id, "coordinates": allBatches[j].coordinates, "data": Array.from(prediction_argmax.dataSync()) }) - let curBatchMaxLabel = findArrayMax(Array.from(prediction_argmax.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - tf.dispose(prediction_argmax); - - - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons.fontcolor("red").bold() : ""; - document.getElementById("progressBar").style.width = (curProgBar + (j + 1)*(100 - curProgBar)/allBatches.length) + "%"; - - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - //-- let memoryStatusData=[{ memoryUse: Math.round(tf.memory().numBytesInGPU/(1024*1024*20))}]; - //-- $$("memoryMonitor").clearAll(); - //-- $$("memoryMonitor").parse(memoryStatusData); - - //-- document.getElementById("progressBar").innerHTML= Math.floor((j+1)*100/allBatches.length) + "%"; - - if( j == allBatches.length-1 ) { - window.clearInterval( timer ); - - let Inference_t = ((performance.now() - startTime)/1000).toFixed(4); - - let numSegClasses = maxLabelPredicted + 1; - console.log("Num of seg classes: ", numSegClasses); - - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - - startTime = performance.now(); - // Generate output volume or slices - console.log("Merging subvolumes... "); - let outLabelVolume = tf.tidy(() => { - return mergeSubVolumesV2(allPredictions, slices_3d.shape[0], slices_3d.shape[1], slices_3d.shape[2], numSegClasses, batch_D, batch_H, batch_W, axis); - }) - - allPredictions = []; - let Merge_t = ((performance.now() - startTime)/1000).toFixed(4); - - - if(enableCrop) { - let pad = inferenceModelsList[$$("selectModel").getValue() - 1]["cropPadding"]; - outLabelVolume = removeZeroPaddingFrom3dTensor(outLabelVolume, pad, pad, pad); - console.log(" outLabelVolume without padding shape : ", outLabelVolume.shape); - outLabelVolume = resizeWithZeroPadding(outLabelVolume, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" outLabelVolume final shape after resizing : ", outLabelVolume.shape); - } - - startTime = performance.now(); - console.log("Generating output..."); - try { - const img = new Uint32Array(outLabelVolume.dataSync()); - const Vshape = outLabelVolume.shape; - const Vtype = outLabelVolume.dtype; - tf.dispose(outLabelVolume); - generateOutputSlicesV2(img, Vshape, Vtype, num_of_slices, numSegClasses, slice_height, slice_width); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - console.log(" SubVolume inference num of tensors after generateOutputSlicesV2: " , tf.memory().numTensors ); - } catch(error) { - - - //-- Timing data to collect - - tf.engine().endScope(); - tf.engine().disposeVariables(); - - webix.alert("Failed while generating output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Merge_t"] = Merge_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Failed while generating output"; - - document.getElementById("progressBar").style.width = 0; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - let Postprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - document.getElementById("progressBar").style.width = 0; - //webix.message.hide("waitMessage"); - - - $$("downloadBtn").enable(); - $$("segmentBtn").enable(); - // $$("imageUploader").enable(); - tf.engine().disposeVariables(); - - - console.log("Processing the whole brain volume in tfjs tooks for multi-class output mask : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Merge_t"] = Merge_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK" - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - - } - - j++; - - }, delay); - - } - catch(err) { - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - - }); - - } - - ///////////////////////////////////////////////////////////////////////// -///////////////----------------SEQ LAYER-----------------//////////////// -//////////////////////////////////////////////////////////////////////// - -/** -* This function is designed to process a large tensor in smaller chunks to manage memory usage effectively. -* -* @since 3.0.0 -* @param {tf.Tensor} inputTensor e.g.[ D, H, W, Ch] or [ Ch, D, H, W]->[ 256, 256, 256, 5 ] or [ 5, 256, 256, 256 ] -* @param {tf.Tensor} vector - e.g. filterWeight: [-1.4474995, 0.6897876, -0.2812168, -0.0344299, 1.266812] -* @param {number} chunkSize -parameter important for memory, the larger it is, the more memory in use. e.g. 4 -* @return {tf.Tensor} -* -*/ -function processTensorInChunks(inputTensor, filterWeights, chunkSize) { - // Assuming inputTensor's shape: [batch, depth, height, width, inChannels] - // and filterWeights's shape: [filterDepth, filterHeight, filterWidth, inChannels, outChannels] - const stride = 1; - const pad = 0; - const dilationRate = 1; - const inChannels = inputTensor.shape[4]; - const numSlices = Math.ceil(inChannels / chunkSize); - - let accumulatedResult = null; - - for (let i = 0; i < numSlices; i++) { - const startChannel = i * chunkSize; - const endChannel = Math.min((i + 1) * chunkSize, inChannels); - const channels = endChannel - startChannel; - - const inputSlice = tf.tidy(() => { - // Slice the input tensor to get the current chunk - return inputTensor.slice([0, 0, 0, 0, startChannel], [-1, -1, -1, -1, channels]); - }); - - const filterSlice = tf.tidy(() => { - // Slice the filter weights to match the input tensor's current chunk - return filterWeights.slice([0, 0, 0, startChannel, 0], [-1, -1, -1, channels, -1]); - }); - - const resultSlice = tf.conv3d(inputSlice, filterSlice, stride, pad, 'NDHWC', dilationRate); - // Clean up the slices to free memory - inputSlice.dispose(); - filterSlice.dispose(); - - // Squeeze the result slice to remove dimensions of size 1 - const squeezedResultSlice = tf.squeeze(resultSlice); - resultSlice.dispose(); // Dispose of the original resultSlice after squeezing - - if (accumulatedResult === null) { - accumulatedResult = squeezedResultSlice; - } else { - // Accumulate the result by adding the new result slice to it - const newAccumulatedResult = accumulatedResult.add(squeezedResultSlice); - - // Dispose of the previous accumulatedResult and squeezedResultSlice - accumulatedResult.dispose(); - // Dispose of squeezedResultSlice only if it wasn't assigned to accumulatedResult - if (accumulatedResult !== squeezedResultSlice) { - squeezedResultSlice.dispose(); - } - // Update accumulatedResult with the new result - accumulatedResult = newAccumulatedResult; - } - - tf.tidy(() => { - tf.matMul(tf.zeros([1, 1]), tf.zeros([1, 1])); - }); - } - - return accumulatedResult; -} - -/** -* This function is show memory status while running sequential processing -* -* @since 3.0.0 -* @param {number} chIdx -* @param {number} totalChannels -* @return {promise} -* -*/ - -showMemStatus = async(chIdx, totalChannels) => { - - return new Promise((resolve, reject) => { - - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons.fontcolor("red").bold() : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - document.getElementById("memStatusParagraph").innerHTML = "Channels completed: " + (chIdx + 1) + " / " + totalChannels + - // https://js.tensorflow.org/api/latest/#memory - "

    " +"TF Memory Status: " + memStatus.fontcolor(tf.memory().unreliable ? "red" : "green").bold() + - // numBytes: Number of bytes allocated (undisposed) at this time - "
    " + "numBytes : " + Math.round(tf.memory().numBytes/(1024*1024)) + " MB" + - //numBytesInGPU : Number of bytes allocated (undisposed) in the GPU only at this time - "
    " + "numBytesInGPU : " + Math.round(tf.memory().numBytesInGPU/(1024*1024)) + " MB" + - "
    " + "numBytesInGPUAllocated : " + Math.round(tf.memory().numBytesInGPUAllocated/(1024*1024)) + " MB" + - "
    " + "numBytesInGPUFree : " + Math.round(tf.memory().numBytesInGPUFree/(1024*1024)) + " MB" + - // numDataBuffers : Number of unique data buffers allocated (undisposed) at this time, which is ≤ the number of tensors - "
    " + "numDataBuffers : " + tf.memory().numDataBuffers + - "
    " + "numTensors : " + tf.memory().numTensors + - "
    " + unreliableReasons ; - - resolve(); // When this fires, the code in a().then(/..../); is executed. - - }); - - -} - - -class SequentialConvLayer { - constructor(model, chunkSize, isChannelLast) { - this.model = model; - this.outChannels = model.outputLayers[0].kernel.shape[4]; - this.chunkSize = chunkSize; - this.isChannelLast = isChannelLast; - } - - /** - * Apply sequential convolution layer - * @since 3.0.0 - * @member SequentialConvLayer - * @param {tf.Tensor} inputTensor e.g. [ 1, 256, 256, 256, 5 ] - * @return {promise} - * - * convLayer.rank -> 3 - * typeof(convLayer) -> "object" - * convLayer: Object { dataFormat: "channelsLast", dilationRate: Array(3) [ 1, 1, 1 ], inputSpec: Array [ {…} ], - * name: "output", padding: "same", strides: Array(3) [ 1, 1, 1 ], ...} - * - * weights.shape -> Array(5) [ 1, 1, 1, 5, 3 ] - * weights.print() - * //=> Tensor - * [[[[[0.146999 , -1.4474995, -2.8961499], - * [1.1067894, 0.6897876 , -0.7573005], - * [-0.38512 , -0.2812168, -0.8637539], - * [0.9341159, -0.0344299, -2.3668685], - * [0.1052373, 1.266812 , 0.6542516 ]]]]] - * - * biases.shape -> Array [ 3 ] - * biases.print() - * //=> Tensor - * [-0.7850812, -2.3238883, 2.1639345] - * - * for idx = 0 -> filterWeights.shape -> Array(5) [ 1, 1, 1, 5, 1 ] - * filterWeights.print() - * //=> Tensor - * [[[[[0.146999 ], - * [1.1067894], - * [-0.38512 ], - * [0.9341159], - * [0.1052373]]]]] - * - * for idx = 0 -> filterBiases.shape -> Array [1] - * filterBiases.print() - * //=> Tensor - * [-0.7850812] - - */ - - async apply(inputTensor) { - - const oldDeleteTextureThreshold = tf.ENV.get('WEBGL_DELETE_TEXTURE_THRESHOLD') - tf.ENV.set('WEBGL_DELETE_TEXTURE_THRESHOLD', 0) - - const self = this - // Important to avoid "undefined" class var members inside the timer. - // "this" has another meaning inside the timer. - - document.getElementById("progressBarChild").parentElement.style.visibility = "visible"; - - return new Promise((resolve) => { - - const startTime = performance.now() - - const convLayer = self.model.layers[self.model.layers.length - 1] - const weights = convLayer.getWeights()[0] - const biases = convLayer.getWeights()[1] - const outputShape = self.isChannelLast ? inputTensor.shape.slice(1,-1) : inputTensor.shape.slice(2) - //-- e.g. outputShape : [256,256,256] or cropped Dim - //-- if inputTensor [ 1, D, H, W, 50 ], channelLast true -> outputShape : outputShape [D, H, W] - //-- if inputTensor [ 1, 50, D, H, W ], channelLast false -> outputShape : outputShape [D, H, W] - - let outB = tf.mul(tf.ones(outputShape), -10000) - //-- e.g. outB.shape [256,256,256] - let outC = tf.zeros(outputShape) - //-- e.g. outC.shape [256,256,256] - let chIdx = 0 - - // console.log("---------------------------------------------------------"); - console.log(" channel loop") - - const seqTimer = window.setInterval(async function() { - - tf.engine().startScope(); // Start TensorFlow.js scope - console.log('======================='); - const memoryInfo0 = await tf.memory(); - console.log(`| Number of Tensors: ${memoryInfo0.numTensors}`); - console.log(`| Number of Data Buffers: ${memoryInfo0.numDataBuffers}`); - console.log("Channel : ", chIdx); - - const result = await tf.tidy(() => { - const filterWeights = weights.slice([0, 0, 0, 0, chIdx], [-1, -1, -1, -1, 1]); - // -- e.g. filterWeights.shape [ 1, 1, 1, 5, 1 ] - const filterBiases = biases.slice([chIdx], [1]); - //-- e.g. filterBiases.shape [1] -> Tensor [-0.7850812] - const outA = processTensorInChunks(inputTensor, - filterWeights, - Math.min(self.chunkSize, self.outChannels)) - .add(filterBiases); - const greater = tf.greater(outA, outB); - const newoutB = tf.where(greater, outA, outB); - const newoutC = tf.where(greater, tf.fill(outC.shape, chIdx), outC); - // Dispose the old tensors before reassigning - tf.dispose([outB, outC, filterWeights, filterBiases, outA, greater]); - // Dummy operation to trigger cleanup - // tf.tidy(() => tf.matMul(tf.ones([1, 1]), tf.ones([1, 1]))); - return [newoutC, newoutB]; - }); - - // Log memory usage - const memoryInfo = await tf.memory(); - console.log('======================='); - console.log(`Number of Tensors: ${memoryInfo.numTensors}`) - console.log(`Number of Data Buffers: ${memoryInfo.numDataBuffers}`) - console.log(`Megabytes In Use: ${(memoryInfo.numBytes / 1048576).toFixed(3)} MB`) - if (memoryInfo.unreliable) { - console.log(`Unreliable: ${memoryInfo.unreliable}`) - } - - // Dispose of previous values before assigning new tensors to outC and outB - if (typeof outC !== 'undefined') { - outC.dispose() - } - if (typeof outB !== 'undefined') { - outB.dispose() - } - // Assign the new values to outC and outB - outC = tf.keep(result[0]); - outB = tf.keep(result[1]); - // // Assign the new values to outC and outB - // outC = result[0]; - // outB = result[1]; - tf.engine().endScope(); - - if(chIdx == (self.outChannels -1)) { - window.clearInterval( seqTimer ); - document.getElementById("progressBarChild").style.width = 0 + "%"; - tf.dispose(outB); - const endTime = performance.now(); - const executionTime = endTime - startTime; - console.log(`Execution time for output layer: ${executionTime} milliseconds`); - tf.ENV.set('WEBGL_DELETE_TEXTURE_THRESHOLD', oldDeleteTextureThreshold); - resolve(outC) - } else { - - chIdx++; - - document.getElementById("progressBarChild").style.width = (chIdx + 1) * 100 / self.outChannels + "%"; - - } - - // Artificially introduce a pause to allow for garbage collection to catch up - await new Promise((resolve) => setTimeout(resolve, 300)); - - }, 0); - }); - - - } - - - -} // <<<< End of class - - - -/** -* This function better memory managment during the model layer processing -* -* @since 3.0.0 -* @param {tf.Tensor} input -* @param {tf.Tensor} filter -* @param {tf.Tensor} biases -* @param {Array} stride e.g. [ 1, 1, 1 ] -* @param {string} pad e.g. "same" -* @param {Array} dilationRate e.g. [ 1, 1, 1 ] -* @param {number} sliceSize e.g. 3 -* @return {} -* -*/ - -function convByOutputChannelAndInputSlicing(input, filter, biases, stride, pad, dilationRate, sliceSize) { - const batchSize = input.shape[0]; - const depth = input.shape[1]; - const height = input.shape[2]; - const width = input.shape[3]; - const inChannels = input.shape[4]; - const outChannels = filter.shape[4]; - - // Create an empty array to hold the output channels - let outputChannels = null; - - // Slice the input tensor and process one output channel at a time - for (let channel = 0; channel < outChannels; channel++) { - const numSlices = Math.ceil(inChannels / sliceSize); - const biasesSlice = biases.slice([channel], [1]); - let outputChannel = null; - - for (let i = 0; i < numSlices; i++) { - const startChannel = i * sliceSize; - const endChannel = Math.min((i + 1) * sliceSize, inChannels); - - // Only proceed if there are channels to process - if (startChannel < inChannels) { - const resultSlice = tf.tidy(() => { - const inputSlice = input.slice([0, 0, 0, 0, startChannel], [-1, -1, -1, -1, endChannel - startChannel]); - const filterSlice = filter.slice([0, 0, 0, startChannel, channel], [-1, -1, -1, endChannel - startChannel, 1]); - // Perform the convolution for the current slice and output channel - return tf.conv3d(inputSlice, filterSlice, stride, pad, 'NDHWC', dilationRate); - }); - - if (outputChannel === null) { - outputChannel = resultSlice; - } else { - const updatedOutputChannel = outputChannel.add(resultSlice); - outputChannel.dispose(); - resultSlice.dispose(); - outputChannel = updatedOutputChannel; - } - } - } - - // Add the biases to the accumulated convolutions for this channel - const biasedOutputChannel = outputChannel.add(biasesSlice); - outputChannel.dispose(); - biasesSlice.dispose(); - - // Accumulate the channel to the output array - if (outputChannels == null){ - outputChannels = biasedOutputChannel; - }else{ - const updatedOutputChannels = tf.concat([outputChannels, biasedOutputChannel], 4); - biasedOutputChannel.dispose(); - outputChannels.dispose(); - outputChannels = updatedOutputChannels; - } - } - - return outputChannels; -} - - - -/** -* Inference Function for full volume and also apply sequential convoluton layer -* Suitable for low memory devices and low performance devices. -* -* @since 1.0.0 -* @param {promise} model -* @param {tf.Tensor} slices_3d -* @param {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] -* @param {boolen} isChannelLast- check input shape for channel position. -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - - async function inferenceFullVolumeSeqCovLayer(model, slices_3d, input_shape, isChannelLast, num_of_slices, slice_height, slice_width) { - console.log(" ---- Start FullVolume Inference with Sequential Convoluton Layer ---- "); - - statData["No_SubVolumes"] = 1; - - model.then(async function (res) { - - try { - startTime = performance.now(); - let inferenceStartTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - let delay = inferenceModelsList[$$("selectModel").getValue() - 1]["inferenceDelay"]; - console.log("Inference delay :", delay); - - let quantileNorm = inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - slices_3d = minMaxNormalizeVolumeData(slices_3d); - } - - - let i = 1; - let layersLength = res.layers.length; - console.log("Total num of layers ", layersLength); - - // Determine the number of output channels in the last layer of the model - // e.g. 3, 50, 104 - const outputLayer = res.layers[res.layers.length - 1]; - console.log("Output Layer : ", outputLayer); - - const expected_Num_labels = isChannelLast ? - outputLayer.outputShape[outputLayer.outputShape.length - 1]: - outputLayer.outputShape[1]; - console.log("Num of output channels : ", expected_Num_labels); - - - let curTensor = []; - curTensor[0] = slices_3d.reshape(input_shape); - // console.log("curTensor[0] :", curTensor[0].dataSync()); - - const timer = window.setInterval(async function() { - - try { - if (res.layers[i].activation.getClassName() !== 'linear') { - curTensor[i] = await res.layers[i].apply( curTensor[i-1]); - } else { - - curTensor[i] = await convByOutputChannelAndInputSlicing( - curTensor[i-1], - res.layers[i].getWeights()[0], - res.layers[i].getWeights()[1], - res.layers[i].strides, - res.layers[i].padding, - res.layers[i].dilationRate, - 3); // important for memory use - } - - // Log memory usage - const memoryInfo = tf.memory(); - console.log(`Iteration ${i}:`); - console.log(`Number of Tensors: ${memoryInfo.numTensors}`); - console.log(`Number of Data Buffers: ${memoryInfo.numDataBuffers}`); - console.log(`Bytes In Use: ${memoryInfo.numBytes}`); - console.log(`Megabytes In Use: ${(memoryInfo.numBytes / 1048576).toFixed(3)} MB`); - console.log(`Unreliable: ${memoryInfo.unreliable}`); - - - tf.dispose(curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } // end of catch - - console.log("layer ", i); - console.log("layer output Tensor shape : ", curTensor[i].shape); - console.log("layer count params ", res.layers[i].countParams()); - - res.layers[i].dispose(); - curTensor[i-1].dispose(); - - document.getElementById("progressBar").style.width = (i + 1)*100/layersLength + "%"; - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - if( i == layersLength - 2) { //Stop before the last layer or classification layer. - - window.clearInterval( timer ); - - // // Create an instance of SequentialConvLayer - // The second parameter is important for memory, - // the larger it is, the more memory it uses - // it was 8, but I set it to 3, got a different error - const seqConvLayer = await new SequentialConvLayer(res, 10, isChannelLast); - - let outputTensor = await seqConvLayer.apply(curTensor[i]); - - - //-- document.getElementById("progressBarChild").style.width = 0 + "%";; - - // Dispose the previous layer input tensor - tf.dispose(curTensor[i]); - // delete the used class - // delete seqConvLayer; - - // You can now use 'outputTensor' as needed - console.log(outputTensor); - console.log(" Output tensor shape : ", outputTensor.shape); - // Array(3) [ 256, 256, 256 ] - - if(outputTensor.shape.length != 3) { - webix.alert("Output tensor shape should be 3 dims but it is " + outputTensor.shape.length, "alert-error"); - } - - let Inference_t = ((performance.now() - startTime) / 1000).toFixed(4); - - console.log("find array max: "); - let curBatchMaxLabel = findArrayMax(Array.from(outputTensor.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - let numSegClasses = maxLabelPredicted + 1; - console.log("Predicted num of segmentation classes", numSegClasses); - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - if( numSegClasses != expected_Num_labels ) { - webix.alert("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses + ". For possible solutions please refer to FAQ .", "alert-error"); - console.log("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses); - } - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outLabelVolume transposed"); - outputTensor = outputTensor.transpose(); - } - - startTime = performance.now(); - - // Generate output volume or slices - console.log("Generating output"); - - try { - const img = new Uint32Array(outputTensor.dataSync()); - const Vshape = outputTensor.shape; - const Vtype = outputTensor.dtype; - tf.dispose(outputTensor); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - generateOutputSlicesV2(img, Vshape, Vtype, num_of_slices, numSegClasses, slice_height, slice_width); - console.log(" FullVolume inference num of tensors after generateOutputSlicesV2: " , tf.memory().numTensors ); - } catch (error) { - - //-- Timing data to collect - tf.engine().endScope(); - tf.engine().disposeVariables(); - - console.log("Error while generating output: ", error) - - webix.alert("Failed while generating output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Failed while generating output"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - let Postprocess_t = ((performance.now() - startTime) / 1000).toFixed(4); - - document.getElementById("progressBar").style.width = 0; - - $$("downloadBtn").enable(); - $$("segmentBtn").enable(); - - tf.engine().disposeVariables(); - - console.log("Processing the whole brain volume in tfjs for multi-class output mask took : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - } else { - - i++; - } - - - }, delay); - - } catch(err) { - - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - }); - - } - - - -/** -* Inference function for full volume that crops input MRI and also apply sequential convoluton layer (Phase 2) -* Suitable for low memory devices and low performance devices. -* Phase-1 find the mask -* -* @since 1.2.0 -* @param {promise} model, selected model for inference. -* @param {tf.Tensor} pipeline1_out 3D e.g. null or tensor -* @param {tf.Tensor} slices_3d -* @param {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - -async function inferenceFullVolumeSeqCovLayerPhase2 (model, slices_3d, num_of_slices, slice_height, slice_width, pipeline1_out) { - - //--Phase-2, After remove the skull try to allocate brain volume and make inferece - console.log(" ---- Start FullVolume Inference with Sequential Conv Layer for phase-II ---- "); - - const quantileNorm = inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - slices_3d = await minMaxNormalizeVolumeData(slices_3d); - } - - - - let mask_3d; - - if(pipeline1_out == null) { // preModel is null - - // Check if thresholding the MRI to remove noisy voxels for better cropping is needed. - let autoThresholdValue = inferenceModelsList[$$("selectModel").getValue() - 1]["autoThreshold"]; - - if( (autoThresholdValue > 0) && (autoThresholdValue <= 1) ) { - - // Filtered MRI from noisy voxel below autoThresholdValue - mask_3d = await applyMriThreshold(slices_3d, autoThresholdValue); - } else { - console.log("No valid crop threshold value"); - // binarize original image - mask_3d = await slices_3d.greater([0]).asType('bool'); - } - - } else { - - mask_3d = await pipeline1_out.greater([0]).asType('bool'); - //-- pipeline1_out.dispose(); - - } - - console.log(" mask_3d shape : ", mask_3d.shape); - - const coords = await tf.whereAsync(mask_3d); - //-- Get each voxel coords (x, y, z) - - mask_3d.dispose(); - - const coordsArr = coords.arraySync(); - - let row_min = slice_height, row_max = 0, col_min = slice_width, col_max = 0, depth_min = num_of_slices, depth_max = 0; - - for(let i = 0; i < coordsArr.length; i++) { - - if ( row_min > coordsArr[i][0] ) { - row_min = coordsArr[i][0]; - } else if(row_max < coordsArr[i][0]) { - row_max = coordsArr[i][0]; - } - - if ( col_min > coordsArr[i][1] ) { - col_min = coordsArr[i][1]; - } else if(col_max < coordsArr[i][1]) { - col_max = coordsArr[i][1]; - } - - if ( depth_min > coordsArr[i][2] ) { - depth_min = coordsArr[i][2]; - } else if(depth_max < coordsArr[i][2]) { - depth_max = coordsArr[i][2]; - } - } - - - console.log( "row min and max :", row_min, row_max); - console.log( "col min and max :", col_min, col_max); - console.log( "depth min and max :", depth_min, depth_max); - - //-- Reference voxel that cropped volume started slice with it - const refVoxel = [row_min, col_min, depth_min]; - // -- Starting form refVoxel, size of bounding volume - const boundVolSizeArr = [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1]; - - coords.dispose(); - - //-- Extract 3d object (e.g. brain) - const cropped_slices_3d = slices_3d.slice([row_min, col_min, depth_min], [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1] ) - - slices_3d.dispose(); - - //-- Padding size add to cropped brain - const pad = inferenceModelsList[$$("selectModel").getValue() - 1]["cropPadding"]; - - // Create margin around the bounding volume - let cropped_slices_3d_w_pad = addZeroPaddingTo3dTensor(cropped_slices_3d, [pad, pad] , [pad, pad], [pad, pad]); - console.log(" cropped slices_3d with padding shape: ", cropped_slices_3d_w_pad.shape); - - cropped_slices_3d.dispose(); - - - if(opts.drawBoundingVolume) { - - let testVol = await removeZeroPaddingFrom3dTensor(cropped_slices_3d_w_pad, pad, pad, pad); - console.log(" outLabelVolume without padding shape : ", testVol.shape); - - testVol = await resizeWithZeroPadding(testVol, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" outLabelVolume final shape after resizing : ", testVol.shape); - - draw3dObjBoundingVolume(tf.unstack(testVol)); - testVol.dispose(); - - return 0; - } - - - statData["Brainchop_Ver"] = "FullVolume" - - // model.then(function (res) { - const res = await model - try { - let startTime = performance.now(); - const inferenceStartTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - const transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - const delay = inferenceModelsList[$$("selectModel").getValue() - 1]["inferenceDelay"]; - console.log("Inference delay :", delay); - - if(transpose) { - cropped_slices_3d_w_pad = await cropped_slices_3d_w_pad.transpose() - console.log("Input transposed for pre-model"); - } else { - console.log("Transpose not enabled for pre-model"); - } - - let i = 1; - let layersLength = res.layers.length; - console.log("res.layers.length ", layersLength); - - let isChannelLast = isModelChnlLast(res); - const batchSize = opts.batchSize; - const numOfChan = opts.numOfChan; - let adjusted_input_shape - //-- Adjust model input shape - if(isChannelLast) { - - res.layers[0].batchInputShape[1] = cropped_slices_3d_w_pad.shape[0]; - res.layers[0].batchInputShape[2] = cropped_slices_3d_w_pad.shape[1]; - res.layers[0].batchInputShape[3] = cropped_slices_3d_w_pad.shape[2]; - - adjusted_input_shape = [batchSize, res.layers[0].batchInputShape[1], - res.layers[0].batchInputShape[2], - res.layers[0].batchInputShape[3], - numOfChan]; - - } else { - - res.layers[0].batchInputShape[2] = cropped_slices_3d_w_pad.shape[0]; - res.layers[0].batchInputShape[3] = cropped_slices_3d_w_pad.shape[1]; - res.layers[0].batchInputShape[4] = cropped_slices_3d_w_pad.shape[2]; - - adjusted_input_shape = [batchSize, numOfChan, - res.layers[0].batchInputShape[2], - res.layers[0].batchInputShape[3], - res.layers[0].batchInputShape[4]]; - - } - - console.log(" Model batch input shape : ", res.layers[0].batchInputShape); - // -- batchInputShape {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] - - statData["Input_Shape"] = JSON.stringify(res.layers[0].batchInputShape); - statData["Output_Shape"] = JSON.stringify(res.output.shape); - statData["Channel_Last"] = isChannelLast; - statData["Model_Param"] = getModelNumParameters(res); - statData["Model_Layers"] = getModelNumLayers(res); - statData["Model"] = inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"]; - statData["Extra_Info"] = null; - - - // Determine the number of output channels in the last layer of the model - // e.g. 3, 50, 104 - const outputLayer = res.layers[res.layers.length - 1]; - console.log("Output Layer : ", outputLayer); - - const expected_Num_labels = isChannelLast ? - outputLayer.outputShape[outputLayer.outputShape.length - 1]: - outputLayer.outputShape[1]; - console.log("Num of output channels : ", expected_Num_labels); - - - - const curTensor = []; - curTensor[0] = await cropped_slices_3d_w_pad.reshape(adjusted_input_shape); - // console.log("curTensor[0] :", curTensor[0].dataSync()); - - let curProgBar = parseInt(document.getElementById("progressBar").style.width); - - const timer = window.setInterval(async function() { - - try { - if (res.layers[i].activation.getClassName() !== 'linear') { - curTensor[i] = await res.layers[i].apply( curTensor[i-1]); - } else { - - curTensor[i] = await convByOutputChannelAndInputSlicing(curTensor[i-1], - res.layers[i].getWeights()[0], - res.layers[i].getWeights()[1], - res.layers[i].strides, - res.layers[i].padding, - res.layers[i].dilationRate, - 3); // important for memory use - } - - tf.dispose(curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - console.log("layer ", i); - console.log("layer output Tensor shape : ", curTensor[i].shape); - console.log("layer count params ", res.layers[i].countParams()); - - res.layers[i].dispose(); - curTensor[i-1].dispose(); - - - document.getElementById("progressBar").style.width = (curProgBar + (i + 1)*(100 - curProgBar)/layersLength) + "%"; - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - - if( i == layersLength - 2) { //Stop before the last layer or classification layer. - - window.clearInterval( timer ); - - - // // Create an instance of SequentialConvLayer - //The second parameter is important for memory, - // the larger it is, the more memory it uses - // it was 8, but I set it to 3, got a different error - const seqConvLayer = await new SequentialConvLayer(res, 10, isChannelLast); - - - // Apply the last output tensor to the seq. instance - let outputTensor = await seqConvLayer.apply(curTensor[i]) - - //-- document.getElementById("progressBarChild").style.width = 0 + "%";; - - // Dispose the previous layer input tensor - tf.dispose(curTensor[i]); - // delete the used class - delete seqConvLayer; - - // You can now use 'outputTensor' as needed - console.log(" Output tensor shape : ", outputTensor.shape); - // Array(3) [ 256, 256, 256 ] - - if(outputTensor.shape.length != 3) { - webix.alert("Output tensor shape should be 3 dims but it is " + outputTensor.shape.length, "alert-error"); - } - - - const Inference_t = ((performance.now() - startTime)/1000).toFixed(4); - - console.log(" find array max "); - let curBatchMaxLabel = findArrayMax(Array.from(outputTensor.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - const numSegClasses = maxLabelPredicted + 1; - console.log("Predicted num of segmentation classes", numSegClasses); - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - if( numSegClasses != expected_Num_labels ) { - webix.alert("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses + ". For possible solutions please refer to FAQ .", "alert-error"); - console.log("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses); - } - - //-- Transpose back to fit Papaya display settings - let outLabelVolume = outputTensor.reshape([cropped_slices_3d_w_pad.shape[0], cropped_slices_3d_w_pad.shape[1], cropped_slices_3d_w_pad.shape[2]]); - tf.dispose(outputTensor); - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outLabelVolume transposed"); - outLabelVolume = outLabelVolume.transpose(); - } - - outLabelVolume = removeZeroPaddingFrom3dTensor(outLabelVolume, pad, pad, pad); - console.log(" outLabelVolume without padding shape : ", outLabelVolume.shape); - outLabelVolume = resizeWithZeroPadding(outLabelVolume, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" outLabelVolume final shape after resizing : ", outLabelVolume.shape); - - let filterOutWithPreMask = inferenceModelsList[$$("selectModel").getValue() - 1]["filterOutWithPreMask"]; - - // To clean the skull area wrongly segmented inphase-2. - if(pipeline1_out != null && opts.isBrainCropMaskBased && filterOutWithPreMask) { - outLabelVolume = outLabelVolume.mul(binarizeVolumeDataTensor(pipeline1_out)); - } - - - startTime = performance.now(); - // Generate output volume or slices - console.log("Generating correct output"); - - try { - const img = new Uint32Array(outLabelVolume.dataSync()); - const Vshape = outLabelVolume.shape; - const Vtype = outLabelVolume.dtype; - tf.dispose(outLabelVolume); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - generateOutputSlicesV2(img, Vshape, Vtype, num_of_slices, numSegClasses, slice_height, slice_width); - console.log(" Phase-2 num of tensors after generateOutputSlicesV2: " , tf.memory().numTensors ); - - } catch (error) { - - //-- Timing data to collect - tf.engine().endScope(); - tf.engine().disposeVariables(); - console.log("Error while generating output: ", error) - - webix.alert("Failed while generating output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Failed while generating output"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - let Postprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - document.getElementById("progressBar").style.width = 0; - //webix.message.hide("waitMessage"); - - $$("downloadBtn").enable(); - $$("segmentBtn").enable(); - // $$("imageUploader").enable(); - tf.engine().disposeVariables(); - - console.log("Processing the whole brain volume in tfjs for multi-class output mask took : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - } else { - - i++; - } - - }, delay); - - } catch(err) { - - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - } - - - - -/** -* Inference Function for full volume -* No Sequential Convolution Layer -* Faster -* -* @since 1.0.0 -* @param {promise} model -* @param {tf.Tensor} slices_3d -* @param {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] -* @param {boolen} isChannelLast- check input shape for channel position. -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - -async function inferenceFullVolume(model, slices_3d, input_shape, isChannelLast, num_of_slices, slice_height, slice_width) { - - statData["No_SubVolumes"] = 1; - - //-- let modelLayersOrg = JSON.parse(JSON.stringify(modelObject)); - - model.then(async function (res) { - - try { - startTime = performance.now(); - let inferenceStartTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - let delay = inferenceModelsList[$$("selectModel").getValue() - 1]["inferenceDelay"]; - console.log("Inference delay :", delay); - - let quantileNorm = inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - slices_3d = minMaxNormalizeVolumeData(slices_3d); - } - - let i = 1; - let layersLength = res.layers.length; - console.log("res.layers.length ", layersLength); - - - let curTensor = []; - curTensor[0] = slices_3d.reshape(input_shape); - // console.log("curTensor[0] :", curTensor[0].dataSync()); - - - let timer = window.setInterval(function() { - - try { - curTensor[i] = res.layers[i].apply( curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - console.log("layer ", i); - console.log("layer output Tensor shape : ", curTensor[i].shape); - console.log("layer count params ", res.layers[i].countParams()); - - res.layers[i].dispose(); - curTensor[i-1].dispose(); - - - document.getElementById("progressBar").style.width = (i + 1)*100/layersLength + "%"; - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - - if( i == layersLength - 1) { - window.clearInterval( timer ); - - // prediction = res.layers[res.layers.length-1].apply(curTensor[i]); - // curTensor[i].print(); - //outputDataBeforArgmx = Array.from(curTensor[i].dataSync()) - - let axis = isChannelLast ? -1 : 1; - console.log(" find argmax ") - console.log("last Tensor shape : ", curTensor[i].shape); - //-- curTensor[i].shape : [ 1, 256, 256, 256, 3 ] - let expected_Num_labels = isChannelLast ? curTensor[i].shape[4] : curTensor[i].shape[1]; - let prediction_argmax; - - // Try for argMax with model output tensor. - - try { - let argMaxTime = performance.now(); - console.log(" Try tf.argMax for fullVolume .."); - prediction_argmax = tf.argMax(curTensor[i], axis); - console.log("tf.argMax for fullVolume takes : ", ((performance.now() - argMaxTime)/1000).toFixed(4) ); - - } catch(err1) { - // if channel last - if(axis == -1) { - - try { - let argMaxLargeTime = performance.now(); - console.log(" tf.argMax failed .. try argMaxLarge .."); - let modelOutBuffer = tensor2LightBuffer(curTensor[i].reshape([num_of_slices, slice_height, slice_width, expected_Num_labels]), 'float16'); - prediction_argmax = argMaxLarge(modelOutBuffer, num_of_slices, slice_height, slice_width, expected_Num_labels, 'float16'); - console.log("argMaxLarge for fullVolume takes : ", ((performance.now() - argMaxLargeTime)/1000).toFixed(4) ); - - } catch(err2) { - - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - prediction_argmax.dispose(); - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err2.message; - statData["Extra_Err_Info"] = "prediction_argmax from argMaxLarge failed"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - } else { - // if channel first .. - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - prediction_argmax.dispose(); - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err1.message; - statData["Extra_Err_Info"] = "prediction_argmax from argMaxLarge not support yet channel first"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - } - - - - console.log(" prediction_argmax shape : ", prediction_argmax.shape); - //-- prediction_argmax.shape : [ 1, 256, 256, 256] - - let Inference_t = ((performance.now() - startTime)/1000).toFixed(4); - - //outputDataBeforArgmx = Array.from(prediction_argmax.dataSync()) - tf.dispose(curTensor[i]); - // allPredictions.push({"id": allBatches[j].id, "coordinates": allBatches[j].coordinates, "data": Array.from(prediction_argmax.dataSync()) }) - console.log(" find array max "); - let curBatchMaxLabel = findArrayMax(Array.from(prediction_argmax.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - let numSegClasses = maxLabelPredicted + 1; - console.log("numSegClasses", numSegClasses); - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - - //-- Transpose back to fit Papaya display settings - let outLabelVolume = prediction_argmax.reshape([num_of_slices, slice_height, slice_width]); - tf.dispose(prediction_argmax); - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outLabelVolume transposed"); - outLabelVolume = outLabelVolume.transpose(); - } - - startTime = performance.now(); - // Generate output volume or slices - console.log("Generating correct output"); - - try { - const img = new Uint32Array(outLabelVolume.dataSync()); - const Vshape = outLabelVolume.shape; - const Vtype = outLabelVolume.dtype; - tf.dispose(outLabelVolume); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - generateOutputSlicesV2(img, Vshape, Vtype, num_of_slices, numSegClasses, slice_height, slice_width); - console.log(" FullVolume inference num of tensors after generateOutputSlicesV2: " , tf.memory().numTensors ); - } catch (error) { - - //-- Timing data to collect - tf.engine().endScope(); - tf.engine().disposeVariables(); - - webix.alert("Failed while generating output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Failed while generating output"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - let Postprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - document.getElementById("progressBar").style.width = 0; - //webix.message.hide("waitMessage"); - - $$("downloadBtn").enable(); - $$("segmentBtn").enable(); - // $$("imageUploader").enable(); - tf.engine().disposeVariables(); - - console.log("Processing the whole brain volume in tfjs tooks for multi-class output mask : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - } - i++; - - }, delay); - - } catch(err) { - - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - }); - - } - -/** -* Remove zero padding from 3D tensor -* pad([[1,1],[1,1]]) means: 1 row of zeros befor, 1 row of zeros after, -* 1 col of zeros befor, 1 col of zeros after, -* -* @since 1.2.0 -* @param {tf.tensor/ Array} object3d- Can be tf.tensor or array, it can represent the cropped brain tissue in 3D with added padding -* @returns {tf.tensor} Returns same input tensor without zero padding margins -* @example -* -* tsr = removeZeroPaddingFrom3dTensor( array2Tensor ( [[[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 1, 0], -* [0, 2, 3, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 4, 5, 0], -* [0, 6, 7, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]]] ) ) -* -* //=> tsr.print() -* Tensor -* [[[0, 1], -* [2, 3]], -* -* [[4, 5], -* [6, 7]]] -* -*/ - -removeZeroPaddingFrom3dTensor = (tensor3d, rowPad = 1, colPad = 1, depthPad = 1) => { - - if(tensor3d.rank != 3) { - throw "Tensor must be 3D"; - } - - [h, w, d] = tensor3d.shape; - return tensor3d.slice( [rowPad , colPad, depthPad], [h - (2 * rowPad), w - (2 * colPad), d - (2 * depthPad) ] ); -} - - -/** -* Add zero padding to 3D tensor -* pad([[1,1],[1,1]]) means: 1 row of zeros befor, 1 row of zeros after, -* 1 col of zeros befor, 1 col of zeros after, -* -* @since 1.2.0 -* @param {tf.tensor} tensor3d- tf.tensor, it can represent the cropped brain tissue in 3D befor adding padding for inference -* @returns {tf.tensor} Returns same input tensor with zero padding margins -* @example -* -* tsr = addZeroPaddingTo3dTensor ( tf.tensor([0, 1, 2, 3, 4, 5, 6, 7, ], [2,2,2] ) ) -* -* //=> tsr.print() -* Tensor -* [[[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 1, 0], -* [0, 2, 3, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 4, 5, 0], -* [0, 6, 7, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]]] -* -*/ - - -addZeroPaddingTo3dTensor = (tensor3d, rowPadArr = [1, 1], colPadArr = [1, 1], depthPadArr = [1, 1]) => { - if(tensor3d.rank != 3) { - throw "Tensor must be 3D"; - } - - return tensor3d.pad([ rowPadArr ,colPadArr, depthPadArr ]); -} - - - -/** -* Resize cropped 3d tensor to original size with filling zero padding -* It creates padding around cropped volume to restore orginal MRI size after the inference -Original voxel is the x,y,z used for slicing the brain -* pad([[1,1],[1,1]]) means: 1 row of zeros befor, 1 row of zeros after, -* 1 col of zeros befor, 1 col of zeros after, -* -* @since 1.2.0 -* @param {tf.tensor} croppedTensor3d- tf.tensor, it represents the cropped brain tissue resulted from the model inference -* @param {number} newDepth, the new depth to resize cropped volume to it, e.g. 256 -* @param {number} newHeight, the new height to resize cropped volume to it, e.g. 256 -* @param {number} newWidth, the new width to resize cropped volume to it, e.g. 256 -* @param {Array} refVoxel, the reference voxel to position the cropped volume into the new volume or original volume size -* @param {Array} boundVolSizeArr, size dim of the cropped brain bounding volume -* @returns {tf.tensor} Returns same input tensor with zero padding margins -* @example -* -* tsr = resizeWithZeroPadding ( tf.tensor([0, 1, 2, 3, 4, 5, 6, 7, ], [2,2,2]), 4, 4, 4, [1, 1, 1], [2, 2, 2] ) -* -* //=> tsr.print() -* Tensor -* [[[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 1, 0], -* [0, 2, 3, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 4, 5, 0], -* [0, 6, 7, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]]] -* -*/ - - - - resizeWithZeroPadding = (croppedTensor3d, newDepth, newHeight, newWidth, refVoxel, boundVolSizeArr ) => { - - let row_pad_befor = refVoxel[0]; - let col_pad_befor = refVoxel[1]; - let depth_pad_befor = refVoxel[2]; - // last and lower volume voxel - let row_max = row_pad_befor + boundVolSizeArr[0] -1; // size [2, 2, 2] means 2 voxels total in each dim - let col_max = col_pad_befor + boundVolSizeArr[1] -1; - let depth_max = depth_pad_befor + boundVolSizeArr[2] -1; - - let row_pad_after = (newHeight - row_max -1) > 0 ? (newHeight - row_max -1) : 0; - let col_pad_after = (newWidth - col_max -1) > 0 ? (newWidth - col_max -1) : 0; - let depth_pad_after = (newDepth - depth_max -1) > 0 ? (newDepth - depth_max -1) : 0; - - return croppedTensor3d.pad([ [row_pad_befor, row_pad_after] ,[col_pad_befor, col_pad_after], [depth_pad_befor, depth_pad_after] ]); - - } - - -/** -* Generate output labels of all slices. (refine) -* Find current voxel value of the related seg class buffer, if we have numSegClasses = 3 then we have 3 buffers, -* one for each seg classes 0, 1, 2 -* -* @since 1.0.0 -* @param {tf.Tensor} unstackOutVolumeTensor -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* @return {tensor} -* -*/ - - -generateBrainMask = (unstackOutVolumeTensor, num_of_slices, slice_height, slice_width) => { - - console.log("Generate Brain Masking ... "); - // Convert all slices into 1 Dim array to download - - let allOutputSlices3DCC = []; - let allOutputSlices3DContours = []; - - - // dataSync() using to flatten array. Takes around 1.5 s - for(let sliceTensorIdx = 0; sliceTensorIdx < unstackOutVolumeTensor.length; sliceTensorIdx++ ) { - allOutputSlices3DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()); - } - - - let isPreModelPostProcessEnable = inferenceModelsList[$$("selectModel").getValue() - 1]["preModelPostProcess"]; - - if(isPreModelPostProcessEnable) { - console.log("Phase-1 Post processing enabled ... "); - allOutputSlices3DCC = tf.tidy(() => { - // Remove noisy regions using 3d CC - let sliceWidth = niftiHeader.dims[1]; - let sliceHeight = niftiHeader.dims[2]; - return postProcessSlices3D(allOutputSlices3DCC, sliceHeight, sliceWidth ); - }) - console.log("Post processing done "); - } else { - console.log("Phase-1 Post processing disabled ... "); - } - - - let allOutputSlices3DCC1DimArray = []; - // Use this conversion to download output slices as nii file. Takes around 0.5 s - for(let sliceIdx = 0; sliceIdx < allOutputSlices3DCC.length; sliceIdx++ ) { - allOutputSlices3DCC1DimArray.push.apply(allOutputSlices3DCC1DimArray, allOutputSlices3DCC[sliceIdx]); - } - - - let brainOut = []; - - if(opts.isBrainCropMaskBased) { // Mask-based - - let brainMaskTensor1d = binarizeVolumeDataTensor(tf.tensor1d(allOutputSlices3DCC1DimArray)); - brainOut = Array.from(brainMaskTensor1d.dataSync()); - - } else { // Brain tissue - - let allSlices = getAllSlicesData1D(num_of_slices, niftiHeader, niftiImage); - for(let sliceIdx = 0; sliceIdx < allOutputSlices3DCC.length; sliceIdx++ ) { - for(pixelIdx = 0; pixelIdx < (slice_height * slice_width); pixelIdx++) { - //Filter smaller regions original MRI data - if(allOutputSlices3DCC[sliceIdx][pixelIdx] == 0) { - allSlices[sliceIdx][pixelIdx] = 0; - } - } - - brainOut.push.apply(brainOut, allSlices[sliceIdx]) - } - } - - - let labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, brainOut); - - - if(opts.showPhase1Output) { // flag to not draw for now - - if(opts.isColorEnable) { - let blob = new Blob([labelArrayBuffer], {type: "application/octet-binary;charset=utf-8"}); - let file = new File([blob], "temp.nii"); - params_label["files"] = [file]; - params_label[file["name"]] = {lut: "Grayscale", interpolation: false}; - - } else { - params_label["binaryImages"] = [labelArrayBuffer]; - } - - // Set the view of container-2 as container-1 - params_label["mainView"] = papayaContainers[0].viewer.mainImage.sliceDirection == 1? "axial" : - papayaContainers[0].viewer.mainImage.sliceDirection == 2? "coronal" : "sagittal"; - - - //-- Remove any existing overlay - resetMriViewerOverlay(1); - - - - // Label segmenation voxels according to label file - console.log("label path: ", inferenceModelsList[$$("selectModel").getValue() - 1]["labelsPath"]) - - // set 1 for label viewer - papaya.Container.resetViewer(1, params_label); - - - // To sync swap view button - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).addEventListener("click", function(){ - papayaContainers[1].viewer.rotateViews() - - }) - - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[1].containerIndex).addEventListener("click", function(){ - papayaContainers[0].viewer.rotateViews() - - }) - - } - - if(opts.isBrainCropMaskBased) { - // To show brain volume - console.log("Output Segmentation Labels (ROI) volumes : ", arrValuesFreq(brainOut)); - } - - return tf.tensor(brainOut, [num_of_slices, slice_height, slice_width] ); - - } - - -/** -* (Option) Draw 3D bounding volume of the brain in Papaya label viewer -* For visualization purpose -* @since 1.2.0 -* @param {tf.Tensor} unstackOutVolumeTensor -* -*/ - - -draw3dObjBoundingVolume= (unstackOutVolumeTensor) => { - - console.log("Plot cropped volume shape ... "); - // Convert all slices into 1 Dim array to download - - let allOutputSlices3DCC = []; - let allOutputSlices3DContours = []; - - - // dataSync() using to flatten array. Takes around 1.5 s - for(let sliceTensorIdx = 0; sliceTensorIdx < unstackOutVolumeTensor.length; sliceTensorIdx++ ) { - allOutputSlices3DCC[sliceTensorIdx] = Array.from(unstackOutVolumeTensor[sliceTensorIdx].dataSync()); - } - - // if(false) { // Enable contour for overlay option - // // Remove noisy regions using 3d CC - // let sliceWidth = niftiHeader.dims[1]; - // let sliceHeight = niftiHeader.dims[2]; - // allOutputSlices3DCC = findVolumeContours(allOutputSlices3DCC, sliceHeight, sliceWidth, 2 ); - // } - - let allOutputSlices3DCC1DimArray = []; - // Use this conversion to download output slices as nii file. Takes around 0.5 s - for(let sliceIdx = 0; sliceIdx < allOutputSlices3DCC.length; sliceIdx++ ) { - allOutputSlices3DCC1DimArray.push.apply(allOutputSlices3DCC1DimArray, allOutputSlices3DCC[sliceIdx]); - } - - console.log("Done with allOutputSlices3DCC1DimArray ") - - let brainOut = []; - - - let brainMaskTensor1d = binarizeVolumeDataTensor(tf.tensor1d(allOutputSlices3DCC1DimArray)); - brainOut = Array.from(brainMaskTensor1d.dataSync()); - - - // labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, brainExtractionData1DimArr); - let labelArrayBuffer = createNiftiOutArrayBuffer(rawNiftiData, brainOut); - - - if(true) { // flag to not draw for now - - if(opts.isColorEnable) { - let blob = new Blob([labelArrayBuffer], {type: "application/octet-binary;charset=utf-8"}); - let file = new File([blob], "temp.nii"); - params_label["files"] = [file]; - params_label[file["name"]] = {lut: "Grayscale", interpolation: false}; - - } else { - params_label["binaryImages"] = [labelArrayBuffer]; - } - - // Set the view of container-2 as container-1 - params_label["mainView"] = papayaContainers[0].viewer.mainImage.sliceDirection == 1? "axial" : - papayaContainers[0].viewer.mainImage.sliceDirection == 2? "coronal" : "sagittal"; - - - papaya.Container.resetViewer(1, params_label); - papayaContainers[1].viewer.screenVolumes[0].alpha = 0.2; // 0 to 1 screenVolumes[0] is first image loaded in Labels viewer - papayaContainers[1].viewer.drawViewer(true, false); - - - // To sync swap view button - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).addEventListener("click", function(){ - papayaContainers[1].viewer.rotateViews() - - }) - - document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[1].containerIndex).addEventListener("click", function(){ - papayaContainers[0].viewer.rotateViews() - - }) - - } - - } - -/** -* Function return the 3D bounding volume of the brain -* For visualization purpose -* @since 1.2.0 -* @param {tf.Tensor} slices_3d, input 3D tesnor shape -* @return {Pormise} promise with result has minVoxelCoord Array, maxVoxelCoord Array , boundVolSize Array. -* @example -* -* result = get3dObjectBoundingVolume( array2Tensor ( [[[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 1, 0], -* [0, 2, 3, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 4, 5, 0], -* [0, 6, 7, 0], -* [0, 0, 0, 0]], -* -* [[0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0], -* [0, 0, 0, 0]]] ) ) -* -* result.then(function (res){ console.log(res )}) -* //=> -* Object { minVoxelCoord: [1, 1, 1], maxVoxelCoord: [2, 2, 2 ], boundVolSize: [2, 2, 2 ] } -* -*/ - -get3dObjectBoundingVolume = async(slices_3d) => { - - // Get the shape mask - let maskTensor_3d = slices_3d.greater([0]).asType('bool'); - //-- Don't dispose slices_3d here, dispose it from the calling function.. - - const coords = await tf.whereAsync(maskTensor_3d); - //-- Get each voxel coords (x, y, z) - maskTensor_3d.dispose(); - - const coordsArr = coords.arraySync(); - coords.dispose(); - - - let row_min = 256, row_max = 0, col_min = 256, col_max = 0, depth_min = 256, depth_max = 0; - - for(let i = 0; i < coordsArr.length; i++) { - - if ( row_min > coordsArr[i][0] ) { - row_min = coordsArr[i][0]; - } else if(row_max < coordsArr[i][0]) { - row_max = coordsArr[i][0]; - } - - if ( col_min > coordsArr[i][1] ) { - col_min = coordsArr[i][1]; - } else if(col_max < coordsArr[i][1]) { - col_max = coordsArr[i][1]; - } - - if ( depth_min > coordsArr[i][2] ) { - depth_min = coordsArr[i][2]; - } else if(depth_max < coordsArr[i][2]) { - depth_max = coordsArr[i][2]; - } - } - - - let minVoxel = [row_min, col_min, depth_min]; - let maxVoxel = [row_max, col_max, depth_max]; - let boundVolSize = [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1]; - - return {"minVoxelCoord" : minVoxel, "maxVoxelCoord" : maxVoxel, "boundVolSize": boundVolSize}; - -} - - -/** -* Inference function for full volume that crops input MRI (Phase 2) -* Phase-1 find the mask -* -* @since 1.2.0 -* @param {promise} model, selected model for inference. -* @param {tf.Tensor} pipeline1_out 3D e.g. null or tensor -* @param {tf.Tensor} slices_3d -* @param {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - - inferenceFullVolumePhase2 = async(model, slices_3d, num_of_slices, slice_height, slice_width, pipeline1_out) => { - - //--Phase-2, After remove the skull try to allocate brain volume and make inferece - console.log(" ---- Start FullVolume inference phase-II ---- "); - - let quantileNorm = inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - slices_3d = minMaxNormalizeVolumeData(slices_3d); - } - - - let mask_3d; - - if(pipeline1_out == null) { // preModel is null - - // Check if thresholding the MRI to remove noisy voxels for better cropping is needed. - let autoThresholdValue = inferenceModelsList[$$("selectModel").getValue() - 1]["autoThreshold"]; - - if( (autoThresholdValue > 0) && (autoThresholdValue <= 1) ) { - - // Filtered MRI from noisy voxel below autoThresholdValue - mask_3d = await applyMriThreshold(slices_3d, autoThresholdValue); - } else { - console.log("No valid crop threshold value"); - // binarize original image - mask_3d = slices_3d.greater([0]).asType('bool'); - } - - } else { - - mask_3d = pipeline1_out.greater([0]).asType('bool'); - //-- pipeline1_out.dispose(); - - } - - console.log(" mask_3d shape : ", mask_3d.shape); - - const coords = await tf.whereAsync(mask_3d); - //-- Get each voxel coords (x, y, z) - - mask_3d.dispose(); - - const coordsArr = coords.arraySync(); - - let row_min = slice_height, row_max = 0, col_min = slice_width, col_max = 0, depth_min = num_of_slices, depth_max = 0; - - for(let i = 0; i < coordsArr.length; i++) { - - if ( row_min > coordsArr[i][0] ) { - row_min = coordsArr[i][0]; - } else if(row_max < coordsArr[i][0]) { - row_max = coordsArr[i][0]; - } - - if ( col_min > coordsArr[i][1] ) { - col_min = coordsArr[i][1]; - } else if(col_max < coordsArr[i][1]) { - col_max = coordsArr[i][1]; - } - - if ( depth_min > coordsArr[i][2] ) { - depth_min = coordsArr[i][2]; - } else if(depth_max < coordsArr[i][2]) { - depth_max = coordsArr[i][2]; - } - } - - - console.log( "row min and max :", row_min, row_max); - console.log( "col min and max :", col_min, col_max); - console.log( "depth min and max :", depth_min, depth_max); - - //-- Reference voxel that cropped volume started slice with it - let refVoxel = [row_min, col_min, depth_min]; - console.log("refVoxel :", refVoxel) - - // -- Starting form refVoxel, size of bounding volume - let boundVolSizeArr = [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1]; - - console.log("boundVolSizeArr :", boundVolSizeArr) - - coords.dispose(); - - //-- Extract 3d object (e.g. brain) - let cropped_slices_3d = slices_3d.slice([row_min, col_min, depth_min], [row_max - row_min + 1, col_max - col_min + 1, depth_max - depth_min + 1] ) - - slices_3d.dispose(); - - //-- Padding size add to cropped brain - let pad = inferenceModelsList[$$("selectModel").getValue() - 1]["cropPadding"]; - - // Create margin around the bounding volume - cropped_slices_3d_w_pad = addZeroPaddingTo3dTensor(cropped_slices_3d, [pad, pad] , [pad, pad], [pad, pad]); - console.log(" cropped slices_3d with padding shape: ", cropped_slices_3d_w_pad.shape); - - cropped_slices_3d.dispose(); - - - //-- Test dim after padding .. - // for (let i = 0; i < cropped_slices_3d_w_pad.rank; i++) { - // if(cropped_slices_3d_w_pad.shape[i] > 256) { - // console.log(" cropped_slices_3d_w_pad > 256 "); - // } - - // } - - - - if(opts.drawBoundingVolume) { - - let testVol = removeZeroPaddingFrom3dTensor(cropped_slices_3d_w_pad, pad, pad, pad); - console.log(" outLabelVolume without padding shape : ", testVol.shape); - - testVol = resizeWithZeroPadding(testVol, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" outLabelVolume final shape after resizing : ", testVol.shape); - - draw3dObjBoundingVolume(tf.unstack(testVol)); - testVol.dispose(); - - return 0; - } - - - statData["Brainchop_Ver"] = "FullVolume"; - - model.then(function (res) { - - try { - startTime = performance.now(); - let inferenceStartTime = performance.now(); - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - let delay = inferenceModelsList[$$("selectModel").getValue() - 1]["inferenceDelay"]; - console.log("Inference delay :", delay); - - if(transpose) { - cropped_slices_3d_w_pad = cropped_slices_3d_w_pad.transpose() - console.log("Input transposed for pre-model"); - } else { - console.log("Transpose not enabled for pre-model"); - } - - let i = 1; - let layersLength = res.layers.length; - console.log("res.layers.length ", layersLength); - - let isChannelLast = isModelChnlLast(res); - const batchSize = opts.batchSize; - const numOfChan = opts.numOfChan; - - //-- Adjust model input shape - if(isChannelLast) { - - res.layers[0].batchInputShape[1] = cropped_slices_3d_w_pad.shape[0]; - res.layers[0].batchInputShape[2] = cropped_slices_3d_w_pad.shape[1]; - res.layers[0].batchInputShape[3] = cropped_slices_3d_w_pad.shape[2]; - - adjusted_input_shape = [batchSize, res.layers[0].batchInputShape[1], - res.layers[0].batchInputShape[2], - res.layers[0].batchInputShape[3], - numOfChan]; - - } else { - - res.layers[0].batchInputShape[2] = cropped_slices_3d_w_pad.shape[0]; - res.layers[0].batchInputShape[3] = cropped_slices_3d_w_pad.shape[1]; - res.layers[0].batchInputShape[4] = cropped_slices_3d_w_pad.shape[2]; - - adjusted_input_shape = [batchSize, numOfChan, - res.layers[0].batchInputShape[2], - res.layers[0].batchInputShape[3], - res.layers[0].batchInputShape[4]]; - - } - - console.log(" Model batch input shape : ", res.layers[0].batchInputShape); - // -- batchInputShape {Array} input_shape - e.g. [?, D, H, W, Ch] or [?, Ch, D, H, W] - - statData["Input_Shape"] = JSON.stringify(res.layers[0].batchInputShape); - statData["Output_Shape"] = JSON.stringify(res.output.shape); - statData["Channel_Last"] = isChannelLast; - statData["Model_Param"] = getModelNumParameters(res); - statData["Model_Layers"] = getModelNumLayers(res); - statData["Model"] = inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"]; - statData["Extra_Info"] = null; - - - let curTensor = []; - curTensor[0] = cropped_slices_3d_w_pad.reshape(adjusted_input_shape); - // console.log("curTensor[0] :", curTensor[0].dataSync()); - - let curProgBar = parseInt(document.getElementById("progressBar").style.width); - - let timer = window.setInterval(function() { - - try { - //-- curTensor[i] = res.layers[i].apply( curTensor[i-1]); - curTensor[i] = res.layers[i].apply( curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - console.log("layer ", i); - console.log("layer output Tensor shape : ", curTensor[i].shape); - console.log("layer count params ", res.layers[i].countParams()); - - res.layers[i].dispose(); - curTensor[i-1].dispose(); - - - document.getElementById("progressBar").style.width = (curProgBar + (i + 1)*(100 - curProgBar)/layersLength) + "%"; - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - - if( i == layersLength - 1) { - window.clearInterval( timer ); - - // prediction = res.layers[res.layers.length-1].apply(curTensor[i]); - // curTensor[i].print(); - //outputDataBeforArgmx = Array.from(curTensor[i].dataSync()) - - let axis = isChannelLast ? -1 : 1; - console.log(" find argmax ") - console.log("last Tensor shape : ", curTensor[i].shape); - //-- curTensor[i].shape e.g. [ 1, 256, 256, 256, 3 ] - let expected_Num_labels = isChannelLast ? curTensor[i].shape[4] : curTensor[i].shape[1]; - let prediction_argmax; - - // Try for argMax with model output tensor. - - try { - let argMaxTime = performance.now(); - console.log(" Try tf.argMax for fullVolume .."); - prediction_argmax = tf.argMax(curTensor[i], axis); - console.log("tf.argMax for fullVolume takes : ", ((performance.now() - argMaxTime)/1000).toFixed(4) ); - - } catch(err1) { - // if channel last - if(axis == -1) { - - try { - let argMaxLargeTime = performance.now(); - console.log(" tf.argMax failed .. try argMaxLarge .."); - let modelOutBuffer = tensor2LightBuffer(curTensor[i].reshape([cropped_slices_3d_w_pad.shape[0], cropped_slices_3d_w_pad.shape[1], cropped_slices_3d_w_pad.shape[2], expected_Num_labels]), 'float16'); - prediction_argmax = argMaxLarge(modelOutBuffer, cropped_slices_3d_w_pad.shape[0], cropped_slices_3d_w_pad.shape[1], cropped_slices_3d_w_pad.shape[2], expected_Num_labels, 'float16'); - console.log("argMaxLarge for fullVolume takes : ", ((performance.now() - argMaxLargeTime)/1000).toFixed(4) ); - - } catch(err2) { - - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err2.message; - statData["Extra_Err_Info"] = "prediction_argmax from argMaxLarge failed"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - } else { - // if channel first .. - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - prediction_argmax.dispose(); - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err1.message; - statData["Extra_Err_Info"] = "prediction_argmax from argMaxLarge not support yet channel first"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - } - - - - console.log(" prediction_argmax shape : ", prediction_argmax.shape); - //-- prediction_argmax.shape : [ 1, 256, 256, 256] - - let Inference_t = ((performance.now() - startTime)/1000).toFixed(4); - - //outputDataBeforArgmx = Array.from(prediction_argmax.dataSync()) - tf.dispose(curTensor[i]); - // allPredictions.push({"id": allBatches[j].id, "coordinates": allBatches[j].coordinates, "data": Array.from(prediction_argmax.dataSync()) }) - console.log(" find array max "); - let curBatchMaxLabel = findArrayMax(Array.from(prediction_argmax.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - let numSegClasses = maxLabelPredicted + 1; - console.log("numSegClasses", numSegClasses); - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - - if( numSegClasses != expected_Num_labels ) { - webix.alert("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses + ". For possible solutions please refer to FAQ .", "alert-error"); - console.log("expected " + expected_Num_labels + " labels, but the predicted are " + numSegClasses); - } - - - //-- Transpose back to fit Papaya display settings - let outLabelVolume = prediction_argmax.reshape([cropped_slices_3d_w_pad.shape[0], cropped_slices_3d_w_pad.shape[1], cropped_slices_3d_w_pad.shape[2]]); - tf.dispose(prediction_argmax); - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("outLabelVolume transposed"); - outLabelVolume = outLabelVolume.transpose(); - } - - outLabelVolume = removeZeroPaddingFrom3dTensor(outLabelVolume, pad, pad, pad); - console.log(" outLabelVolume without padding shape : ", outLabelVolume.shape); - outLabelVolume = resizeWithZeroPadding(outLabelVolume, num_of_slices, slice_height, slice_width, refVoxel, boundVolSizeArr ); - console.log(" outLabelVolume final shape after resizing : ", outLabelVolume.shape); - - let filterOutWithPreMask = inferenceModelsList[$$("selectModel").getValue() - 1]["filterOutWithPreMask"]; - // To clean the skull area wrongly segmented in phase-2. - if(pipeline1_out != null && opts.isBrainCropMaskBased && filterOutWithPreMask) { - outLabelVolume = outLabelVolume.mul(binarizeVolumeDataTensor(pipeline1_out)); - } - - startTime = performance.now(); - // Generate output volume or slices - console.log("Generating correct output"); - - try { - const img = new Uint32Array(outLabelVolume.dataSync()); - const Vshape = outLabelVolume.shape; - const Vtype = outLabelVolume.dtype; - tf.dispose(outLabelVolume); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - generateOutputSlicesV2(img, Vshape, Vtype, num_of_slices, numSegClasses, slice_height, slice_width); - console.log(" Phase-2 num of tensors after generateOutputSlicesV2: " , tf.memory().numTensors ); - - } catch (error) { - - //-- Timing data to collect - tf.engine().endScope(); - tf.engine().disposeVariables(); - - webix.alert("Failed while generating output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Failed while generating output"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - let Postprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - document.getElementById("progressBar").style.width = 0; - //webix.message.hide("waitMessage"); - - $$("downloadBtn").enable(); - $$("segmentBtn").enable(); - // $$("imageUploader").enable(); - //tf.engine().endScope(); - tf.engine().disposeVariables(); - - console.log("Processing the whole brain volume in tfjs for multi-class output mask took : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - } - i++; - - }, delay); - - } catch(err) { - - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - }); - - } - -/** -* Function to check if there is any problem with the sequence of ids -* -* @since 1.2.0 -* -*/ - -checkInferenceModelList = () => { - inferenceModelsList.forEach((model, idx) => { - if(model.id != ( idx + 1) ) { - webix.alert("inferenceModelsList needs review for inconsistency in models ID"); - return 0; - } - }); - -} - - -/** -* Inference function for full volume that find tissue first (Phase-1) by apply pre-model e.g. Brain_Extraction or Brain_Masking -* This is a first part of pipeline of two stages, phase-1 to apply pre-model, and phase-2 to crop the MRI -* @since 1.2.0 -* @param {promise} model, selected model for inference. -* @param {tf.Tensor} slices_3d -* @param {number} num_of_slices- Total Number of slices a.k.a z-dim -* @param {number} slice_height- - Slice Height -* @param {number} slice_width- Slice Width -* -*/ - - - inferenceFullVolumePhase1 = async(model, slices_3d, num_of_slices, slice_height, slice_width, isModelFullVol) => { - - statData["No_SubVolumes"] = 1; - - let modelEntry = inferenceModelsList[$$("selectModel").getValue() - 1]; - console.log("modelEntry ", modelEntry) - - // load pre-model for inference first, can be null if no pre-model such as GWM models - if(modelEntry["preModelId"]) { - - let preModel = load_model(inferenceModelsList[ modelEntry["preModelId"] - 1]['path'] ); - let transpose = inferenceModelsList[ modelEntry["preModelId"] - 1]["enableTranspose"]; - let quantileNorm = inferenceModelsList[ modelEntry["preModelId"] - 1]["enableQuantileNorm"]; - let preModel_slices_3d = null; - - //-- If pre-model is not null then slices_3d mask will be generated.. - //-- The mask is needed to remove the skull and set noise in background to 0, and get the brain bounding volume properly - let slices_3d_mask = null; - - if(quantileNorm) { - // Quantile normalize function needs specific models to be used - console.log("preModel Quantile normalization enabled"); - preModel_slices_3d = await quantileNormalizeVolumeData(slices_3d); - } else { - // Min Max Nomalize MRI data to be from 0 to 1 - console.log("preModel Min Max normalization enabled"); - preModel_slices_3d = minMaxNormalizeVolumeData(slices_3d); - } - - - //-- Transpose MRI data to be match pytorch/keras input output - //-- Check if pre-model needs transpose.. - if(transpose) { - - preModel_slices_3d = preModel_slices_3d.transpose(); - console.log("Input transposed for pre-model"); - - } else { - console.log("Transpose not enabled for pre-model"); - } - - statData["Brainchop_Ver"] = "PreModel_FV" ; // e.g. "PreModel_FV" - - preModel.then(function (res) { - - try { - - let inferenceStartTime = performance.now(); - let preModelObject = res; - - // read input shape from model.json object - let preModelBatchInputShape = preModelObject.layers[0].batchInputShape; - console.log(" Pre-Model batch input shape : ", preModelBatchInputShape) - - //-- Verify input shape - if(preModelBatchInputShape.length != 5) { - webix.alert("The pre-model input shape must be 5D "); - return 0; - } - - let isPreModelChannelLast = isModelChnlLast(preModelObject); - const batchSize = opts.batchSize; - const numOfChan = opts.numOfChan; - let batch_D, batch_H, batch_W; - - if(isPreModelChannelLast ) { - console.log("Pre-Model Channel Last") - if (isNaN(preModelBatchInputShape[4]) || (preModelBatchInputShape[4] !=1)) { - webix.alert("The number of channels for pre-model input shape must be 1"); - return 0; - } - - batch_D = preModelBatchInputShape[1]; - batch_H = preModelBatchInputShape[2]; - batch_W = preModelBatchInputShape[3]; - - preModel_input_shape = [batchSize, batch_D, batch_H, batch_W, numOfChan]; - - } else { - console.log("Pre-Model Channel First") - if (isNaN(preModelBatchInputShape[1]) || (preModelBatchInputShape[1] !=1)) { - webix.alert("The number of channels for pre-model input shape must be 1"); - return 0; - } - - batch_D = preModelBatchInputShape[2]; - batch_H = preModelBatchInputShape[3]; - batch_W = preModelBatchInputShape[4]; - - preModel_input_shape = [batchSize, numOfChan, batch_D, batch_H, batch_W]; - - } - - - statData["Input_Shape"] = JSON.stringify(preModel_input_shape); - statData["Output_Shape"] = JSON.stringify(preModelObject.output.shape); - statData["Channel_Last"] = isPreModelChannelLast; - statData["Model_Param"] = getModelNumParameters(preModelObject); - statData["Model_Layers"] = getModelNumLayers(preModelObject); - statData["Model"] = inferenceModelsList[ modelEntry["preModelId"] - 1]["modelName"]; - statData["Extra_Info"] = inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"]; - - - // maxLabelPredicted in whole volume of the brain - let maxLabelPredicted = 0; - let delay = inferenceModelsList[ modelEntry["preModelId"] - 1]["inferenceDelay"]; - - let i = 1; - let layersLength = res.layers.length; - - let curTensor = []; - //-- reshape MRI to model input shape - curTensor[0] = preModel_slices_3d.reshape(preModel_input_shape); - - //Dispose the volume - tf.dispose(preModel_slices_3d); - - let timer = window.setInterval(function() { - - try { - curTensor[i] = res.layers[i].apply( curTensor[i-1]); - - } catch(err) { - - if( err.message === "Failed to compile fragment shader.") { - webix.confirm({ - title:"", - ok:"Ok", - cancel:"Cancel", - type: "confirm-error", - width: 500, - text: "Context lost due to limited Memory available, please check current browser resouces in the toolbar and verified GPUs for each model" - }) - .then(() => { - //--- - $$("browserResourcesWindow").show(); - - - }).fail(() => { - //--- - - }); - - } else { - webix.alert(err.message); - } - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err.message; - statData["Extra_Err_Info"] = "PreModel Failed while model layer " + i + " apply"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - console.log("layer ", i); - - res.layers[i].dispose(); - curTensor[i-1].dispose(); - - - document.getElementById("progressBar").style.width = (i + 1)*50/layersLength + "%"; - let memStatus = tf.memory().unreliable ? "Red" : "Green"; - let unreliableReasons = tf.memory().unreliable ? "unreliable reasons :" + tf.memory().reasons : ""; - document.getElementById("memoryStatus").style.backgroundColor = memStatus; - - - if( i == layersLength - 1) { - window.clearInterval( timer ); - - //-- prediction = res.layers[res.layers.length-1].apply(curTensor[i]); - //-- curTensor[i].print(); - //-- outputDataBeforArgmx = Array.from(curTensor[i].dataSync()) - - let axis = isPreModelChannelLast ? -1 : 1; - console.log(" find argmax ") - console.log("last Tensor shape : ", curTensor[i].shape); - //-- curTensor[i].shape : [ 1, 256, 256, 256, 3 ] - let expected_Num_labels = isPreModelChannelLast ? curTensor[i].shape[4] : curTensor[i].shape[1]; - let prediction_argmax; - - // Try for argMax with model output tensor. - - try { - console.log(" Try tf.argMax for fullVolume .."); - prediction_argmax = tf.argMax(curTensor[i], axis); - - } catch(err1) { - // if channel last - if(axis == -1) { - - try { - let argMaxLargeTime = performance.now(); - console.log(" tf.argMax failed .. try argMaxLarge .."); - let modelOutBuffer = tensor2LightBuffer(curTensor[i].reshape([num_of_slices, slice_height, slice_width, expected_Num_labels]), 'float16'); - prediction_argmax = argMaxLarge(modelOutBuffer, num_of_slices, slice_height, slice_width, expected_Num_labels, 'float16'); - console.log("argMaxLarge for fullVolume takes : ", ((performance.now() - argMaxLargeTime)/1000).toFixed(4) ); - - } catch(err2) { - - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - prediction_argmax.dispose(); - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err2.message; - statData["Extra_Err_Info"] = "preModel prediction_argmax from argMaxLarge failed"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - - } - - } else { - // if channel first .. - let errTxt = "argMax buffer couldn't be created due to limited memory resources."; - webix.alert(errTxt); - - prediction_argmax.dispose(); - - window.clearInterval( timer ); - tf.engine().endScope(); - tf.engine().disposeVariables(); - - statData["Inference_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = err1.message; - statData["Extra_Err_Info"] = "preModel prediction_argmax from argMaxLarge not support yet channel first"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - } - - - - console.log(" Pre-model prediction_argmax shape : ", prediction_argmax.shape); - //-- prediction_argmax.shape : [ 1, 256, 256, 256] - - let Inference_t = ((performance.now() - inferenceStartTime)/1000).toFixed(4); - - tf.dispose(curTensor[i]); - - console.log(" Pre-model find array max "); - let curBatchMaxLabel = findArrayMax(Array.from(prediction_argmax.dataSync())); - - if( maxLabelPredicted < curBatchMaxLabel ) { - maxLabelPredicted = curBatchMaxLabel; - } - - let numSegClasses = maxLabelPredicted + 1; - console.log("Pre-model numSegClasses", numSegClasses); - - statData["Actual_Labels"] = numSegClasses; - statData["Expect_Labels"] = expected_Num_labels; - statData["NumLabels_Match"] = numSegClasses == expected_Num_labels? true : false; - - //-- Transpose back to fit Papaya display settings - let outLabelVolume = prediction_argmax.reshape([num_of_slices, slice_height, slice_width]); - tf.dispose(prediction_argmax); - - // Transpose MRI data to be match pytorch/keras input output - if(transpose) { - console.log("Pre-model outLabelVolume transposed"); - outLabelVolume = outLabelVolume.transpose(); - } - - - startTime = performance.now(); - // Generate output volume or slices - console.log("Generating pre-model output"); - - try { - slices_3d_mask = tf.tidy(() => { - let unstackOutVolumeTensor = tf.unstack(outLabelVolume); - tf.dispose(outLabelVolume); - return generateBrainMask(unstackOutVolumeTensor, num_of_slices, slice_height, slice_width); - }); - - console.log(" Phase-1 num of tensors after generateBrainMask: " , tf.memory().numTensors ); - - } catch (error) { - - //-- Timing data to collect - tf.engine().endScope(); - tf.engine().disposeVariables(); - - webix.alert("Failed while generating pre-model output due to limited browser memory available"); - - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Infinity; - statData["Status"] = "Fail"; - statData["Error_Type"] = error.message; - statData["Extra_Err_Info"] = "Pre-model failed while generating output"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - return 0; - } - - let Postprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - - console.log("Pre-model processing the whole brain volume in tfjs tooks for multi-class output mask : ", - ((performance.now()-inferenceStartTime)/1000).toFixed(4) + " Seconds"); - - - //-- Timing data to collect - statData["Inference_t"] = Inference_t; - statData["Postprocess_t"] = Postprocess_t; - statData["Status"] = "OK"; - - if(opts.telemetryFlag) { - submitTiming2GoogleSheet(statData); - } - - - if(slices_3d_mask == null) { - - console.log("slice_3d_mask failed ..."); - webix.message("slice_3d_mask failed ..."); - return 0; - - } else { - - //--Phase-2, After remove the skull try to allocate brain volume and make inferece - console.log("--- pre-model done ---"); - // --mask_3d = slices_3d_mask.greater([0]).asType('bool'); - // --slices_3d_mask.dispose(); - - if(isModelFullVol) { - - if(modelEntry["enableSeqConv"]) { - // Mask cropping & seq conv - // Non-Atlas model (e.g. GWM) needs sequential convolution layer. - // Sequential convolution layer to be used after cropping - slow but reliable on most machines - console.log("------ Mask Cropping & Seq Convoluton ------"); - inferenceFullVolumeSeqCovLayerPhase2(model, slices_3d, num_of_slices, slice_height, slice_width, slices_3d_mask); - // inferenceFullVolumeSeqCovLayerPhase2(model, slices_3d.transpose(), num_of_slices, slice_height, slice_width, slices_3d_mask); - } else { - // Mask cropping BUT no seq conv - console.log("------ Mask Cropping - NO Seq Convoluton ------"); - inferenceFullVolumePhase2(model, slices_3d, num_of_slices, slice_height, slice_width, slices_3d_mask); - // inferenceFullVolumePhase2(model, slices_3d.transpose(), num_of_slices, slice_height, slice_width, slices_3d_mask); - } - - } else { - // -- In version 3.0.0 this function not used - inferenceSubVolumes(model, slices_3d, num_of_slices, slice_height, slice_width, slices_3d_mask); - //inferenceSubVolumes(model, slices_3d.transpose(), num_of_slices, slice_height, slice_width, slices_3d_mask); - } - - } - - } - i++; - - }, delay); - - } catch(err) { - - webix.alert(err.message); - console.log( err.message ); - console.log( - "If webgl context is lost, try to restore webgl context by visit the link " + - 'here' - ); - - - document.getElementById("webGl2Status").style.backgroundColor = isWebGL2ContextLost() ? "Red" : "Green"; - - document.getElementById("memoryStatus").style.backgroundColor = tf.memory().unreliable ? "Red" : "Green"; - } - }); - - //-- if(...) end - } else { // No preModel - - //--Phase-2, After remove the skull try to allocate brain volume and make inferece - console.log("--- No pre-model is selected ---"); - console.log("------ Run voxel cropping ------"); - //-- mask_3d = slices_3d.greater([0]).asType('bool'); - - if(isModelFullVol) { - - if(modelEntry["enableSeqConv"]) { - // Voxel cropping & seq conv - // Non-Atlas model (e.g. GWM) needs sequential convolution layer. - // Sequential convolution layer to be used after cropping - slow but reliable on most machines - console.log("------ Seq Convoluton ------"); - inferenceFullVolumeSeqCovLayerPhase2(model, slices_3d, num_of_slices, slice_height, slice_width, null); - } else { - // Voxel cropping BUT no seq conv - inferenceFullVolumePhase2(model, slices_3d, num_of_slices, slice_height, slice_width, null); - } - - } else { - // -- In version 3.0.0 this function not used - inferenceSubVolumes(model, slices_3d, num_of_slices, slice_height, slice_width, null); - } - } - - - - } - - /** -* Inference Function -* @since 1.2.0 -* -*/ - - enableProductionMode = async(textureF16Flag = true) => { - - //-- tf.setBackend('cpu'); - //-- tf.removeBackend('cpu') - - //-- Calling enableProdMode() method - await tf.enableProdMode(); - //-- Setting debug mode of the environment - tf.env().set('DEBUG', false); - - - tf.env().set('WEBGL_FORCE_F16_TEXTURES', textureF16Flag); - //-- set this flag so that textures are deleted when tensors are disposed. - tf.env().set("WEBGL_DELETE_TEXTURE_THRESHOLD", 0); - //-- tf.env().set('WEBGL_PACK', false); - - //-- Put ready after sets above - await tf.ready(); - - //-- Printing output - console.log(tf.env().flags); - console.log("tf env() features :", tf.env().features); - console.log("tf env total features: ", Object.keys(tf.env().features).length); - console.log(tf.getBackend()); - } - -/** -* Pre-Inference settings -* @since 1.3.0 -* -*/ - -resetMainParameters = () => { - // free global variable of 16777216 voxel - allOutputSlices3DCC1DimArray = []; - Object.keys(outVolumeStatus).forEach(key => outVolumeStatus[key] = null); -} - -/** -* Inference function -* @since 1.0.0 -* -*/ - - runInference = async() => { - let startTime = performance.now(); - - const batchSize = opts.batchSize; - const numOfChan = opts.numOfChan; - - - - if (isNaN(batchSize) || batchSize != 1) { - webix.alert("The batch Size for input shape must be 1"); - return 0; - - } - - if (isNaN(numOfChan) || (numOfChan != 1)) { - webix.alert("The number of channels for input shape must be 1"); - return 0; - } - - tf.engine().startScope() - - console.log("Batch size: ", batchSize); - console.log("Num of Channels: ", numOfChan); - - //-- Get model - let modelEntry, model; - - if($$("selectModel").getValue() <= numOfModelsWithoutBrowse) { //-- inferenceModelsList orginal num of models are five - modelEntry = inferenceModelsList[$$("selectModel").getValue() - 1]; - model = load_model(modelEntry["path"]); - } else { - modelEntry = browserModelList.filter(entry => entry.id == $$("selectModel").getValue().toString())[0]; - model = load_browser_model( modelEntry.modelFile, modelEntry.weightFile); - } - - // Enable production model: - // true enable F16 bit, false enable F32 bit processing - await enableProductionMode(true); - - let modelObject = {}; - // get model object data e.g. layers etc - model.then(function(res) { - modelObject = res; - - let batchInputShape = []; - - // free global variable of 16777216 voxel - // allOutputSlices3DCC1DimArray = []; - // outputSceneRendered = false; - // read input shape from model.json object - batchInputShape = modelObject.layers[0].batchInputShape; - console.log(" Model batch input shape : ", batchInputShape) - - //-- Verify input shape - if(batchInputShape.length != 5) { - webix.alert("The model input shape must be 5D "); - return 0; - } - - let batch_D, batch_H, batch_W; - let slice_width, slice_height, num_of_slices; - let input_shape; - - - slice_width = niftiHeader.dims[1]; - slice_height = niftiHeader.dims[2]; - num_of_slices = niftiHeader.dims[3]; - - let isChannelLast = isModelChnlLast(modelObject); - - if(isChannelLast) { - console.log("Model Channel Last") - if (isNaN(batchInputShape[4]) || (batchInputShape[4] !=1)) { - webix.alert("The number of channels for input shape must be 1"); - return 0; - } - - batch_D = batchInputShape[1]; - batch_H = batchInputShape[2]; - batch_W = batchInputShape[3]; - - input_shape = [batchSize, batch_D, batch_H, batch_W, numOfChan]; - - } else { - console.log("Model Channel First") - if (isNaN(batchInputShape[1]) || (batchInputShape[1] !=1)) { - webix.alert("The number of channels for input shape must be 1"); - return 0; - } - - batch_D = batchInputShape[2]; - batch_H = batchInputShape[3]; - batch_W = batchInputShape[4]; - - input_shape = [batchSize, numOfChan, batch_D, batch_H, batch_W]; - - } - - // //-- Atlas version check - // if ( (batch_D > 30) && (batch_H == 256) && (batch_W == 256) ) { - // webix.alert("The subvolume dimension in z-axis shouldn't exceed 30 number of slices for browser limitation"); - // return 0; - // } - - //--Check whether the model will make inference at once as FullVolumeModel - let isModelFullVol; - - if ( (batch_D == 256) && (batch_H == 256) && (batch_W == 256) ) { - isModelFullVol = true; - - } else { - isModelFullVol = false; - - } - - - let modelNumLayers = modelObject.layers.length; - // Model output number of segmentations - let outLabels = modelObject.layers[ modelNumLayers - 1 ].bias.shape[0]; - - let allSlices = getAllSlicesData1D(num_of_slices, niftiHeader, niftiImage); - - let allSlices_2D = getAllSlices2D(allSlices, slice_height, slice_width); - - // free array from mem - allSlices = null; - - // Get slices_3d tensor - let slices_3d = getSlices3D(allSlices_2D); - - // free tensor from mem - tf.dispose(allSlices_2D); - - - // if(inferenceModelsList[$$("selectModel").getValue() - 1]["enableQuantileNorm"]) { - // // Quantile normalize function needs specific models to be used - // console.log("Quantile normalization enabled"); - // slices_3d = await quantileNormalizeVolumeData(slices_3d); - // } else { - // // Min Max Nomalize MRI data to be from 0 to 1 - // console.log("Min Max normalization enabled"); - // slices_3d = minMaxNormalizeVolumeData(slices_3d); - // } - - - let Preprocess_t = ((performance.now() - startTime)/1000).toFixed(4); - - - //-- Timing data to collect - let today = new Date(); - - if(isModelFullVol) { - statData["Brainchop_Ver"] = "FullVolume"; - } else { - statData["Brainchop_Ver"] = "SubVolumes"; - - } - - - let geoData = getBrowserLocationInfo(); - if(geoData) { - statData["Country"] = geoData["Country"]; - statData["State"] = geoData["Region"]; - statData["City"] = geoData["City"]; - } else { - statData["Country"] = ""; - statData["State"] = ""; - statData["City"] = ""; - } - - - - statData["Date"] = parseInt(today.getMonth() + 1) + "/" + today.getDate() + "/" + today.getFullYear(); - statData["Time"] = checkZero(today.getHours()) + ":" + checkZero(today.getMinutes()) + ":" + checkZero(today.getSeconds()); - statData["File_Name"] = refFileName == "" ? opts.uiSampleName: refFileName; - statData["Input_Shape"] = JSON.stringify(batchInputShape); - statData["Output_Shape"] = JSON.stringify(modelObject.output.shape); - statData["Channel_Last"] = isChannelLast; - statData["Model_Param"] = getModelNumParameters(modelObject); - statData["Model_Layers"] = getModelNumLayers(modelObject); - - statData["Preprocess_t"] = Preprocess_t; - statData["Model"] = inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"]; - statData["Browser"] = detectBrowser(); - statData["Browser_Ver"] = detectBrowserVersion(); - statData["OS"] = detectOperatingSys(); - statData["WebGL1"] = checkWebGl1(); - statData["WebGL2"] = checkWebGl2(); - statData["GPU_Vendor"] = detectGPUVendor(); - statData["GPU_Card"] = detectGPUCardType(); - statData["GPU_Vendor_Full"] = detectGPUVendor_v0(); - statData["GPU_Card_Full"] = detectGPUCardType_v0(); - statData["CPU_Cores"] = getCPUNumCores(); - statData["TF_Backend"] = tf.getBackend(); - - statData["Which_Brainchop"] = "latest"; - statData["Seq_Conv"] = inferenceModelsList[$$("selectModel").getValue() - 1]["enableSeqConv"]; - - - //-- Init - statData["Actual_Labels"] = Infinity; - statData["Expect_Labels"] = Infinity; - statData["NumLabels_Match"] = null; - statData["Inference_t"] = Infinity; - statData["Merge_t"] = Infinity; - statData["Postprocess_t"] = Infinity; - statData["Status"] = null; - statData["Error_Type"] = null; - statData["Extra_Err_Info"] = null; - statData["Extra_Info"] = null; - - - if(isChrome()) { - statData["Heap_Size_MB"] = window.performance.memory["totalJSHeapSize"]/(1024*1024).toFixed(2); - statData["Used_Heap_MB"] = window.performance.memory["usedJSHeapSize"]/(1024*1024).toFixed(2); - statData["Heap_Limit_MB"] = window.performance.memory["jsHeapSizeLimit"]/(1024*1024).toFixed(2); - } - - - let gl = checkWebGl2() ? document.createElement('canvas').getContext('webgl2') : - checkWebGl1() ? document.createElement('canvas').getContext('webgl1') : null; - - console.log("MAX_TEXTURE_SIZE :", gl.getParameter(gl.MAX_TEXTURE_SIZE)); - console.log("MAX_RENDERBUFFER_SIZE :", gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)); - - //-- check to see if machine has two graphics card: one is the builtin e.g. Intel Iris Pro, the other is NVIDIA GeForce GT 750M. - //-- check browser use which one, if debugInfo is null then installed GPU is not used - let debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - console.log("VENDOR WEBGL:", gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) ); - - if(gl) { - statData["Texture_Size"] = gl.getParameter(gl.MAX_TEXTURE_SIZE) //--returns the maximum dimension the GPU can address - } else { - statData["Texture_Size"] = null; - } - - - let transpose = inferenceModelsList[$$("selectModel").getValue() - 1]["enableTranspose"]; - let enableCrop = inferenceModelsList[$$("selectModel").getValue() - 1]["enableCrop"]; - - - if (isModelFullVol) { - - if( enableCrop) { - // FullVolume with Crop option before inference .. - // pre-model to mask the volume, can also be null and the cropping will be on the MRI. - inferenceFullVolumePhase1(model, slices_3d, num_of_slices, slice_height, slice_width, isModelFullVol); - } else { - // Transpose MRI data to be match pytorch/keras input output - console.log("Cropping Disabled"); - - if(transpose) { - slices_3d = slices_3d.transpose() - console.log("Input transposed"); - } else { - console.log("Transpose NOT Enabled"); - } - - let enableSeqConv = inferenceModelsList[$$("selectModel").getValue() - 1]["enableSeqConv"]; - - if(enableSeqConv) { - console.log("Seq Convoluton Enabled"); - inferenceFullVolumeSeqCovLayer(model, slices_3d, input_shape, isChannelLast, num_of_slices, slice_height, slice_width); - } else { - console.log("Seq Convoluton Disabled"); - inferenceFullVolume(model, slices_3d, input_shape, isChannelLast, num_of_slices, slice_height, slice_width); - } - - - } - - } else { - - // // In version 3.0.0 this function not used - //-- if(enableCrop) { - // // FullVolume with Crop option before inference .. - // // pre-model to mask the volume, can also be null and the cropping will be on the MRI. - //-- inferenceFullVolumePhase1(model, slices_3d, num_of_slices, slice_height, slice_width, isModelFullVol); - //-- } else { - // // Transpose MRI data to be match pytorch/keras input output - //-- if(transpose) { - //-- slices_3d = slices_3d.transpose() - //-- console.log("Input transposed"); - //-- } else { - //-- console.log("Transpose not enabled"); - //-- } - - //-- inferenceSubVolumes(model, slices_3d, num_of_slices, slice_height, slice_width); - //-- } - - console.log("This is not a full volume model"); - webix.alert({title: "", text: "This is not a full volume model", type:"alert-error"}); - - } - - }) //-- End of model.then - - } //-- End of runInference - - -})(); diff --git a/v3/js/brainchop/mainNiftiReadingFunctions.js b/v3/js/brainchop/mainNiftiReadingFunctions.js deleted file mode 100644 index 303748d..0000000 --- a/v3/js/brainchop/mainNiftiReadingFunctions.js +++ /dev/null @@ -1,635 +0,0 @@ -/* -========================================================= -* Brainchop - v2.0.1 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 38, 38, 38, 1] -* Model : Meshnet or similar -* -* Authors: Mohamed Masoud and Sergey Plis - 2022 -========================================================= - - - -========================================================= - Brainchop for 3D Brain Segmentation -=========================================================*/ - - -/** For future use -* Function to normalize Nifti image data for nii files with float values or values > 255 -* -* @since 1.0.0 -* @param {ArrayBuffer} rawData, raw data of browsing Nifti file -* @returns {ArrayBuffer} Returns normalized Nifti image ArrayBuffer -* @example -* -* normalizeNiftiRawData(ArrayBuffer) -* -* // => ArrayBuffer { byteLength: 16777568 } -*/ - -normalizeNiftiImageData = (rawData) => { - let headerObj = readNiftiHeader(rawData); - let imageDataArrBuf = readNiftiImageData(headerObj, rawData); - let imageDataArr = arrayBuffer2Array(imageDataArrBuf, headerObj.datatypeCode); - - //Round image data - let imageDataTensor = tf.round(tf.tensor1d(imageDataArr)); - //Normalize image data - imageDataArr = tensor2Array(tf.mul(normalizeVolumeData(imageDataTensor), tf.scalar(255))); - - return array2ArrayBuffer(imageDataArr); -} - -/* -* Function to check Nifti file whether it needs resampling by use mri_convert.js -* Check if voxel 1mm dim -* @since 1.1.0 -* @param {Object} niftiHeader, Nifti header object -* @returns {boolen} Returns true/false -* @example -* -*/ - - -isVoxelSize1mm = (niftiHeader) => { - - for(let i = 1; i <= niftiHeader.dims[0]; i++) { - if(niftiHeader['pixDims'][i] != 1){ - return false; - } - } - - return true; -} - -/* -* Function to check Nifti file whether it needs resampling/conversion/Normalization by use mri_convert.js -* Check MRI shape, num of pixel, data type Code( 2 for int). -* @since 1.1.0 -* @param {Object} niftiHeader, Nifti header object -* @returns {boolen} Returns true/false -* @example -* -*/ - -isNiftiFileVerified = (niftiHeader) => { - - if( (niftiHeader.dims[1]!= 256) || (niftiHeader.dims[2]!= 256) || (niftiHeader.dims[3]!= 256) || - (niftiHeader['numBitsPerVoxel'] != 8) || (niftiHeader['datatypeCode'] != 2) || - !isVoxelSize1mm(niftiHeader) ) - { - return false; - } - - return true; -} - - - -/** -* Function to decompress/check Nifti arraybuffer from uploaded file source -* -* @since 1.0.0 -* @param {ArrayBuffer} rawData, raw data of browsing Nifti file -* @returns {ArrayBuffer} Returns checked/decompressed Nifti ArrayBuffer -* @example -* -*reader.onloadend = function(event) { -* getNiftiRawData(event.target.result) -*} -* // => ArrayBuffer { byteLength: 16777568 } -*/ - -getNiftiRawData = (rawData) => { - - // Parse nifti - if (nifti.isCompressed(rawData)) { - rawData = nifti.decompress(rawData); - } - - return nifti.isNIFTI(rawData) ? rawData : null; -} - - -/** -* Function to read Nifti header -* -* @since 1.0.0 -* @param {ArrayBuffer} Nifti Raw data -* @returns {Object} Returns Nifti header object -* @example -* -* readNiftiHeader(ArrayBuffer) -* // => Object { littleEndian: true, dim_info: 0, dims: (8) […], datatypeCode: 2, numBitsPerVoxel: 8, slice_start: 0, … } -*/ - -readNiftiHeader = (rawNiftiData) => { - return rawNiftiData ? nifti.readHeader(rawNiftiData) : null; -} - -/** -* Function to read Nifti file image data -* -* @since 1.0.0 -* @param {ArrayBuffer} data, raw data of browsing Nifti file -* @param {Object} Nifti header data -* @returns {ArrayBuffer} Returns Nifti image ArrayBuffer -* @example -* -* readNiftiImageData(headerObject, ArrayBuffer) -* -* // => ArrayBuffer { byteLength: 16777216 } -*/ - -readNiftiImageData = (niftiHeader, rawNiftiData) => { - return niftiHeader ? nifti.readImage(niftiHeader, rawNiftiData) : null; -} - -/** -* Create Nifti Output Array Buffer -* -* @since 1.0.0 -* @param {ArrayBuffer} rawData -* @param {Array} data -* @returns {ArrayBuffer} Returns Output labels as ArrayBuffer -* -*/ - -function createNiftiOutArrayBuffer(rawData, data) { - // Read raw NIfTI data header - let header = readNiftiHeader(rawData); - let imageOffset = header.vox_offset; - let timeDim = header.dims[4] || 1; - let statDim = header.dims[5] || 1; - let voxelSize = header.numBitsPerVoxel / 8; - let imageSize = header.dims[1] * header.dims[2] * header.dims[3] * timeDim * statDim * voxelSize; - - // Create a new ArrayBuffer that can contain both the header and the image data - let combinedBuffer = new ArrayBuffer(imageOffset + imageSize); - - // Copy the header into the combined buffer - let headerBuffer = new Uint8Array(rawData.slice(0, imageOffset)); - let combinedArray = new Uint8Array(combinedBuffer); - combinedArray.set(headerBuffer); - - // Zero out the vox_offset and scl_slope fields - // Note: Adjust the byte offsets according to the actual structure of your NIfTI header - combinedArray.fill(0, 112, 120); // Assuming these offsets for vox_offset and scl_slope - - // Copy the image data into the combined buffer at the appropriate offset - // localData = new Uint8Array(data); - let imageDataArray = new Uint8Array(data); // new Uint8Array(localData.buffer, localData.byteOffset, localData.byteLength); - combinedArray.set(imageDataArray, imageOffset); - - return combinedBuffer; -} - -function nifti2data(rawNiftiData) { - // Read raw NIfTI data header - let header = readNiftiHeader(rawNiftiData); - let imageOffset = header.vox_offset; - let timeDim = header.dims[4] || 1; - let statDim = header.dims[5] || 1; - let voxelSize = header.numBitsPerVoxel / 8; - let imageSize = header.dims[1] * header.dims[2] * header.dims[3] * timeDim * statDim * voxelSize; - - // Create a typed array for the image data based on its data type - let imageDataTypedArray; - switch (header.datatypeCode) { - case nifti.NIFTI1.TYPE_UINT8: - imageDataTypedArray = new Uint8Array(rawNiftiData, imageOffset, imageSize / voxelSize); - break; - // Include other cases for different data types as needed - default: - throw new Error('Unsupported NIfTI data type'); - } - return imageDataTypedArray; -} - -/** -* Convert array to ArrayBuffer -* -* @since 1.0.0 -* @param {Array} array -* @returns {ArrayBuffer} Returns -* @example -* -* array2ArrayBuffer([1,2,3]) -* // => ArrayBuffer { byteLength: 3 } -* -*/ - -array2ArrayBuffer = (array, datatypeCode) => { - let typedArray; - - if (datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedArray = Uint8Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedArray = Int16Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedArray = Int32Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedArray = Float32Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedArray = Float64Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedArray = Int8Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedArray = Uint16Array.from(array); - } else if (datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedArray = Uint32Array.from(array); - } else { - return; - } - - - // Convert typedArray to ArrayBuffer and return ArrayBuffer - return typedArray.buffer.slice(typedArray.byteOffset, typedArray.byteLength + typedArray.byteOffset) -} - - -/** -* Convert ArrayBuffer to array -* -* @since 1.0.0 -* @param {ArrayBuffer} arrayBuffer -* @returns {Array} Returns -* @example -* -* arrBuf = array2ArrayBuffer([1,2,3]) -* -* arrayBuffer2Array( arrBuf) -* // => [ 1, 2, 3 ] -* -*/ - -arrayBuffer2Array = (arrayBuffer, datatypeCode) => { - let typedArrData; - - if (datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedArrData = new Uint8Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedArrData = new Int16Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedArrData = new Int32Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedArrData = new Float32Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedArrData = new Float64Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedArrData = new Int8Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedArrData = new Uint16Array(arrayBuffer); - } else if (datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedArrData = new Uint32Array(arrayBuffer); - } else { - return; - } - - - // Convert typedArray to array using Spread syntax[...] - let arr = [...typedArrData]; - return arr; -} - - - -/** -* Convert typedArray to array -* -* @since 1.0.0 -* @param {TypedArray} typedArray e.g. Uint8Array(65536) [ 0, 0, 0, 0 ... ] -* @returns {Array} Returns -* @example -* -* typedArr -* // => Uint8Array(3) [1,2,3] -* -* typedArray2Array(typedArr ) -* -* // => [ 1, 2, 3 ] -* -*/ - -typedArray2Array = (typedArray) => { - // Convert typedArray to array using Spread syntax[...] - return [...typedArrData]; -} - - - -/** -* Download Nifti data as *.nii file -* -* @since 1.0.0 -* @param {Array} mriData - All Output Slices labels as 1Dim Array -* @param {string} fileName -* @param {ArrayBuffer} rawNiftiData -* -*/ - - -downloadNifti = (mriData, rawNiftiData, fileName ) => { - var downloadFileData = (function () { - var a = document.createElement("a"); - document.body.appendChild(a); - a.style = "display: none"; - return function (data, fileName) { - // create Blob "Binary Large Object" of type octet-binary for the ArrayBuffer - var blob = new Blob([data], {type: "application/octet-binary;charset=utf-8"}); - var url = window.URL.createObjectURL(blob); - a.href = url; - a.download = fileName; - a.click(); - window.URL.revokeObjectURL(url); - }; - }()); - - //Get the data ArrayBuffer - var data = createNiftiOutArrayBuffer(rawNiftiData, mriData); - - if (nifti.isNIFTI(data)) { - // save arraybuffer data to disk - downloadFileData(data, fileName); - } else { - console.log("Not Nifti data....... "); - } -} - - -/** -* Download Json data as *.json file -* -* @since 1.2.0 -* @param {Object} jsonObj - -* @param {string} fileName -* @example -* -* downloadJsonObj( {1: "test1", 2: "test2"}, "test.json") -* -*/ - - -downloadJsonObj = (jsonObj, fileName ) => { - var downloadFileData = (function () { - var a = document.createElement("a"); - document.body.appendChild(a); - a.style = "display: none"; - return function (data, fileName) { - // create Blob "Binary Large Object" of type octet-binary for the ArrayBuffer - var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonObj)); - a.href = dataStr; - a.download = fileName; - a.click(); - }; - }()); - - if (Object.keys(jsonObj).length) { - // save arraybuffer data to disk - downloadFileData(jsonObj, fileName); - } else { - console.log("Not JSON data found....... "); - } -} - - -/** -* Download current viewer canvas data as *.png or *.jpg image file -* -* @since 1.0.0 -* @param {object} canvas e.g. papayaContainers[0].viewer.canvas -* @param {string} fileName -* @example -* -* downloadCanvas( papayaContainers[0].viewer.canvas, "slice125.png") -* -*/ - -downloadCanvas = (canvas, fileName) => { // canvas : papayaContainers[0].viewer.canvas - var downloadFileData = (function () { - var a = document.createElement("a"); - document.body.appendChild(a); - a.style = "display: none"; - return function (canvas, fileName) { - let image = canvas.toDataURL("image/jpg"); - a.href = image; - a.download = fileName; - a.click(); - }; - }()); - - downloadFileData(canvas, fileName); -} - - -/** -* Find max value with array -* -* @since 1.0.0 -* @param {Array} arr -* @returns {number} max -* @example -* -* -* labelMax( [1,2,3]) -* // => 3 -* -*/ - -labelMax = (arr) => { - let max = arr[0]; - - for(let i=1; i < arr.length; i++) { - if(arr[i] > max) { - max = arr[i]; - } - } - - return max; -} - - -/** -* Find the frequence of array unique values -* @since 1.1.0 -* @param {Array} arr -* @returns {Map} returnMap e.g. returnMap = { 2 → 4, 3 → 1}: use returnMap.get(2) --> 4 -* @example -* -* -* arrValuesFreq( [2, 2, 2, 2, 3]) -* // => Map(900) { 2 → 4, 3 → 1} -* -*/ - - -arrValuesFreq = (arr)=> { - let arrCopy = [...arr]; //clone - arrCopy.sort(); - let resultMap = arrCopy.reduce((acc, curr) => acc.set(curr, (acc.get(curr) || 0) + 1), new Map()); - return resultMap; - -} - - -/** -* Function to convert Map to Json object need JS ES6, -* Object.fromEntries add to ES6 since 2017 -* -* @param {Map} map { a → 1, b → 2 } -* @returns {Object} e.g. Object { a: 1, b: 2 } -* @example -* -* -* map2Object( new Map().set('a', 1).set('b', 2) ) -* // => Object { a: 1, b: 2 } -* -*/ - - -map2Object = (map)=> { - return Object.fromEntries(map); -} - - -/** -* Find if browser supports Workers -* @since 1.2.0 -* @returns {bool} true/false -* @example -* -*/ - -isWinWorkerSupported = () => { - return window.Worker ? true : false; -} - - -/** -* Draw Ground Truth labels if any -* -* @since 1.0.0 -* @param {Array} canvas -* @param {number} sliceIdx -* @param {object} labelNiftiHeader- The header of nifti file. -* @param {ArrayBuffer} labelNiftiImage- The image data of nifti file. -*/ - -drawGtCanvas = (canvas, sliceIdx, labelNiftiHeader, labelNiftiImage) => { - // get nifti dimensions - let cols = labelNiftiHeader.dims[1]; - let rows = labelNiftiHeader.dims[2]; - - // Set canvas dimensions to nifti slice dimensions - canvas.width = cols; - canvas.height = rows; - - - // Make canvas image data - let ctx = canvas.getContext("2d"); - let canvasImageData = ctx.createImageData(canvas.width, canvas.height); - - // Convert raw data to typed array based on nifti datatype - let typedData; - - if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT8) { - typedData = new Uint8Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT16) { - typedData = new Int16Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT32) { - typedData = new Int32Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT32) { - typedData = new Float32Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_FLOAT64) { - typedData = new Float64Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_INT8) { - typedData = new Int8Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT16) { - typedData = new Uint16Array(labelNiftiImage); - } else if (labelNiftiHeader.datatypeCode === nifti.NIFTI1.TYPE_UINT32) { - typedData = new Uint32Array(labelNiftiImage); - } else { - return; - } - - - let n_classes = labelMax(typedData) + 1; - // offset to specified slice - let sliceSize = cols * rows; - let sliceOffset = sliceSize * sliceIdx; - - // draw pixels - for (let row = 0; row < rows; row++) { - let rowOffset = row * cols; - - for (let col = 0; col < cols; col++) { - let offset = sliceOffset + rowOffset + col; - let value = typedData[offset]; - - /* - Assumes data is 8-bit, otherwise you would need to first convert - to 0-255 range based on datatype range, data range (iterate through - data to find), or display range (cal_min/max). - - Other things to take into consideration: - - data scale: scl_slope and scl_inter, apply to raw value before - applying display range - - orientation: displays in raw orientation, see nifti orientation - info for how to orient data - - assumes voxel shape (pixDims) is isometric, if not, you'll need - to apply transform to the canvas - - byte order: see littleEndian flag - */ - value = Math.ceil(value*255/(n_classes - 1)) - - canvasImageData.data[(rowOffset + col) * 4] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 1] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 2] = value & 0xFF; - canvasImageData.data[(rowOffset + col) * 4 + 3] = 0xFF; - - } - } - - ctx.putImageData(canvasImageData, 0, 0); - - } - -/** -* Draw Ground Truth labels if any -* -* @since 1.0.0 -* @param {object} file -* @param {number} start - e.g. 0 -* @param {number} length - File size -* @returns {blob object} -* -*/ - - makeSlice = (file, start, length) => { - var fileType = (typeof File); - - if (fileType === 'undefined') { - return function () {}; - } - - if (File.prototype.slice) { - return file.slice(start, start + length); - } - - if (File.prototype.mozSlice) { - return file.mozSlice(start, length); - } - - if (File.prototype.webkitSlice) { - return file.webkitSlice(start, length); - } - - return null; - } - - - - diff --git a/v3/js/brainchop/mainParameters.js b/v3/js/brainchop/mainParameters.js deleted file mode 100644 index df27a75..0000000 --- a/v3/js/brainchop/mainParameters.js +++ /dev/null @@ -1,510 +0,0 @@ -/* -========================================================= -* Brainchop - v3.4.0 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 38, 38, 38, 1] -* Model : Meshnet or similar -* -* Authors: Mohamed Masoud and Sergey Plis - 2024 -========================================================= - - - -========================================================= - Brainchop for 3D Brain Segmentation -=========================================================*/ - - - - //---------- initialize Globals-------// - - - //Raw Nifti Data and header - var rawNiftiData = []; - //Object - var niftiHeader = []; - //ArrayBuffer - var niftiImage = []; - //Object - var labelNiftiHeader = []; - //ArrayBuffer - var labelNiftiImage = []; - //Flag for futur use - var gtLabelLoaded = false; - - // Nifti file to load *.nii - var refFileName = ''; - - // When browsing to tfjs model with browse window - var modelFile; - var weightFile; - var labelFile; - // Models loaded from browsing window - var browserModelList = []; - - // Number of overlay added to MRI viewer - var numOfOverlays; - - var numOfModelsWithoutBrowse; - - - // Parameters change with each inference - var allOutputSlices3DCC1DimArray = []; - - var outputSceneRendered = false, inputSceneRendered = false; - var output_gui = {}, input_gui = {}; - - - - - var opts = { - // General settings for input shape [batchSize, batch_D, batch_H, batch_W, numOfChan]; - batchSize: 1, //How many batches are used during each inference iteration - numOfChan: 1, // num of channel of the input shape - - isColorEnable: true, // If false, grey scale will enabled - isAutoColors: true, // If false, manualColorsRange will be in use - bgLabelValue: 0, // Semenatic Segmentation background label value - - drawBoundingVolume: false, // plot bounding volume used to crop the brain - isBrainCropMaskBased: true, // Check if brain masking will be used for cropping & optional show or brain tissue will be used - showPhase1Output: false, // This will load to papaya the output of phase-1 (ie. brain mask or brain tissue) - - isPostProcessEnable: true, // If true 3D Connected Components filter will apply - - isContoursViewEnable: false, // If true 3D contours of the labeled regions will apply - - browserArrayBufferMaxZDim: 30, // This value depends on Memory available - - telemetryFlag: false, // Ethical and transparent collection of browser usage while adhering to security and privacy standards - - chartXaxisStepPercent: 10, // percent from total labels on Xaxis - - uiSampleName: "BC_UI_Sample", // Sample name used by interface - - atlasSelectedColorTable: "Fire" // Select from ["Hot-and-Cold", "Fire", "Grayscale", "Gold", "Spectrum"] - } - - // Statistical data to analysis client performance - var statData = { Brainchop_Ver: null, Country: null, State: null, City: null, Date: null, Time: null, - File_Name: null, Img_Size: null, Num_Bits_Per_Voxel: null, Data_Type_Code: null, Vox_Offset: null, Vox_1mm: null, Resampled: null, File_Verified: null, - Input_Shape: null, Output_Shape: null, Channel_Last: null, Model_Param: Infinity, Model_Layers: Infinity, - No_SubVolumes: null, Actual_Labels: Infinity, Expect_Labels: Infinity, NumLabels_Match: null, - Data_Load: null, Preprocess_t: null, Inference_t: null, Merge_t: null, Postprocess_t: null, - Model: null, Browser: null, Browser_Ver: null, OS: null, Texture_Size: null, Heap_Size_MB: Infinity, Used_Heap_MB: Infinity, Heap_Limit_MB: Infinity, - WebGL1: null, WebGL2: null, TF_Backend: null, GPU_Vendor: null, GPU_Vendor_Full: null, - GPU_Card: null, GPU_Card_Full:null, Status: null, CPU_Cores: null, Error_Type: null, Extra_Err_Info: null, Extra_Info: null, Which_Brainchop: null, Seq_Conv: null }; - - - - // Inference Models, the ids must start from 1 in sequence - var inferenceModelsList = [ - { - id: 1, - type: "Segmentation", - path: "./models/model5_gw_ae/model.json", - modelName: "\u26A1 Tissue GWM (light)", - labelsPath: "./models/model5_gw_ae/labels.json", - colorsPath: "./models/model5_gw_ae/colorLUT.json", - preModelId: null, // Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 2, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: null, // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Gray and white matter segmentation model. Operates on full T1 image in a single pass, but uses only 5 filters per layer. Can work on integrated graphics cards but is barely large enough to provide good accuracy. Still more accurate than the subvolume model." - } - - ,{ - id: 2, - type: "Segmentation", - path:"./models/model20chan3cls/model.json", - modelName:"\u{1F52A} Tissue GWM (High Acc)", - labelsPath: "./models/model20chan3cls/labels.json", - colorsPath: "./models/model20chan3cls/colorLUT.json", - preModelId: null, // Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0.2, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: true, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Gray and white matter segmentation model. Operates on full T1 image in a single pass but needs a dedicated graphics card to operate. Provides the best accuracy with hard cropping for better speed" - } - - ,{ - id: 3, - type: "Segmentation", - path:"./models/model20chan3cls/model.json", - modelName:"\u{1F52A} Tissue GWM (High Acc, Low Mem)", - labelsPath: "./models/model20chan3cls/labels.json", - colorsPath: "./models/model20chan3cls/colorLUT.json", - preModelId: null, // Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0.2, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: true, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Gray and white matter segmentation model. Operates on full T1 image in a single pass but needs a dedicated graphics card to operate. Provides high accuracy and fit low memory available but slower" - } - - - - ,{ - id: 4, - type: "Atlas", - path:"./models/model30chan18cls/model.json", - modelName:"\u{1FA93} Subcortical + GWM (High Mem, Fast)", - labelsPath: "./models/model30chan18cls/labels.json", - colorsPath: "./models/model30chan18cls/colorLUT.json", - preModelId: null,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0.2, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Parcellation of the brain into 17 regions: gray and white matter plus subcortical areas. This is a robust model able to handle range of data quality, including varying saturation, and even clinical scans. It may work on infant brains, but your mileage may vary." - } - - ,{ - id: 5, - type: "Atlas", - path:"./models/model30chan18cls/model.json", - modelName:"\u{1FA93} Subcortical + GWM (Low Mem, Slow)", - labelsPath: "./models/model30chan18cls/labels.json", - colorsPath: "./models/model30chan18cls/colorLUT.json", - preModelId: null,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0.2, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Parcellation of the brain into 17 regions: gray and white matter plus subcortical areas. This is a robust model able to handle range of data quality, including varying saturation, and even clinical scans. It may work on infant brains, but your mileage may vary." - } - - ,{ - id: 6, - type: "Atlas", - path:"./models/model18cls/model.json", - modelName:"\u{1FA93} Subcortical + GWM (Low Mem, Faster)", - labelsPath: "./models/model18cls/labels.json", - colorsPath: "./models/model18cls/colorLUT.json", - preModelId: null, // model run first e.g. Brain_Extraction { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0.2, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Parcellation of the brain into 17 regions: gray and white matter plus subcortical areas. This is a robust model able to handle range of data quality, including varying saturation, and even clinical scans. It may work on infant brains, but your mileage may vary." - } - - ,{ - id: 7, - type: "Atlas", - path:"./models/model30chan18cls/model.json", - modelName:"\u{1F52A}\u{1FA93} Subcortical + GWM (Failsafe, Less Acc)", - labelsPath: "./models/model30chan18cls/labels.json", - colorsPath: "./models/model30chan18cls/colorLUT.json", - preModelId: 1, // model run first e.g. Brain_Extraction { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Parcellation of the brain into 17 regions: gray and white matter plus subcortical areas. This is not a robust model, it may work on low data quality, including varying saturation, and even clinical scans. It may work also on infant brains, but your mileage may vary." - } - - ,{ - id: 8, - type: "Atlas", - path:"./models/model30chan50cls/model.json", - modelName:"\u{1F52A} Aparc+Aseg 50 (High Mem, Fast)", - labelsPath: "./models/model30chan50cls/labels.json", - colorsPath: "./models/model30chan50cls/colorLUT.json", - preModelId: 1,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: true, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "This is a 50-class model, that segments the brain into the Aparc+Aseg Freesurfer Atlas but one where cortical homologues are merged into a single class." - } - - ,{ - id: 9, - type: "Atlas", - path:"./models/model30chan50cls/model.json", - modelName:"\u{1F52A} Aparc+Aseg 50 (Low Mem, Slow)", - labelsPath: "./models/model30chan50cls/labels.json", - colorsPath: "./models/model30chan50cls/colorLUT.json", - preModelId: 1,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: true, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last laye - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "This is a 50-class model, that segments the brain into the Aparc+Aseg Freesurfer Atlas but one where cortical homologues are merged into a single class. The model use sequential convolution for inference to overcome browser memory limitations but leads to longer computation time." - } - - - ,{ - id: 10, - type: "Brain_Extraction", - path: "./models/model5_gw_ae/model.json", - modelName:"\u26A1 Extract the Brain (FAST)", - labelsPath: null, - colorsPath: null, - preModelId: null, // Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 2, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: null, // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Extract the brain fast model operates on full T1 image in a single pass, but uses only 5 filters per layer. Can work on integrated graphics cards but is barely large enough to provide good accuracy. Still more accurate than the failsafe version." - } - - ,{ - id: 11, - type: "Brain_Extraction", - path: "./models/model11_gw_ae/model.json", - modelName:"\u{1F52A} Extract the Brain (High Acc, Slow)", - labelsPath: null, - colorsPath: null, - preModelId: 1, // Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 2, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "Extract the brain high accuracy model operates on full T1 image in a single pass, but uses only 11 filters per layer. Can work on dedicated graphics cards. Still more accurate than the fast version." - } - - ,{ - id: 12, - type: "Brain_Masking", - path: "./models/model5_gw_ae/model.json", - modelName:"\u26A1 Brain Mask (FAST)", - labelsPath: null, - colorsPath: null, - preModelId: null,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 2, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: null, // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "This fast masking model operates on full T1 image in a single pass, but uses only 5 filters per layer. Can work on integrated graphics cards but is barely large enough to provide good accuracy. Still more accurate than failsafe version." - } - - ,{ - id: 13, - type: "Brain_Masking", - path: "./models/model11_gw_ae/model.json", - modelName:"\u{1F52A} Brain Mask (High Acc, Low Mem)", - labelsPath: null, - colorsPath: null, - preModelId: 1,// Model run first e.g. crop the brain { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 0, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 2, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "This masking model operates on full T1 image in a single pass, but uses 11 filters per layer. Can work on dedicated graphics cards. Still more accurate than fast version." - } - - ,{ - id: 14, - type: "Atlas", - path:"./models/model21_104class/model.json", - modelName:"\u{1F52A} Aparc+Aseg 104 (High Mem, Fast)", - labelsPath: "./models/model21_104class/labels.json", - colorsPath: "./models/model21_104class/colorLUT.json", - preModelId: 1, // model run first e.g. Brain_Extraction { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: false, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "FreeSurfer aparc+aseg atlas 104 parcellate brain areas into 104 regions. It contains a combination of the Desikan-Killiany atlas for cortical area and also segmentation of subcortical regions." - } - - ,{ - id: 15, - type: "Atlas", - path:"./models/model21_104class/model.json", - modelName:"\u{1F52A} Aparc+Aseg 104 (Low Mem, Slow)", - labelsPath: "./models/model21_104class/labels.json", - colorsPath: "./models/model21_104class/colorLUT.json", - preModelId: 1, // model run first e.g. Brain_Extraction { null, 1, 2, .. } - preModelPostProcess: false, // If true, perform postprocessing to remove noisy regions after preModel inference generate output. - isBatchOverlapEnable: false, //create extra overlap batches for inference - numOverlapBatches: 200, //Number of extra overlap batches for inference - enableTranspose : true, // Keras and tfjs input orientation may need a tranposing step to be matched - enableCrop: true, // For speed-up inference, crop brain from background before feeding to inference model to lower memory use. - cropPadding: 0, // Padding size add to cropped brain - autoThreshold: 0, // Threshold between 0 and 1, given no preModel and tensor is normalized either min-max or by quantiles. Will remove noisy voxels around brain - enableQuantileNorm: false, // Some models needs Quantile Normaliztion. - filterOutWithPreMask: false, // Can be used to multiply final output with premodel output mask to crean noisy areas - enableSeqConv: true, // For low memory system and low configuration, enable sequential convolution instead of last layer - textureSize: 0, // Requested Texture size for the model, if unknown can be 0. - warning: "This model may need dedicated graphics card. For more info please check with Browser Resources .", // Warning message to show when select the model. - inferenceDelay: 100, // Delay in ms time while looping layers applying. - description: "FreeSurfer aparc+aseg atlas 104 parcellate brain areas into 104 regions. It contains a combination of the Desikan-Killiany atlas for cortical area and also segmentation of subcortical regions. The model use sequential convolution for inference to overcome browser memory limitations but leads to longer computation time. " - } - - - ]; - - - //--For use with three.js - var outVolumeStatus = { out3DArr: null, totalVolume: 0, labelsHistoObj: null, colorLutObj: null, labelsObj: null}; - - //Heatmap colors, for reproduce: https://www.w3schools.com/colors/colors_hsl.asp - var manualColorsRange = [/*Red*/ "hsla(0,100%,50%)", /*Vermillion*/ "hsla(30,100%,50%)", /*Orange*/ "hsla(60,100%,50%)", - /*Amber*/ "hsla(90,100%,50%)", /*Yellow*/ "hsla(120,100%,50%)", /*Chartreuse*/ "hsla(150,100%,50%)", - /*Green*/ "hsla(180,100%,50%)", /*Aquamanrine*/ "hsla(210,100%,50%)", /*Blue*/ "hsla(240,100%,50%)", - /*Indiago*/ "hsla(270,100%,50%)", /*Purple*/ "hsla(300,100%,50%)", /*Magenta*/ "hsla(330,100%,50%)" - ] - - // For futur use - var manualColorsRangeHex = [ - "#330000", "#FF0000", "#FF3333", "#FF6666", - "#FF9999", "#FFB266", "#FFE5CC", "#FFFF66", - "#FFFFCC", "#FFFFFF", "#CCE5FF", "#99CCFF", - "#66B2FF", "#3399FF", "#0080FF", "#0066CC", "#004C99" - ] - - - - - //--https://github.com/rii-mango/Papaya/wiki/Configuration - // MRI Viewer settings - var params_mri = []; - params_mri["worldSpace"] = false; - params_mri["expandable"] = true; - // To hide the toolbar - params_mri["kioskMode"] = false; - params_mri["noNewFiles"] = true; - - papaya.Container.syncViewers = true; - - // Labels Viewer settings - var params_label = []; - params_label["worldSpace"] = false; - params_label["expandable"] = true; - // To hide the toolbar - params_label["kioskMode"] = false; - params_label["noNewFiles"] = true; - params_label["smoothDisplay"] = false; diff --git a/v3/js/brainchop/mainTypes.js b/v3/js/brainchop/mainTypes.js deleted file mode 100644 index fe364af..0000000 --- a/v3/js/brainchop/mainTypes.js +++ /dev/null @@ -1,214 +0,0 @@ -/* -========================================================= -* Brainchop - v2.0.1 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 38, 38, 38, 1] -* Model : Meshnet or similar -* -* Authors: Mohamed Masoud and Sergey Plis - 2022 -========================================================= - - - -========================================================= - Brainchop for 3D Brain Segmentation -=========================================================*/ - -/** -* Source: https://github.com/petamoriken/float16 -* -* @since 1.3.0 -* @example -* -* arr = new Float16Array([1.0, 1.1, 1.2, 1.3]) -* -* arr[1] -* // => 1.099 -* -* arr.byteLength -* // => 8 -* -* arr.buffer -* // => ArrayBuffer { byteLength: 8 } -* -* -* @example -* isFloat16Array(new Float16Array(buffer)); -* // => true -*/ - - const { - Float16Array, isFloat16Array, isTypedArray, - getFloat16, setFloat16, - hfround, - } = float16; - - - - - -/** -* Create buffer of type 'uint8'|'int8'|'uint16'|'int16'| 'float16' -* -* @since 1.3.0 -* @param {Array} shape e.g. [2, 3, 4, 5] // 2 batches, each batch has 3 slices, each slice has 4 rows, each row has 5 columns -* @param {string} dtype e.g. 'uint8' -* @param {Array} dataSync e.g. [1, 1, 0, 1] , must be 1D array -* @returns {TypedArray / tf.Tensor} Returns -* @example -* -* -* b = new Buffer( [2,2,2,2], 'uint8', Array.from({ length: 16 }, (v, i) => i) ) -* -* b.toTensor().print() -* // => Tensor -* [[[[0 , 1 ], -* [2 , 3 ]], -* -* [[4 , 5 ], -* [6 , 7 ]]], -* -* -* [[[8 , 9 ], -* [10, 11]], -* -* [[12, 13], -* [14, 15]]]] -* -* -* b = new Buffer( [2,2,2,2], 'float16', Array.from({ length: 16 }, (v, i) => i).map((value) => value * 0.01) ) -* // => Tensor -* [[[[0 , 0.0099945], -* [0.019989 , 0.0299988]], -* -* [[0.039978 , 0.0499878], -* [0.0599976, 0.0699463]]], -* -* -* [[[0.0799561, 0.0899658], -* [0.0999756, 0.1099854]], -* -* [[0.1199951, 0.1298828], -* [0.1398926, 0.1499023]]]] -* -*/ - - - - -class Buffer { - constructor(shape, dtype = 'uint8', dataSync = [] ) { - this.shape = shape; // buffer shape - - if(this.shape.length != 4) { - throw "Shape Must be 4D"; - } - - if( ! ( dtype === 'uint8' || dtype === 'int8' || dtype === 'uint16' || dtype === 'int16' || dtype === 'float16') ){ - throw "dtype not supported, must be 'uint8'|'int8'|'uint16'|'int16'| 'float16' "; - } - - if( ! Array.isArray(dataSync) ) { - throw "Data Must be array"; - } - - if( Array.isArray(dataSync[0]) ) { - throw "Data array Must be 1D"; - } - - this.numOfBatchs = this.shape[0]; // Total num of batches - this.numOfSlices = this.shape[1]; // Num of slices per batch - this.numOfRows = this.shape[2]; // Num of Rows per batch - this.numOfCols = this.shape[3]; // Num of cols per batch - - this.dtype = dtype; - - if (dtype === 'uint8') { - this.bytePerElement = Uint8Array.BYTES_PER_ELEMENT; - } else if (dtype === 'int8') { - this.bytePerElement = Int8Array.BYTES_PER_ELEMENT; - } else if (dtype === 'uint16') { - this.bytePerElement = Uint16Array.BYTES_PER_ELEMENT; - } else if (dtype === 'int16') { - this.bytePerElement = Int16Array.BYTES_PER_ELEMENT; - } else if (dtype === 'float16') { - this.bytePerElement = Float16Array.BYTES_PER_ELEMENT; - } - - this.bufferSizeBytes = this.shape[0] * this.shape[1] * this.shape[2] * this.shape[3] * this.bytePerElement; - this.bufferSizeGigaBytes = this.bufferSizeBytes/(1024*1024*1024); - // Strides - this.batchSize = this.numOfSlices * this.numOfRows * this.numOfCols; - this.sliceSize = this.numOfRows * this.numOfCols; - - - - if( dataSync.length == 0) { // Uint16Array: 0 to 65535, Uint8Array: 0 to 255 - - this.buffer = new ArrayBuffer( this.bufferSizeBytes ); - - if (dtype === 'uint8') { - this.typedArrObj = new Uint8Array(this.buffer); - } else if (dtype === 'int8') { - this.typedArrObj = new Int8Array(this.buffer); - } else if (dtype === 'uint16') { - this.typedArrObj = new Uint16Array(this.buffer); - } else if (dtype === 'int16') { - this.typedArrObj = new Int16Array(this.buffer); - } else if (dtype === 'float16') { - this.typedArrObj = new Float16Array(this.buffer); - } - - } else { - - if (dtype === 'uint8') { - this.typedArrObj = Uint8Array.from(dataSync); - } else if (dtype === 'int8') { - this.typedArrObj = Int8Array.from(dataSync); - } else if (dtype === 'uint16') { - this.typedArrObj = Uint16Array.from(dataSync); - } else if (dtype === 'int16') { - this.typedArrObj = Int16Array.from(dataSync); - } else if (dtype === 'float16') { - this.typedArrObj = new Float16Array(dataSync); - } - - } - - } - - - // batchIdx is index of batch, sliceIdx, rowIdx, colIdx for the batch Dim. - // function map 4D dim to dataSync 1D dim - getVoxelOffset = (batchIdx, sliceIdx, rowIdx, colIdx) => { - let batchOffset = batchIdx * this.batchSize; - - let sliceOffset = sliceIdx * this.sliceSize; - - let rowOffset = rowIdx * this.numOfCols; - - let offset = batchOffset + sliceOffset + rowOffset + colIdx; - - return offset; - - } - - set = (value, batchIdx, sliceIdx, rowIdx, colIdx) => { // N is number of batches, D,H,W are the batch Dim. - let offset = this.getVoxelOffset(batchIdx, sliceIdx, rowIdx, colIdx); - this.typedArrObj[offset] = value; - } - - - get = (batchIdx, sliceIdx, rowIdx, colIdx) => { // N is number of batches, D,H,W are the batch Dim. - let offset = this.getVoxelOffset(batchIdx, sliceIdx, rowIdx, colIdx); - return this.typedArrObj[offset]; - } - - - toTensor = () => { - return tf.tensor([...this.typedArrObj], this.shape); - } - - } \ No newline at end of file diff --git a/v3/js/brainchop/mri_convert.js b/v3/js/brainchop/mri_convert.js deleted file mode 100644 index c049e24..0000000 --- a/v3/js/brainchop/mri_convert.js +++ /dev/null @@ -1,924 +0,0 @@ -/* -========================================================= -* Brainchop - v2.0.1 -========================================================= - -* Discription: A user interface for whole brain segmentation -* Input shape : [1, D, H, W, 1] e.g. [1, 256, 256, 256, 1] -* Model : Meshnet or similar -* -* Authors: Mohamed Masoud and Sergey Plis - 2022 -* -* Credit: Martin Reuter (https://github.com/m-reuter) -* for originally coding conform.py in https://github.com/Deep-MI/FastSurfer -========================================================= - - - -========================================================= - mri_convert.js -=========================================================*/ -//--------------------------------------------------------------------------------// -//-- Inspired from NIFTI-Reader-JS https://github.com/rii-mango/NIFTI-Reader-JS--// -//------------ Credit: Michael Martinez https://github.com/martinezmj------------// -//--------------------------------------------------------------------------------// - - -//--https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h -/*! \struct nifti_1_header - \brief Data structure defining the fields in the nifti1 header. - This binary header should be found at the beginning of a valid - NIFTI-1 header file. - sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 - */ - /*************************/ /************************/ -//struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ - /*************************/ /************************/ - - /*--- was header_key substruct ---*/ -// int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ -// char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ -// char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ -// int extents; /*!< ++UNUSED++ */ /* int extents; */ -// short session_error; /*!< ++UNUSED++ */ /* short session_error; */ -// char regular; /*!< ++UNUSED++ */ /* char regular; */ -// char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ - - /*--- was image_dimension substruct ---*/ -// short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ -// float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ - /* short unused9; */ -// float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ - /* short unused11; */ -// float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ - /* short unused13; */ -// short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ -// short datatype; /*!< Defines data type! */ /* short datatype; */ -// short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ -// short slice_start; /*!< First slice index. */ /* short dim_un0; */ -// float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ -// float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ -// float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ -// float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ -// short slice_end; /*!< Last slice index. */ /* float funused3; */ -// char slice_code ; /*!< Slice timing order. */ -// char xyzt_units ; /*!< Units of pixdim[1..4] */ -// float cal_max; /*!< Max display intensity */ /* float cal_max; */ -// float cal_min; /*!< Min display intensity */ /* float cal_min; */ -// float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ -// float toffset; /*!< Time axis shift. */ /* float verified; */ -// int glmax; /*!< ++UNUSED++ */ /* int glmax; */ -// int glmin; /*!< ++UNUSED++ */ /* int glmin; */ - - /*--- was data_history substruct ---*/ -// char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ -// char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ - -// short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ -// short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ - /* are replaced */ -// float quatern_b ; /*!< Quaternion b param. */ -// float quatern_c ; /*!< Quaternion c param. */ -// float quatern_d ; /*!< Quaternion d param. */ -// float qoffset_x ; /*!< Quaternion x shift. */ -// float qoffset_y ; /*!< Quaternion y shift. */ -// float qoffset_z ; /*!< Quaternion z shift. */ - -// float srow_x[4] ; /*!< 1st row affine transform. */ -// float srow_y[4] ; /*!< 2nd row affine transform. */ -// float srow_z[4] ; /*!< 3rd row affine transform. */ - -// char intent_name[16];/*!< 'name' or meaning of data. */ - -// char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ - -//} ; /**** 348 bytes total ****/ - - - -/*---------------------------------------------------------------------------*/ -//--Source: https://nifti.nimh.nih.gov/pub/dist/doc/nifti2.h -/* Changes to the header from NIFTI-1 to NIFTI-2 are intended to allow for - larger and more accurate fields. The changes are as follows: - - - short dim[8] -> int64_t dim[8] - - float intent_p1,2,3 -> double intent_p1,2,3 (3 fields) - - float pixdim[8] -> double pixdim[8] - - float vox_offset -> int64_t vox_offset - - float scl_slope -> double scl_slope - - float scl_inter -> double scl_inter - - float cal_max -> double cal_max - - float cal_min -> double cal_min - - float slice_duration -> double slice_duration - - float toffset -> double toffset - - short slice_start -> int64_t slice_start - - short slice_end -> int64_t slice_end - - char slice_code -> int32_t slice_code - - char xyzt_units -> int32_t xyzt_units - - short intent_code -> int32_t intent_code - - short qform_code -> int32_t qform_code - - short sform_code -> int32_t sform_code - - float quatern_b,c,d -> double quatern_b,c,d (3 fields) - - float srow_x,y,z[4] -> double srow_x,y,z[4] (3 fields) - - char magic[4] -> char magic[8] - - char unused_str[15] -> padding added at the end of the header - - - previously unused fields have been removed: - data_type, db_name, extents, session_error, regular, glmax, glmin - - - the field ordering has been changed ------------------------------------------------------------------------------*/ - -//-- littleEndian : true or false - -setByteAt = (dataView, start, value, littleEndian) => { - dataView.setInt8(start, value, littleEndian); -}; - -setShortAt = (dataview, start, value, littleEndian) => { - dataView.setInt16(start, value, littleEndian); -}; - -setIntAt = (dataview, start, value, littleEndian) => { - dataView.setInt32(start, value, littleEndian); -}; - - -setFloatAt = (dataview, start, value, littleEndian) => { - dataView.setFloat32(start, value, littleEndian); -}; - - -setDoubleAt = (dataview, start, value, littleEndian) => { - dataView.setFloat64(start, value, littleEndian); -}; - - - - -/** - * modify Nifti-1 header data. - * @param {ArrayBuffer} ArrBuf e.g. rawNiftiData - * @param {object} newHdrObj e.g. {'dim_info': 0, 'dims': [..], ..} - * @return {ArrayBuffer} - */ - -modifyNifti1_Header = (ArrBuf, newHdrObj) => { - - dataView = new DataView(ArrBuf); - //--dataView: DataView { buffer: ArrayBuffer, byteLength: xx, byteOffset: 0 } - - let littleEndian = false; - - let magicCookieVal = nifti.Utils.getIntAt(dataView, 0, littleEndian); - - if (magicCookieVal !== nifti.NIFTI1.MAGIC_COOKIE) { // try as little endian - littleEndian = true; - magicCookieVal = nifti.Utils.getIntAt(dataView, 0, littleEndian); - } - - if (magicCookieVal !== nifti.NIFTI1.MAGIC_COOKIE) { - throw new Error("This does not appear to be a NIFTI file!"); - } - - setByteAt(dataView, 39, newHdrObj['dim_info'], littleEndian); - - for (let ctr = 0; ctr < 8; ctr += 1) { - let index = 40 + (ctr * 2); - setShortAt(dataView, index, newHdrObj.dims[ctr], littleEndian); - } - - - setShortAt(dataView, 68, newHdrObj['intent_code'], littleEndian); - - setShortAt(dataView, 70, newHdrObj['datatypeCode'], littleEndian); - - setShortAt(dataView, 72, newHdrObj['numBitsPerVoxel'], littleEndian); - - for (let ctr = 0; ctr < 8; ctr += 1) { - let index = 76 + (ctr * 4); - setFloatAt(dataView, index, newHdrObj.pixDims[ctr], littleEndian); - } - - - setByteAt(dataView, 123, newHdrObj['xyzt_units'], littleEndian); - - setShortAt(dataView, 252, newHdrObj['qform_code'], littleEndian); - setShortAt(dataView, 254, newHdrObj['sform_code'], littleEndian); - - setFloatAt(dataView, 256, newHdrObj['quatern_b'], littleEndian); - setFloatAt(dataView, 260, newHdrObj['quatern_c'], littleEndian); - setFloatAt(dataView, 264, newHdrObj['quatern_d'], littleEndian); - setFloatAt(dataView, 268, newHdrObj['qoffset_x'], littleEndian); - setFloatAt(dataView, 272, newHdrObj['qoffset_y'], littleEndian); - setFloatAt(dataView, 276, newHdrObj['qoffset_z'], littleEndian); - - for (let ctrOut = 0; ctrOut < 4; ctrOut += 1) { //<< --------- changed ctrOut to < 4 - for (let ctrIn = 0; ctrIn < 4; ctrIn += 1) { - let index = 280 + (((ctrOut * 4) + ctrIn) * 4); - setFloatAt(dataView, index, newHdrObj.affine[ctrOut][ctrIn], littleEndian); - } - } - - - return dataView.buffer; -}; - - - -/** - * modify Nifti-2 header data. - * @param {ArrayBuffer} ArrBuf e.g. rawNiftiData - * @param {object} newHdrObj e.g. {'dim_info': 0, 'dims': [..], ..} - * @return {ArrayBuffer} - */ - -modifyNifti2_Header = (ArrBuf, newHdrObj) => { - - dataView = new DataView(ArrBuf); - //--dataView: DataView { buffer: ArrayBuffer, byteLength: xx, byteOffset: 0 } - - let littleEndian = false; - - let magicCookieVal = nifti.Utils.getIntAt(dataView, 0, littleEndian); - - if (magicCookieVal !== nifti.NIFTI2.MAGIC_COOKIE) { // try as little endian - littleEndian = true; - magicCookieVal = nifti.Utils.getIntAt(dataView, 0, littleEndian); - } - - if (magicCookieVal !== nifti.NIFTI2.MAGIC_COOKIE) { - throw new Error("This does not appear to be a NIFTI file!"); - } - - setShortAt(dataView, 12, newHdrObj['datatypeCode'], littleEndian); - setShortAt(dataView, 14, newHdrObj['numBitsPerVoxel'], littleEndian); - - for (let ctr = 0; ctr < 8; ctr += 1) { - let index = 16 + (ctr * 8); - setLongAt(dataView, index, newHdrObj.dims[ctr], littleEndian); - } - - - for (let ctr = 0; ctr < 8; ctr += 1) { - let index = 104 + (ctr * 8); - setDoubleAt(dataView, index, newHdrObj.pixDims[ctr], littleEndian); - } - - - - setIntAt(dataView, 344, newHdrObj['qform_code'], littleEndian); - setIntAt(dataView, 348, newHdrObj['sform_code'], littleEndian); - - setDoubleAt(dataView, 352, newHdrObj['quatern_b'], littleEndian); - setDoubleAt(dataView, 360, newHdrObj['quatern_c'], littleEndian); - setDoubleAt(dataView, 368, newHdrObj['quatern_d'], littleEndian); - setDoubleAt(dataView, 376, newHdrObj['qoffset_x'], littleEndian); - setDoubleAt(dataView, 384, newHdrObj['qoffset_y'], littleEndian); - setDoubleAt(dataView, 392, newHdrObj['qoffset_z'], littleEndian); - - for (let ctrOut = 0; ctrOut < 4; ctrOut += 1) { //<< --------- changed ctrOut to < 4 - for (let ctrIn = 0; ctrIn < 4; ctrIn += 1) { - let index = 400 + (((ctrOut * 4) + ctrIn) * 8); - setDoubleAt(dataView, index, newHdrObj.affine[ctrOut][ctrIn], littleEndian); - } - } - - - setIntAt(dataView, 500, newHdrObj['xyzt_units'], littleEndian); - - setIntAt(dataView, 504, newHdrObj['intent_code'], littleEndian); - - setByteAt(dataView, 524, newHdrObj['dim_info'], littleEndian); - - - return dataView.buffer; -}; - - -/** - * mri convert function to resample and normalize the input nifti file - * @param {String} fileUrl : link to file of type : "application/octet-binary;charset=utf-8" - * @param {ArrayBuffer} rawNiftiFile - * @param {String} fileName - * @return {ArrayBuffer} - */ - - -async function mri_convert(fileUrl, rawNiftiFile, fileName) { - - //-- To be accessed from python using js from import js, it should be global - mriTempUrl = fileUrl; - - niftiVer = nifti.isNIFTI1(rawNiftiFile) ? 1 : nifti.isNIFTI2(rawNiftiFile) ? 2 : null ; - - if(niftiVer == null) { - webix.alert("The file is not Nifti or corrupted "); - return 0; - } - - - if (!fileName.endsWith('.nii') && !fileName.endsWith('.nii.gz')) { - webix.alert('Please select Nifti file *.nii or *.nii.gz '); - return 0; - } else { - niftiFileExtension = fileName.endsWith('.nii') ? '.nii' : '.nii.gz'; - console.log("Nifti File Extension :", niftiFileExtension); - } - - - - try { - pyodide = await loadPyodide(); - - } catch (err) { - console.log(err); - } - - console.log("load pyodide") - - $$("mriConvPrgBarWin").show(); - - document.getElementById("mriConvertProgBar").style.width= "10%"; - - await pyodide.loadPackage("micropip"); - await pyodide.loadPackage("scipy"); - await pyodide.loadPackage("matplotlib"); - - document.getElementById("mriConvertProgBar").style.width= "50%"; - - await pyodide.runPythonAsync(` - - import micropip - - await micropip.install('nibabel') - import nibabel - import nibabel.processing - - import js - from js import document, Blob, window, fetch, File - - import sys - import os - import numpy - import scipy - from scipy.ndimage import affine_transform - from numpy.linalg import inv - from pyodide.http import pyfetch - from nibabel.freesurfer.mghformat import MGHHeader - - - - #--Source: https://github.com/Deep-MI/FastSurfer/blob/stable/FastSurferCNN/data_loader/conform.py - - def getscale(data, dst_min, dst_max, f_low=0.0, f_high=0.999): - """ - Original Author: Martin Reuter (https://github.com/m-reuter) - Function to get offset and scale of image intensities to robustly rescale to range dst_min..dst_max. - Equivalent to how mri_convert conforms images. - :param numpy.ndarray data: image data (intensity values) - :param float dst_min: future minimal intensity value - :param float dst_max: future maximal intensity value - :param f_low: robust cropping at low end (0.0 no cropping) - :param f_high: robust cropping at higher end (0.999 crop one thousandths of high intensity voxels) - :return: float src_min: (adjusted) offset - :return: float scale: scale factor - """ - # get min and max from source - src_min = numpy.min(data) - src_max = numpy.max(data) - - if src_min < 0.0 : - if src_max <= 0.0 : - src_max = 255 - - print("ERROR: Min value in input is below 0.0!, Brainchop will linearly interpolate the intensity range from 0 to max value") - numpy.interp(data, (src_min, src_max), (0, src_max)) - # js_system_exit("ERROR: Min value in input is below 0.0!") - # sys.exit('ERROR: Min value in input is below 0.0!') - - print("Input: min: " + format(src_min) + " max: " + format(src_max)) - - if f_low == 0.0 and f_high == 1.0: - return src_min, 1.0 - - # compute non-zeros and total vox num - nz = (numpy.abs(data) >= 1e-15).sum() - voxnum = data.shape[0] * data.shape[1] * data.shape[2] - - # compute histogram - histosize = 1000 - bin_size = (src_max - src_min) / histosize - hist, bin_edges = numpy.histogram(data, histosize) - - # compute cummulative sum - cs = numpy.concatenate(([0], numpy.cumsum(hist))) - - # get lower limit - nth = int(f_low * voxnum) - idx = numpy.where(cs < nth) - - if len(idx[0]) > 0: - idx = idx[0][-1] + 1 - - else: - idx = 0 - - src_min = idx * bin_size + src_min - - # get upper limit - nth = voxnum - int((1.0 - f_high) * nz) - idx = numpy.where(cs >= nth) - - if len(idx[0]) > 0: - idx = idx[0][0] - 2 - - else: - print('ERROR: rescale upper bound not found') - - src_max = idx * bin_size + src_min - - # scale - if src_min == src_max: - scale = 1.0 - - else: - scale = (dst_max - dst_min) / (src_max - src_min) - - print("rescale: min: " + format(src_min) + " max: " + format(src_max) + " scale: " + format(scale)) - - return src_min, scale - - - def map_image(img, out_affine, out_shape, ras2ras=numpy.array([[1.0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]), - order=1): - """ - Original Author: Martin Reuter (https://github.com/m-reuter) - Function to map image to new voxel space (RAS orientation) - :param nibabel.MGHImage img: the src 3D image with data and affine set - :param numpy.ndarray out_affine: trg image affine - :param numpy.ndarray out_shape: the trg shape information - :param numpy.ndarray ras2ras: ras2ras an additional maping that should be applied (default=id to just reslice) - :param int order: order of interpolation (0=nearest,1=linear(default),2=quadratic,3=cubic) - :return: numpy.ndarray new_data: mapped image data array - """ - - # compute vox2vox from src to trg - vox2vox = inv(out_affine) @ ras2ras @ img.affine - - # here we apply the inverse vox2vox (to pull back the src info to the target image) - image_data = numpy.asanyarray(img.dataobj) - - # convert frames to single image - if len(image_data.shape) > 3: - if any(s != 1 for s in image_data.shape[3:]): - raise ValueError(f'Multiple input frames {tuple(image_data.shape)} not supported!') - image_data = numpy.squeeze(image_data, axis=tuple(range(3,len(image_data.shape)))) - - new_data = affine_transform(image_data, inv(vox2vox), output_shape=out_shape, order=order) - - return new_data - - - def scalecrop(data, dst_min, dst_max, src_min, scale): - """ - Original Author: Martin Reuter (https://github.com/m-reuter) - Function to crop the intensity ranges to specific min and max values - :param numpy.ndarray data: Image data (intensity values) - :param float dst_min: future minimal intensity value - :param float dst_max: future maximal intensity value - :param float src_min: minimal value to consider from source (crops below) - :param float scale: scale value by which source will be shifted - :return: numpy.ndarray data_new: scaled image data - """ - data_new = dst_min + scale * (data - src_min) - - # clip - data_new = numpy.clip(data_new, dst_min, dst_max) - print("Output: min: " + format(data_new.min()) + " max: " + format(data_new.max())) - - return data_new - - - - - def conform(img, order=1): - """ - Original Author: Martin Reuter (https://github.com/m-reuter) - Python version of mri_convert -c, which turns image intensity values into UCHAR, reslices images to standard position, fills up - slices to standard 256x256x256 format and enforces 1 mm isotropic voxel sizes. - Difference to mri_convert -c is that we first interpolate (float image), and then rescale to uchar. mri_convert is - doing it the other way. However, we compute the scale factor from the input to be more similar again - :param nibabel.MGHImage img: loaded source image - :param int order: interpolation order (0=nearest,1=linear(default),2=quadratic,3=cubic) - :return: nibabel.MGHImage new_img: conformed image - """ - - cwidth = 256 - csize = 1 - h1 = MGHHeader.from_header(img.header) # may copy some parameters if input was MGH format - - h1.set_data_shape([cwidth, cwidth, cwidth, 1]) - h1.set_zooms([csize, csize, csize]) - h1['Mdc'] = [[-1, 0, 0], [0, 0, -1], [0, 1, 0]] - h1['fov'] = cwidth - h1['Pxyz_c'] = img.affine.dot(numpy.hstack((numpy.array(img.shape[:3]) / 2.0, [1])))[:3] - - # from_header does not compute Pxyz_c (and probably others) when importing from nii - # Pxyz is the center of the image in world coords - - # get scale for conversion on original input before mapping to be more similar to mri_convert - src_min, scale = getscale(numpy.asanyarray(img.dataobj), 0, 255) - - mapped_data = map_image(img, h1.get_affine(), h1.get_data_shape(), order=order) - # print("max: "+format(numpy.max(mapped_data))) - - if not img.get_data_dtype() == numpy.dtype(numpy.uint8): - - if numpy.max(mapped_data) > 255: - mapped_data = scalecrop(mapped_data, 0, 255, src_min, scale) - - new_data = numpy.uint8(numpy.rint(mapped_data)) - new_img = nibabel.MGHImage(new_data, h1.get_affine(), h1) - - # make sure we store uchar - new_img.set_data_dtype(numpy.uint8) - - return new_img - - - def check_affine_in_nifti(img, logger=None): - """ - Original Author: Martin Reuter (https://github.com/m-reuter) - Function to check affine in nifti Image. Sets affine with qform if it exists and differs from sform. - If qform does not exist, voxelsizes between header information and information in affine are compared. - In case these do not match, the function returns False (otherwise returns True. - :param nibabel.NiftiImage img: loaded nifti-image - :return bool: True, if: affine was reset to qform - voxelsizes in affine are equivalent to voxelsizes in header - False, if: voxelsizes in affine and header differ - """ - check = True - message = "" - - if img.header['qform_code'] != 0 and numpy.max(numpy.abs(img.get_sform() - img.get_qform())) > 0.001: - message = "WARNING: qform and sform transform are not identical! Affine from qform transform will now be used, sform-transform:{} qform-transform:{}".format(img.header.get_sform(), img.header.get_qform()) - - # Set sform with qform affine and update best affine in header - img.set_sform(img.get_qform()) - img.update_header() - - else: - # Check if affine correctly includes voxel information and print Warning/Exit otherwise - vox_size_head = img.header.get_zooms() - aff = img.affine - xsize = numpy.sqrt(aff[0][0] * aff[0][0] + aff[1][0] * aff[1][0] + aff[2][0] * aff[2][0]) - ysize = numpy.sqrt(aff[0][1] * aff[0][1] + aff[1][1] * aff[1][1] + aff[2][1] * aff[2][1]) - zsize = numpy.sqrt(aff[0][2] * aff[0][2] + aff[1][2] * aff[1][2] + aff[2][2] * aff[2][2]) - - if (abs(xsize - vox_size_head[0]) > .001) or (abs(ysize - vox_size_head[1]) > .001) or (abs(zsize - vox_size_head[2]) > 0.001): - message = "ERROR: Invalid Nifti-header! Affine matrix is inconsistent with Voxel sizes. Voxel size (from header) vs. Voxel size in affine: ({}, {}, {}), ({}, {}, {})Input Affine----------------{} ".format(vox_size_head[0], vox_size_head[1], vox_size_head[2], xsize, ysize, zsize, aff) - - check = False - - if logger is not None: - logger.info(message) - - else: - print(message) - - return check - - #--------------------------------------------------# - - async def js_fetch(url): - response = await fetch(url) - js_arr_buffer = await response.arrayBuffer() - return js_arr_buffer - - def preprocess_image(img): - """Unit interval preprocessing""" - img = (img - img.min()) / (img.max() - img.min()) - return img - - def enhance_contrast(image_matrix, bins=256): - """Source: https://medium.com/analytics-vidhya/image-equalization-contrast-enhancing-in-python-82600d3b371c""" - - image_flattened = image_matrix.flatten() - image_hist = numpy.zeros(bins) - - # frequency count of each pixel - for pix in image_flattened: - image_hist[pix] += 1 - - # cummulative sum - cum_sum = numpy.cumsum(image_hist) - norm = (cum_sum - cum_sum.min()) * 255 - # normalization of the pixel values - n_ = cum_sum.max() - cum_sum.min() - uniform_norm = norm / n_ - uniform_norm = uniform_norm.astype('int') - - # flat histogram - image_eq = uniform_norm[image_flattened] - # reshaping the flattened matrix to its original shape - image_eq = numpy.reshape(a=image_eq, newshape=image_matrix.shape) - - return image_eq - - def resolve_xform_code( xform_code ): - - if xform_code == "unknown" : - return 0 - if xform_code == "scanner" : - return 1 - if xform_code == "aligned" : - return 2 - if xform_code == "mni" : - return 4 - else: - print("Error: Undefined xform code") - return 0 # << --- Need check - - ## other transform codes - # TALAIRACH = 3; - # MNI_152 = 4; - - - def resolve_datatype_code( datatype_code ): - """ - For future use... - """ - - if datatype_code == "none" or datatype_code == "unknown" : - print("Error unknown datatype ") - return 0 # << --- Need check - if datatype_code == "binary" : - return 1 - if datatype_code == "uint8" : - return 2 - if datatype_code == "int16" : - return 4 - if datatype_code == "int32" : - return 8 - if datatype_code == "float32" : - return 16 - if datatype_code == "complex64" : - return 32 - if datatype_code == "float64" : - return 64 - if datatype_code == "int8" : - return 256 - if datatype_code == "uint16" : - return 512 - if datatype_code == "uint32" : - return 768 - if datatype_code == "uint64" : - return 1280 - - else: - print("Error: Undefined datatype code") - return 0 # << --- Need check - - - def resolve_bitpix_code( datatype_code ): - """ - For future use... - """ - - if datatype_code == "none" or datatype_code == "unknown" : - print("Error unknown datatype ") - return 0 # << --- Need check - if datatype_code == "binary" : - return 1 - if datatype_code == "uint8" : - return 8 - if datatype_code == "int16" : - return 16 - if datatype_code == "int32" : - return 32 - if datatype_code == "float32" : - return 32 - if datatype_code == "complex64" : - return 64 - if datatype_code == "float64" : - return 64 - if datatype_code == "int8" : - return 8 - if datatype_code == "uint16" : - return 16 - if datatype_code == "uint32" : - return 32 - if datatype_code == "uint64" : - return 64 - else: - print("Error: Undefined datatype code") - return 0 # << --- Need check - - def js_system_exit(msg): - """ - Close convertion progress bar due to error - """ - document.getElementById("mriConvertProgBar").style.width= "0%" - js.webix.alert(msg) - js.closeMriConvPrgBarWin(); - - #--------------------main------------------------# - print("mriTempUrl: " + format(js.mriTempUrl)) - - document.getElementById("mriConvertProgBar").style.width= "75%" - - # Fetch original MRI file from JS - response = await pyfetch(js.mriTempUrl) - - # Get temp Nifti file name with extension nii or nii.gz - inputFileName = "inputFile" + js.niftiFileExtension - print("inputFileName: " + format(inputFileName)) - - if response.status == 200: - with open(inputFileName, "wb") as f: - f.write(await response.bytes()) - - try: - input_NIFTIimg = nibabel.load(inputFileName) - - except: - js_system_exit("File can not be converted.") - sys.exit('ERROR: File can not be read') - - # Input Nifti header - print("Original MRI shape: " + format(input_NIFTIimg.header)) - - # Input Nifti shape - print("Original MRI shape: " + format(input_NIFTIimg.shape)) - - - # Input image Orientation e.g. LIA , RAS or RPS - print("Input Nifti Orientation : " + format(nibabel.aff2axcodes(input_NIFTIimg.affine))) - - # Check if MRI 3D or file contains multiple MRIs - if len(input_NIFTIimg.shape) > 3 and input_NIFTIimg.shape[3] != 1: - js_system_exit("4D MRI is not supported.") - sys.exit('ERROR: Multiple input frames (' + format(input_NIFTIimg.shape[3]) + ') not supported!') - - - if not check_affine_in_nifti(input_NIFTIimg): - js_system_exit("ERROR: inconsistency in nifti-header") - sys.exit("ERROR: inconsistency in nifti-header. Exiting now.") - - print ("Generating conformed MRI ... ") - new_MGHimage = conform(input_NIFTIimg) - - # Test conformed MRI Orientation e.g. LIA or RAS - print("New conformed MRI Orientation : " + format(nibabel.aff2axcodes(new_MGHimage.affine))) - - # Output MRI shape - print("New conformed MRI shape: " + format(new_MGHimage.shape)) - - # save to Nifti - nibabel.save (new_MGHimage, "outNifti.nii") - - # load as NiftiImage - out_NIFTIimg = nibabel.load("outNifti.nii") - - # Test Output MRI Orientation e.g. LIA or RAS - print("Generated NIFTI MRI Orientation : " + format(nibabel.aff2axcodes(out_NIFTIimg.affine))) - - - - - # Globals - #-- data = enhance_contrast(numpy.array(out_NIFTIimg.get_fdata(), dtype=numpy.uint8).T) - print("Disable enahance_contrast.") - data = numpy.array(out_NIFTIimg.get_fdata(), dtype=numpy.uint8).T - hdr = out_NIFTIimg.header - print("Output NIFTI header : " + format(hdr)) - - - # -- For future use - #-- print(int(resolve_xform_code(hdr.structarr['qform_code']))) - #-- print(int(resolve_xform_code(hdr.structarr['sform_code']))) - #-- hdr.structarr['vox_offset'] = 352 - - dim_info = int(hdr.structarr['dim_info']) - dims = hdr.structarr['dim'] - intent_code = int(hdr.structarr['intent_code']) - datatypeCode = 2 - numBitsPerVoxel = 8 - pixDims = hdr.structarr['pixdim'] - xyzt_units = int(hdr.structarr['xyzt_units']) - qform_code = 0 # set by conform.py new hdr - sform_code = 2 # set by conform.py net hdr - quatern_b = float(hdr.structarr['quatern_b']) - quatern_c = float(hdr.structarr['quatern_c']) - quatern_d = float(hdr.structarr['quatern_d']) - qoffset_x = float(hdr.structarr['qoffset_x']) - qoffset_y = float(hdr.structarr['qoffset_y']) - qoffset_z = float(hdr.structarr['qoffset_z']) - affine = out_NIFTIimg.affine #--should be float type - - document.getElementById("mriConvertProgBar").style.width= "95%" - print("----------MRI Convert: Done ------------") - - `); - - - //-----------------New Nifti Data ------------------// - - - console.log("Get New Nifti Data") - let js_data = pyodide.globals.get('data').toJs() - - //-- To avoid possible mem leak - pyodide.globals.get('data').destroy(); - - console.log("Convert data array to tensor") - let data3DTensor = array2Tensor(js_data) - let dataArr1D = tensor2FlattenArray( data3DTensor ); - - - //-----------------New Nifti header ------------------// - let js_dim_info = pyodide.globals.get('dim_info'); - //-- dims is array - let js_dims = pyodide.globals.get('dims').toJs(); - let js_intent_code = pyodide.globals.get('intent_code'); - let js_datatypeCode = pyodide.globals.get('datatypeCode'); - let js_numBitsPerVoxel = pyodide.globals.get('numBitsPerVoxel'); - //-- pixDims is array - let js_pixDims = pyodide.globals.get('pixDims').toJs(); - let js_xyzt_units = pyodide.globals.get('xyzt_units'); - let js_qform_code = pyodide.globals.get('qform_code'); - let js_sform_code = pyodide.globals.get('sform_code'); - let js_quatern_b = pyodide.globals.get('quatern_b'); - let js_quatern_c = pyodide.globals.get('quatern_c'); - let js_quatern_d = pyodide.globals.get('quatern_d'); - let js_qoffset_x = pyodide.globals.get('qoffset_x'); - let js_qoffset_y = pyodide.globals.get('qoffset_y'); - let js_qoffset_z = pyodide.globals.get('qoffset_z'); - //-- affine is array - let js_affine = pyodide.globals.get('affine').toJs(); - - //-- To avoid possible mem leak - pyodide.globals.get('dims').destroy(); - pyodide.globals.get('pixDims').destroy(); - pyodide.globals.get('affine').destroy(); - - - let new_hdr_values = { 'dim_info': js_dim_info, - 'dims': js_dims, - 'intent_code': js_intent_code, - 'datatypeCode': js_datatypeCode, - 'numBitsPerVoxel': js_numBitsPerVoxel, - 'pixDims': js_pixDims, - 'xyzt_units': js_xyzt_units, - 'qform_code': js_qform_code, - 'sform_code': js_sform_code, - 'quatern_b': js_quatern_b, - 'quatern_c': js_quatern_c, - 'quatern_d': js_quatern_d, - 'qoffset_x': js_qoffset_x, - 'qoffset_y': js_qoffset_y, - 'qoffset_z': js_qoffset_z, - 'affine': js_affine - } - - console.log("new_hdr_values :", new_hdr_values) - - - if(niftiVer == 1) { - rawNiftiFile = modifyNifti1_Header(rawNiftiFile, new_hdr_values); - - } else { - rawNiftiFile = modifyNifti2_Header(rawNiftiFile, new_hdr_values); - } - - - rawNiftiFile = createNiftiOutArrayBuffer(rawNiftiFile, new Uint8Array(dataArr1D)); - - - document.getElementById("mriConvertProgBar").style.width= "0%"; - - $$("mriConvPrgBarWin").hide(); - - webix.confirm({ - title:"", - ok:"Save", - cancel:"Cancel", - text: "Saving a copy of input MRI file after resampled ?", - width:500 - }) - .then(() => { - //--- - downloadNifti(dataArr1D, rawNiftiFile, "converted_" + refFileName); - - }) - .fail(() => { - - }); - - - return rawNiftiFile; - - - -} //-- End of mri_convert function diff --git a/v3/js/libs/d3.js b/v3/js/libs/d3.js deleted file mode 100644 index 66d7df9..0000000 --- a/v3/js/libs/d3.js +++ /dev/null @@ -1,16313 +0,0 @@ -// https://d3js.org Version 4.2.8. Copyright 2016 Mike Bostock. -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.d3 = global.d3 || {}))); -}(this, (function (exports) { 'use strict'; - -var version = "4.2.8"; - -var ascending = function(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -}; - -var bisector = function(compare) { - if (compare.length === 1) compare = ascendingComparator(compare); - return { - left: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; - else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; - else lo = mid + 1; - } - return lo; - } - }; -}; - -function ascendingComparator(f) { - return function(d, x) { - return ascending(f(d), x); - }; -} - -var ascendingBisect = bisector(ascending); -var bisectRight = ascendingBisect.right; -var bisectLeft = ascendingBisect.left; - -var descending = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; -}; - -var number = function(x) { - return x === null ? NaN : +x; -}; - -var variance = function(array, f) { - var n = array.length, - m = 0, - a, - d, - s = 0, - i = -1, - j = 0; - - if (f == null) { - while (++i < n) { - if (!isNaN(a = number(array[i]))) { - d = a - m; - m += d / ++j; - s += d * (a - m); - } - } - } - - else { - while (++i < n) { - if (!isNaN(a = number(f(array[i], i, array)))) { - d = a - m; - m += d / ++j; - s += d * (a - m); - } - } - } - - if (j > 1) return s / (j - 1); -}; - -var deviation = function(array, f) { - var v = variance(array, f); - return v ? Math.sqrt(v) : v; -}; - -var extent = function(array, f) { - var i = -1, - n = array.length, - a, - b, - c; - - if (f == null) { - while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; } - while (++i < n) if ((b = array[i]) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - - else { - while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = c = b; break; } - while (++i < n) if ((b = f(array[i], i, array)) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - - return [a, c]; -}; - -var array = Array.prototype; - -var slice = array.slice; -var map = array.map; - -var constant$1 = function(x) { - return function() { - return x; - }; -}; - -var identity = function(x) { - return x; -}; - -var range = function(start, stop, step) { - start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; - - var i = -1, - n = Math.max(0, Math.ceil((stop - start) / step)) | 0, - range = new Array(n); - - while (++i < n) { - range[i] = start + i * step; - } - - return range; -}; - -var e10 = Math.sqrt(50); -var e5 = Math.sqrt(10); -var e2 = Math.sqrt(2); - -var ticks = function(start, stop, count) { - var step = tickStep(start, stop, count); - return range( - Math.ceil(start / step) * step, - Math.floor(stop / step) * step + step / 2, // inclusive - step - ); -}; - -function tickStep(start, stop, count) { - var step0 = Math.abs(stop - start) / Math.max(0, count), - step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), - error = step0 / step1; - if (error >= e10) step1 *= 10; - else if (error >= e5) step1 *= 5; - else if (error >= e2) step1 *= 2; - return stop < start ? -step1 : step1; -} - -var sturges = function(values) { - return Math.ceil(Math.log(values.length) / Math.LN2) + 1; -}; - -var histogram = function() { - var value = identity, - domain = extent, - threshold = sturges; - - function histogram(data) { - var i, - n = data.length, - x, - values = new Array(n); - - for (i = 0; i < n; ++i) { - values[i] = value(data[i], i, data); - } - - var xz = domain(values), - x0 = xz[0], - x1 = xz[1], - tz = threshold(values, x0, x1); - - // Convert number of thresholds into uniform thresholds. - if (!Array.isArray(tz)) tz = ticks(x0, x1, tz); - - // Remove any thresholds outside the domain. - var m = tz.length; - while (tz[0] <= x0) tz.shift(), --m; - while (tz[m - 1] >= x1) tz.pop(), --m; - - var bins = new Array(m + 1), - bin; - - // Initialize bins. - for (i = 0; i <= m; ++i) { - bin = bins[i] = []; - bin.x0 = i > 0 ? tz[i - 1] : x0; - bin.x1 = i < m ? tz[i] : x1; - } - - // Assign data to bins by value, ignoring any outside the domain. - for (i = 0; i < n; ++i) { - x = values[i]; - if (x0 <= x && x <= x1) { - bins[bisectRight(tz, x, 0, m)].push(data[i]); - } - } - - return bins; - } - - histogram.value = function(_) { - return arguments.length ? (value = typeof _ === "function" ? _ : constant$1(_), histogram) : value; - }; - - histogram.domain = function(_) { - return arguments.length ? (domain = typeof _ === "function" ? _ : constant$1([_[0], _[1]]), histogram) : domain; - }; - - histogram.thresholds = function(_) { - return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant$1(slice.call(_)) : constant$1(_), histogram) : threshold; - }; - - return histogram; -}; - -var threshold = function(array, p, f) { - if (f == null) f = number; - if (!(n = array.length)) return; - if ((p = +p) <= 0 || n < 2) return +f(array[0], 0, array); - if (p >= 1) return +f(array[n - 1], n - 1, array); - var n, - h = (n - 1) * p, - i = Math.floor(h), - a = +f(array[i], i, array), - b = +f(array[i + 1], i + 1, array); - return a + (b - a) * (h - i); -}; - -var freedmanDiaconis = function(values, min, max) { - values = map.call(values, number).sort(ascending); - return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3))); -}; - -var scott = function(values, min, max) { - return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3))); -}; - -var max = function(array, f) { - var i = -1, - n = array.length, - a, - b; - - if (f == null) { - while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; } - while (++i < n) if ((b = array[i]) != null && b > a) a = b; - } - - else { - while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = b; break; } - while (++i < n) if ((b = f(array[i], i, array)) != null && b > a) a = b; - } - - return a; -}; - -var mean = function(array, f) { - var s = 0, - n = array.length, - a, - i = -1, - j = n; - - if (f == null) { - while (++i < n) if (!isNaN(a = number(array[i]))) s += a; else --j; - } - - else { - while (++i < n) if (!isNaN(a = number(f(array[i], i, array)))) s += a; else --j; - } - - if (j) return s / j; -}; - -var median = function(array, f) { - var numbers = [], - n = array.length, - a, - i = -1; - - if (f == null) { - while (++i < n) if (!isNaN(a = number(array[i]))) numbers.push(a); - } - - else { - while (++i < n) if (!isNaN(a = number(f(array[i], i, array)))) numbers.push(a); - } - - return threshold(numbers.sort(ascending), 0.5); -}; - -var merge = function(arrays) { - var n = arrays.length, - m, - i = -1, - j = 0, - merged, - array; - - while (++i < n) j += arrays[i].length; - merged = new Array(j); - - while (--n >= 0) { - array = arrays[n]; - m = array.length; - while (--m >= 0) { - merged[--j] = array[m]; - } - } - - return merged; -}; - -var min = function(array, f) { - var i = -1, - n = array.length, - a, - b; - - if (f == null) { - while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; } - while (++i < n) if ((b = array[i]) != null && a > b) a = b; - } - - else { - while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = b; break; } - while (++i < n) if ((b = f(array[i], i, array)) != null && a > b) a = b; - } - - return a; -}; - -var pairs = function(array) { - var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n); - while (i < n) pairs[i] = [p, p = array[++i]]; - return pairs; -}; - -var permute = function(array, indexes) { - var i = indexes.length, permutes = new Array(i); - while (i--) permutes[i] = array[indexes[i]]; - return permutes; -}; - -var scan = function(array, compare) { - if (!(n = array.length)) return; - var i = 0, - n, - j = 0, - xi, - xj = array[j]; - - if (!compare) compare = ascending; - - while (++i < n) if (compare(xi = array[i], xj) < 0 || compare(xj, xj) !== 0) xj = xi, j = i; - - if (compare(xj, xj) === 0) return j; -}; - -var shuffle = function(array, i0, i1) { - var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0), - t, - i; - - while (m) { - i = Math.random() * m-- | 0; - t = array[m + i0]; - array[m + i0] = array[i + i0]; - array[i + i0] = t; - } - - return array; -}; - -var sum = function(array, f) { - var s = 0, - n = array.length, - a, - i = -1; - - if (f == null) { - while (++i < n) if (a = +array[i]) s += a; // Note: zero and null are equivalent. - } - - else { - while (++i < n) if (a = +f(array[i], i, array)) s += a; - } - - return s; -}; - -var transpose = function(matrix) { - if (!(n = matrix.length)) return []; - for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) { - for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) { - row[j] = matrix[j][i]; - } - } - return transpose; -}; - -function length(d) { - return d.length; -} - -var zip = function() { - return transpose(arguments); -}; - -var prefix = "$"; - -function Map() {} - -Map.prototype = map$1.prototype = { - constructor: Map, - has: function(key) { - return (prefix + key) in this; - }, - get: function(key) { - return this[prefix + key]; - }, - set: function(key, value) { - this[prefix + key] = value; - return this; - }, - remove: function(key) { - var property = prefix + key; - return property in this && delete this[property]; - }, - clear: function() { - for (var property in this) if (property[0] === prefix) delete this[property]; - }, - keys: function() { - var keys = []; - for (var property in this) if (property[0] === prefix) keys.push(property.slice(1)); - return keys; - }, - values: function() { - var values = []; - for (var property in this) if (property[0] === prefix) values.push(this[property]); - return values; - }, - entries: function() { - var entries = []; - for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]}); - return entries; - }, - size: function() { - var size = 0; - for (var property in this) if (property[0] === prefix) ++size; - return size; - }, - empty: function() { - for (var property in this) if (property[0] === prefix) return false; - return true; - }, - each: function(f) { - for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this); - } -}; - -function map$1(object, f) { - var map = new Map; - - // Copy constructor. - if (object instanceof Map) object.each(function(value, key) { map.set(key, value); }); - - // Index array by numeric index or specified key function. - else if (Array.isArray(object)) { - var i = -1, - n = object.length, - o; - - if (f == null) while (++i < n) map.set(i, object[i]); - else while (++i < n) map.set(f(o = object[i], i, object), o); - } - - // Convert object to map. - else if (object) for (var key in object) map.set(key, object[key]); - - return map; -} - -var nest = function() { - var keys = [], - sortKeys = [], - sortValues, - rollup, - nest; - - function apply(array, depth, createResult, setResult) { - if (depth >= keys.length) return rollup != null - ? rollup(array) : (sortValues != null - ? array.sort(sortValues) - : array); - - var i = -1, - n = array.length, - key = keys[depth++], - keyValue, - value, - valuesByKey = map$1(), - values, - result = createResult(); - - while (++i < n) { - if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) { - values.push(value); - } else { - valuesByKey.set(keyValue, [value]); - } - } - - valuesByKey.each(function(values, key) { - setResult(result, key, apply(values, depth, createResult, setResult)); - }); - - return result; - } - - function entries(map, depth) { - if (++depth > keys.length) return map; - var array, sortKey = sortKeys[depth - 1]; - if (rollup != null && depth >= keys.length) array = map.entries(); - else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); }); - return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array; - } - - return nest = { - object: function(array) { return apply(array, 0, createObject, setObject); }, - map: function(array) { return apply(array, 0, createMap, setMap); }, - entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); }, - key: function(d) { keys.push(d); return nest; }, - sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; }, - sortValues: function(order) { sortValues = order; return nest; }, - rollup: function(f) { rollup = f; return nest; } - }; -}; - -function createObject() { - return {}; -} - -function setObject(object, key, value) { - object[key] = value; -} - -function createMap() { - return map$1(); -} - -function setMap(map, key, value) { - map.set(key, value); -} - -function Set() {} - -var proto = map$1.prototype; - -Set.prototype = set.prototype = { - constructor: Set, - has: proto.has, - add: function(value) { - value += ""; - this[prefix + value] = value; - return this; - }, - remove: proto.remove, - clear: proto.clear, - values: proto.keys, - size: proto.size, - empty: proto.empty, - each: proto.each -}; - -function set(object, f) { - var set = new Set; - - // Copy constructor. - if (object instanceof Set) object.each(function(value) { set.add(value); }); - - // Otherwise, assume it’s an array. - else if (object) { - var i = -1, n = object.length; - if (f == null) while (++i < n) set.add(object[i]); - else while (++i < n) set.add(f(object[i], i, object)); - } - - return set; -} - -var keys = function(map) { - var keys = []; - for (var key in map) keys.push(key); - return keys; -}; - -var values = function(map) { - var values = []; - for (var key in map) values.push(map[key]); - return values; -}; - -var entries = function(map) { - var entries = []; - for (var key in map) entries.push({key: key, value: map[key]}); - return entries; -}; - -var uniform = function(min, max) { - min = min == null ? 0 : +min; - max = max == null ? 1 : +max; - if (arguments.length === 1) max = min, min = 0; - else max -= min; - return function() { - return Math.random() * max + min; - }; -}; - -var normal = function(mu, sigma) { - var x, r; - mu = mu == null ? 0 : +mu; - sigma = sigma == null ? 1 : +sigma; - return function() { - var y; - - // If available, use the second previously-generated uniform random. - if (x != null) y = x, x = null; - - // Otherwise, generate a new x and y. - else do { - x = Math.random() * 2 - 1; - y = Math.random() * 2 - 1; - r = x * x + y * y; - } while (!r || r > 1); - - return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r); - }; -}; - -var logNormal = function() { - var randomNormal = normal.apply(this, arguments); - return function() { - return Math.exp(randomNormal()); - }; -}; - -var irwinHall = function(n) { - return function() { - for (var sum = 0, i = 0; i < n; ++i) sum += Math.random(); - return sum; - }; -}; - -var bates = function(n) { - var randomIrwinHall = irwinHall(n); - return function() { - return randomIrwinHall() / n; - }; -}; - -var exponential = function(lambda) { - return function() { - return -Math.log(1 - Math.random()) / lambda; - }; -}; - -function linear(t) { - return +t; -} - -function quadIn(t) { - return t * t; -} - -function quadOut(t) { - return t * (2 - t); -} - -function quadInOut(t) { - return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2; -} - -function cubicIn(t) { - return t * t * t; -} - -function cubicOut(t) { - return --t * t * t + 1; -} - -function cubicInOut(t) { - return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; -} - -var exponent = 3; - -var polyIn = (function custom(e) { - e = +e; - - function polyIn(t) { - return Math.pow(t, e); - } - - polyIn.exponent = custom; - - return polyIn; -})(exponent); - -var polyOut = (function custom(e) { - e = +e; - - function polyOut(t) { - return 1 - Math.pow(1 - t, e); - } - - polyOut.exponent = custom; - - return polyOut; -})(exponent); - -var polyInOut = (function custom(e) { - e = +e; - - function polyInOut(t) { - return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2; - } - - polyInOut.exponent = custom; - - return polyInOut; -})(exponent); - -var pi = Math.PI; -var halfPi = pi / 2; - -function sinIn(t) { - return 1 - Math.cos(t * halfPi); -} - -function sinOut(t) { - return Math.sin(t * halfPi); -} - -function sinInOut(t) { - return (1 - Math.cos(pi * t)) / 2; -} - -function expIn(t) { - return Math.pow(2, 10 * t - 10); -} - -function expOut(t) { - return 1 - Math.pow(2, -10 * t); -} - -function expInOut(t) { - return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2; -} - -function circleIn(t) { - return 1 - Math.sqrt(1 - t * t); -} - -function circleOut(t) { - return Math.sqrt(1 - --t * t); -} - -function circleInOut(t) { - return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2; -} - -var b1 = 4 / 11; -var b2 = 6 / 11; -var b3 = 8 / 11; -var b4 = 3 / 4; -var b5 = 9 / 11; -var b6 = 10 / 11; -var b7 = 15 / 16; -var b8 = 21 / 22; -var b9 = 63 / 64; -var b0 = 1 / b1 / b1; - -function bounceIn(t) { - return 1 - bounceOut(1 - t); -} - -function bounceOut(t) { - return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9; -} - -function bounceInOut(t) { - return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2; -} - -var overshoot = 1.70158; - -var backIn = (function custom(s) { - s = +s; - - function backIn(t) { - return t * t * ((s + 1) * t - s); - } - - backIn.overshoot = custom; - - return backIn; -})(overshoot); - -var backOut = (function custom(s) { - s = +s; - - function backOut(t) { - return --t * t * ((s + 1) * t + s) + 1; - } - - backOut.overshoot = custom; - - return backOut; -})(overshoot); - -var backInOut = (function custom(s) { - s = +s; - - function backInOut(t) { - return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2; - } - - backInOut.overshoot = custom; - - return backInOut; -})(overshoot); - -var tau = 2 * Math.PI; -var amplitude = 1; -var period = 0.3; - -var elasticIn = (function custom(a, p) { - var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); - - function elasticIn(t) { - return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p); - } - - elasticIn.amplitude = function(a) { return custom(a, p * tau); }; - elasticIn.period = function(p) { return custom(a, p); }; - - return elasticIn; -})(amplitude, period); - -var elasticOut = (function custom(a, p) { - var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); - - function elasticOut(t) { - return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p); - } - - elasticOut.amplitude = function(a) { return custom(a, p * tau); }; - elasticOut.period = function(p) { return custom(a, p); }; - - return elasticOut; -})(amplitude, period); - -var elasticInOut = (function custom(a, p) { - var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); - - function elasticInOut(t) { - return ((t = t * 2 - 1) < 0 - ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p) - : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2; - } - - elasticInOut.amplitude = function(a) { return custom(a, p * tau); }; - elasticInOut.period = function(p) { return custom(a, p); }; - - return elasticInOut; -})(amplitude, period); - -var area = function(polygon) { - var i = -1, - n = polygon.length, - a, - b = polygon[n - 1], - area = 0; - - while (++i < n) { - a = b; - b = polygon[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - - return area / 2; -}; - -var centroid = function(polygon) { - var i = -1, - n = polygon.length, - x = 0, - y = 0, - a, - b = polygon[n - 1], - c, - k = 0; - - while (++i < n) { - a = b; - b = polygon[i]; - k += c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - - return k *= 3, [x / k, y / k]; -}; - -// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of -// the 3D cross product in a quadrant I Cartesian coordinate system (+x is -// right, +y is up). Returns a positive value if ABC is counter-clockwise, -// negative if clockwise, and zero if the points are collinear. -var cross = function(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); -}; - -function lexicographicOrder(a, b) { - return a[0] - b[0] || a[1] - b[1]; -} - -// Computes the upper convex hull per the monotone chain algorithm. -// Assumes points.length >= 3, is sorted by x, unique in y. -// Returns an array of indices into points in left-to-right order. -function computeUpperHullIndexes(points) { - var n = points.length, - indexes = [0, 1], - size = 2; - - for (var i = 2; i < n; ++i) { - while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size; - indexes[size++] = i; - } - - return indexes.slice(0, size); // remove popped points -} - -var hull = function(points) { - if ((n = points.length) < 3) return null; - - var i, - n, - sortedPoints = new Array(n), - flippedPoints = new Array(n); - - for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i]; - sortedPoints.sort(lexicographicOrder); - for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; - - var upperIndexes = computeUpperHullIndexes(sortedPoints), - lowerIndexes = computeUpperHullIndexes(flippedPoints); - - // Construct the hull polygon, removing possible duplicate endpoints. - var skipLeft = lowerIndexes[0] === upperIndexes[0], - skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1], - hull = []; - - // Add upper hull in right-to-l order. - // Then add lower hull in left-to-right order. - for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]); - for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]); - - return hull; -}; - -var contains = function(polygon, point) { - var n = polygon.length, - p = polygon[n - 1], - x = point[0], y = point[1], - x0 = p[0], y0 = p[1], - x1, y1, - inside = false; - - for (var i = 0; i < n; ++i) { - p = polygon[i], x1 = p[0], y1 = p[1]; - if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside; - x0 = x1, y0 = y1; - } - - return inside; -}; - -var length$1 = function(polygon) { - var i = -1, - n = polygon.length, - b = polygon[n - 1], - xa, - ya, - xb = b[0], - yb = b[1], - perimeter = 0; - - while (++i < n) { - xa = xb; - ya = yb; - b = polygon[i]; - xb = b[0]; - yb = b[1]; - xa -= xb; - ya -= yb; - perimeter += Math.sqrt(xa * xa + ya * ya); - } - - return perimeter; -}; - -var pi$1 = Math.PI; -var tau$1 = 2 * pi$1; -var epsilon = 1e-6; -var tauEpsilon = tau$1 - epsilon; - -function Path() { - this._x0 = this._y0 = // start of current subpath - this._x1 = this._y1 = null; // end of current subpath - this._ = []; -} - -function path() { - return new Path; -} - -Path.prototype = path.prototype = { - constructor: Path, - moveTo: function(x, y) { - this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y); - }, - closePath: function() { - if (this._x1 !== null) { - this._x1 = this._x0, this._y1 = this._y0; - this._.push("Z"); - } - }, - lineTo: function(x, y) { - this._.push("L", this._x1 = +x, ",", this._y1 = +y); - }, - quadraticCurveTo: function(x1, y1, x, y) { - this._.push("Q", +x1, ",", +y1, ",", this._x1 = +x, ",", this._y1 = +y); - }, - bezierCurveTo: function(x1, y1, x2, y2, x, y) { - this._.push("C", +x1, ",", +y1, ",", +x2, ",", +y2, ",", this._x1 = +x, ",", this._y1 = +y); - }, - arcTo: function(x1, y1, x2, y2, r) { - x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r; - var x0 = this._x1, - y0 = this._y1, - x21 = x2 - x1, - y21 = y2 - y1, - x01 = x0 - x1, - y01 = y0 - y1, - l01_2 = x01 * x01 + y01 * y01; - - // Is the radius negative? Error. - if (r < 0) throw new Error("negative radius: " + r); - - // Is this path empty? Move to (x1,y1). - if (this._x1 === null) { - this._.push( - "M", this._x1 = x1, ",", this._y1 = y1 - ); - } - - // Or, is (x1,y1) coincident with (x0,y0)? Do nothing. - else if (!(l01_2 > epsilon)) {} - - // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear? - // Equivalently, is (x1,y1) coincident with (x2,y2)? - // Or, is the radius zero? Line to (x1,y1). - else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) { - this._.push( - "L", this._x1 = x1, ",", this._y1 = y1 - ); - } - - // Otherwise, draw an arc! - else { - var x20 = x2 - x0, - y20 = y2 - y0, - l21_2 = x21 * x21 + y21 * y21, - l20_2 = x20 * x20 + y20 * y20, - l21 = Math.sqrt(l21_2), - l01 = Math.sqrt(l01_2), - l = r * Math.tan((pi$1 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2), - t01 = l / l01, - t21 = l / l21; - - // If the start tangent is not coincident with (x0,y0), line to. - if (Math.abs(t01 - 1) > epsilon) { - this._.push( - "L", x1 + t01 * x01, ",", y1 + t01 * y01 - ); - } - - this._.push( - "A", r, ",", r, ",0,0,", +(y01 * x20 > x01 * y20), ",", this._x1 = x1 + t21 * x21, ",", this._y1 = y1 + t21 * y21 - ); - } - }, - arc: function(x, y, r, a0, a1, ccw) { - x = +x, y = +y, r = +r; - var dx = r * Math.cos(a0), - dy = r * Math.sin(a0), - x0 = x + dx, - y0 = y + dy, - cw = 1 ^ ccw, - da = ccw ? a0 - a1 : a1 - a0; - - // Is the radius negative? Error. - if (r < 0) throw new Error("negative radius: " + r); - - // Is this path empty? Move to (x0,y0). - if (this._x1 === null) { - this._.push( - "M", x0, ",", y0 - ); - } - - // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0). - else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) { - this._.push( - "L", x0, ",", y0 - ); - } - - // Is this arc empty? We’re done. - if (!r) return; - - // Is this a complete circle? Draw two arcs to complete the circle. - if (da > tauEpsilon) { - this._.push( - "A", r, ",", r, ",0,1,", cw, ",", x - dx, ",", y - dy, - "A", r, ",", r, ",0,1,", cw, ",", this._x1 = x0, ",", this._y1 = y0 - ); - } - - // Otherwise, draw an arc! - else { - if (da < 0) da = da % tau$1 + tau$1; - this._.push( - "A", r, ",", r, ",0,", +(da >= pi$1), ",", cw, ",", this._x1 = x + r * Math.cos(a1), ",", this._y1 = y + r * Math.sin(a1) - ); - } - }, - rect: function(x, y, w, h) { - this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y, "h", +w, "v", +h, "h", -w, "Z"); - }, - toString: function() { - return this._.join(""); - } -}; - -var tree_add = function(d) { - var x = +this._x.call(null, d), - y = +this._y.call(null, d); - return add(this.cover(x, y), x, y, d); -}; - -function add(tree, x, y, d) { - if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points - - var parent, - node = tree._root, - leaf = {data: d}, - x0 = tree._x0, - y0 = tree._y0, - x1 = tree._x1, - y1 = tree._y1, - xm, - ym, - xp, - yp, - right, - bottom, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return tree._root = leaf, tree; - - // Find the existing leaf for the new point, or add it. - while (node.length) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; - } - - // Is the new point is exactly coincident with the existing point? - xp = +tree._x.call(null, node.data); - yp = +tree._y.call(null, node.data); - if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; - - // Otherwise, split the leaf node until the old and new point are separated. - do { - parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); - return parent[j] = node, parent[i] = leaf, tree; -} - -function addAll(data) { - var d, i, n = data.length, - x, - y, - xz = new Array(n), - yz = new Array(n), - x0 = Infinity, - y0 = Infinity, - x1 = -Infinity, - y1 = -Infinity; - - // Compute the points and their extent. - for (i = 0; i < n; ++i) { - if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; - xz[i] = x; - yz[i] = y; - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - } - - // If there were no (valid) points, inherit the existing extent. - if (x1 < x0) x0 = this._x0, x1 = this._x1; - if (y1 < y0) y0 = this._y0, y1 = this._y1; - - // Expand the tree to cover the new points. - this.cover(x0, y0).cover(x1, y1); - - // Add the new points. - for (i = 0; i < n; ++i) { - add(this, xz[i], yz[i], data[i]); - } - - return this; -} - -var tree_cover = function(x, y) { - if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points - - var x0 = this._x0, - y0 = this._y0, - x1 = this._x1, - y1 = this._y1; - - // If the quadtree has no extent, initialize them. - // Integer extent are necessary so that if we later double the extent, - // the existing quadrant boundaries don’t change due to floating point error! - if (isNaN(x0)) { - x1 = (x0 = Math.floor(x)) + 1; - y1 = (y0 = Math.floor(y)) + 1; - } - - // Otherwise, double repeatedly to cover. - else if (x0 > x || x > x1 || y0 > y || y > y1) { - var z = x1 - x0, - node = this._root, - parent, - i; - - switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) { - case 0: { - do parent = new Array(4), parent[i] = node, node = parent; - while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1); - break; - } - case 1: { - do parent = new Array(4), parent[i] = node, node = parent; - while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1); - break; - } - case 2: { - do parent = new Array(4), parent[i] = node, node = parent; - while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y); - break; - } - case 3: { - do parent = new Array(4), parent[i] = node, node = parent; - while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y); - break; - } - } - - if (this._root && this._root.length) this._root = node; - } - - // If the quadtree covers the point already, just return. - else return this; - - this._x0 = x0; - this._y0 = y0; - this._x1 = x1; - this._y1 = y1; - return this; -}; - -var tree_data = function() { - var data = []; - this.visit(function(node) { - if (!node.length) do data.push(node.data); while (node = node.next) - }); - return data; -}; - -var tree_extent = function(_) { - return arguments.length - ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) - : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; -}; - -var Quad = function(node, x0, y0, x1, y1) { - this.node = node; - this.x0 = x0; - this.y0 = y0; - this.x1 = x1; - this.y1 = y1; -}; - -var tree_find = function(x, y, radius) { - var data, - x0 = this._x0, - y0 = this._y0, - x1, - y1, - x2, - y2, - x3 = this._x1, - y3 = this._y1, - quads = [], - node = this._root, - q, - i; - - if (node) quads.push(new Quad(node, x0, y0, x3, y3)); - if (radius == null) radius = Infinity; - else { - x0 = x - radius, y0 = y - radius; - x3 = x + radius, y3 = y + radius; - radius *= radius; - } - - while (q = quads.pop()) { - - // Stop searching if this quadrant can’t contain a closer node. - if (!(node = q.node) - || (x1 = q.x0) > x3 - || (y1 = q.y0) > y3 - || (x2 = q.x1) < x0 - || (y2 = q.y1) < y0) continue; - - // Bisect the current quadrant. - if (node.length) { - var xm = (x1 + x2) / 2, - ym = (y1 + y2) / 2; - - quads.push( - new Quad(node[3], xm, ym, x2, y2), - new Quad(node[2], x1, ym, xm, y2), - new Quad(node[1], xm, y1, x2, ym), - new Quad(node[0], x1, y1, xm, ym) - ); - - // Visit the closest quadrant first. - if (i = (y >= ym) << 1 | (x >= xm)) { - q = quads[quads.length - 1]; - quads[quads.length - 1] = quads[quads.length - 1 - i]; - quads[quads.length - 1 - i] = q; - } - } - - // Visit this point. (Visiting coincident points isn’t necessary!) - else { - var dx = x - +this._x.call(null, node.data), - dy = y - +this._y.call(null, node.data), - d2 = dx * dx + dy * dy; - if (d2 < radius) { - var d = Math.sqrt(radius = d2); - x0 = x - d, y0 = y - d; - x3 = x + d, y3 = y + d; - data = node.data; - } - } - } - - return data; -}; - -var tree_remove = function(d) { - if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points - - var parent, - node = this._root, - retainer, - previous, - next, - x0 = this._x0, - y0 = this._y0, - x1 = this._x1, - y1 = this._y1, - x, - y, - xm, - ym, - right, - bottom, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return this; - - // Find the leaf node for the point. - // While descending, also retain the deepest parent with a non-removed sibling. - if (node.length) while (true) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (!(parent = node, node = node[i = bottom << 1 | right])) return this; - if (!node.length) break; - if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; - } - - // Find the point to remove. - while (node.data !== d) if (!(previous = node, node = node.next)) return this; - if (next = node.next) delete node.next; - - // If there are multiple coincident points, remove just the point. - if (previous) return (next ? previous.next = next : delete previous.next), this; - - // If this is the root point, remove it. - if (!parent) return this._root = next, this; - - // Remove this leaf. - next ? parent[i] = next : delete parent[i]; - - // If the parent now contains exactly one leaf, collapse superfluous parents. - if ((node = parent[0] || parent[1] || parent[2] || parent[3]) - && node === (parent[3] || parent[2] || parent[1] || parent[0]) - && !node.length) { - if (retainer) retainer[j] = node; - else this._root = node; - } - - return this; -}; - -function removeAll(data) { - for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); - return this; -} - -var tree_root = function() { - return this._root; -}; - -var tree_size = function() { - var size = 0; - this.visit(function(node) { - if (!node.length) do ++size; while (node = node.next) - }); - return size; -}; - -var tree_visit = function(callback) { - var quads = [], q, node = this._root, child, x0, y0, x1, y1; - if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); - while (q = quads.pop()) { - if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { - var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; - if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); - if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); - if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); - if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); - } - } - return this; -}; - -var tree_visitAfter = function(callback) { - var quads = [], next = [], q; - if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); - while (q = quads.pop()) { - var node = q.node; - if (node.length) { - var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; - if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); - if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); - if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); - if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); - } - next.push(q); - } - while (q = next.pop()) { - callback(q.node, q.x0, q.y0, q.x1, q.y1); - } - return this; -}; - -function defaultX(d) { - return d[0]; -} - -var tree_x = function(_) { - return arguments.length ? (this._x = _, this) : this._x; -}; - -function defaultY(d) { - return d[1]; -} - -var tree_y = function(_) { - return arguments.length ? (this._y = _, this) : this._y; -}; - -function quadtree(nodes, x, y) { - var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN); - return nodes == null ? tree : tree.addAll(nodes); -} - -function Quadtree(x, y, x0, y0, x1, y1) { - this._x = x; - this._y = y; - this._x0 = x0; - this._y0 = y0; - this._x1 = x1; - this._y1 = y1; - this._root = undefined; -} - -function leaf_copy(leaf) { - var copy = {data: leaf.data}, next = copy; - while (leaf = leaf.next) next = next.next = {data: leaf.data}; - return copy; -} - -var treeProto = quadtree.prototype = Quadtree.prototype; - -treeProto.copy = function() { - var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), - node = this._root, - nodes, - child; - - if (!node) return copy; - - if (!node.length) return copy._root = leaf_copy(node), copy; - - nodes = [{source: node, target: copy._root = new Array(4)}]; - while (node = nodes.pop()) { - for (var i = 0; i < 4; ++i) { - if (child = node.source[i]) { - if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); - else node.target[i] = leaf_copy(child); - } - } - } - - return copy; -}; - -treeProto.add = tree_add; -treeProto.addAll = addAll; -treeProto.cover = tree_cover; -treeProto.data = tree_data; -treeProto.extent = tree_extent; -treeProto.find = tree_find; -treeProto.remove = tree_remove; -treeProto.removeAll = removeAll; -treeProto.root = tree_root; -treeProto.size = tree_size; -treeProto.visit = tree_visit; -treeProto.visitAfter = tree_visitAfter; -treeProto.x = tree_x; -treeProto.y = tree_y; - -var slice$1 = [].slice; - -var noabort = {}; - -function Queue(size) { - if (!(size >= 1)) throw new Error; - this._size = size; - this._call = - this._error = null; - this._tasks = []; - this._data = []; - this._waiting = - this._active = - this._ended = - this._start = 0; // inside a synchronous task callback? -} - -Queue.prototype = queue.prototype = { - constructor: Queue, - defer: function(callback) { - if (typeof callback !== "function" || this._call) throw new Error; - if (this._error != null) return this; - var t = slice$1.call(arguments, 1); - t.push(callback); - ++this._waiting, this._tasks.push(t); - poke(this); - return this; - }, - abort: function() { - if (this._error == null) abort(this, new Error("abort")); - return this; - }, - await: function(callback) { - if (typeof callback !== "function" || this._call) throw new Error; - this._call = function(error, results) { callback.apply(null, [error].concat(results)); }; - maybeNotify(this); - return this; - }, - awaitAll: function(callback) { - if (typeof callback !== "function" || this._call) throw new Error; - this._call = callback; - maybeNotify(this); - return this; - } -}; - -function poke(q) { - if (!q._start) { - try { start(q); } // let the current task complete - catch (e) { - if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously - else if (!q._data) throw e; // await callback errored synchronously - } - } -} - -function start(q) { - while (q._start = q._waiting && q._active < q._size) { - var i = q._ended + q._active, - t = q._tasks[i], - j = t.length - 1, - c = t[j]; - t[j] = end(q, i); - --q._waiting, ++q._active; - t = c.apply(null, t); - if (!q._tasks[i]) continue; // task finished synchronously - q._tasks[i] = t || noabort; - } -} - -function end(q, i) { - return function(e, r) { - if (!q._tasks[i]) return; // ignore multiple callbacks - --q._active, ++q._ended; - q._tasks[i] = null; - if (q._error != null) return; // ignore secondary errors - if (e != null) { - abort(q, e); - } else { - q._data[i] = r; - if (q._waiting) poke(q); - else maybeNotify(q); - } - }; -} - -function abort(q, e) { - var i = q._tasks.length, t; - q._error = e; // ignore active callbacks - q._data = undefined; // allow gc - q._waiting = NaN; // prevent starting - - while (--i >= 0) { - if (t = q._tasks[i]) { - q._tasks[i] = null; - if (t.abort) { - try { t.abort(); } - catch (e) { /* ignore */ } - } - } - } - - q._active = NaN; // allow notification - maybeNotify(q); -} - -function maybeNotify(q) { - if (!q._active && q._call) { - var d = q._data; - q._data = undefined; // allow gc - q._call(q._error, d); - } -} - -function queue(concurrency) { - return new Queue(arguments.length ? +concurrency : Infinity); -} - -var constant$2 = function(x) { - return function constant() { - return x; - }; -}; - -var epsilon$1 = 1e-12; -var pi$2 = Math.PI; -var halfPi$1 = pi$2 / 2; -var tau$2 = 2 * pi$2; - -function arcInnerRadius(d) { - return d.innerRadius; -} - -function arcOuterRadius(d) { - return d.outerRadius; -} - -function arcStartAngle(d) { - return d.startAngle; -} - -function arcEndAngle(d) { - return d.endAngle; -} - -function arcPadAngle(d) { - return d && d.padAngle; // Note: optional! -} - -function asin(x) { - return x >= 1 ? halfPi$1 : x <= -1 ? -halfPi$1 : Math.asin(x); -} - -function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { - var x10 = x1 - x0, y10 = y1 - y0, - x32 = x3 - x2, y32 = y3 - y2, - t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10); - return [x0 + t * x10, y0 + t * y10]; -} - -// Compute perpendicular offset line of length rc. -// http://mathworld.wolfram.com/Circle-LineIntersection.html -function cornerTangents(x0, y0, x1, y1, r1, rc, cw) { - var x01 = x0 - x1, - y01 = y0 - y1, - lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), - ox = lo * y01, - oy = -lo * x01, - x11 = x0 + ox, - y11 = y0 + oy, - x10 = x1 + ox, - y10 = y1 + oy, - x00 = (x11 + x10) / 2, - y00 = (y11 + y10) / 2, - dx = x10 - x11, - dy = y10 - y11, - d2 = dx * dx + dy * dy, - r = r1 - rc, - D = x11 * y10 - x10 * y11, - d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), - cx0 = (D * dy - dx * d) / d2, - cy0 = (-D * dx - dy * d) / d2, - cx1 = (D * dy + dx * d) / d2, - cy1 = (-D * dx + dy * d) / d2, - dx0 = cx0 - x00, - dy0 = cy0 - y00, - dx1 = cx1 - x00, - dy1 = cy1 - y00; - - // Pick the closer of the two intersection points. - // TODO Is there a faster way to determine which intersection to use? - if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; - - return { - cx: cx0, - cy: cy0, - x01: -ox, - y01: -oy, - x11: cx0 * (r1 / r - 1), - y11: cy0 * (r1 / r - 1) - }; -} - -var arc = function() { - var innerRadius = arcInnerRadius, - outerRadius = arcOuterRadius, - cornerRadius = constant$2(0), - padRadius = null, - startAngle = arcStartAngle, - endAngle = arcEndAngle, - padAngle = arcPadAngle, - context = null; - - function arc() { - var buffer, - r, - r0 = +innerRadius.apply(this, arguments), - r1 = +outerRadius.apply(this, arguments), - a0 = startAngle.apply(this, arguments) - halfPi$1, - a1 = endAngle.apply(this, arguments) - halfPi$1, - da = Math.abs(a1 - a0), - cw = a1 > a0; - - if (!context) context = buffer = path(); - - // Ensure that the outer radius is always larger than the inner radius. - if (r1 < r0) r = r1, r1 = r0, r0 = r; - - // Is it a point? - if (!(r1 > epsilon$1)) context.moveTo(0, 0); - - // Or is it a circle or annulus? - else if (da > tau$2 - epsilon$1) { - context.moveTo(r1 * Math.cos(a0), r1 * Math.sin(a0)); - context.arc(0, 0, r1, a0, a1, !cw); - if (r0 > epsilon$1) { - context.moveTo(r0 * Math.cos(a1), r0 * Math.sin(a1)); - context.arc(0, 0, r0, a1, a0, cw); - } - } - - // Or is it a circular or annular sector? - else { - var a01 = a0, - a11 = a1, - a00 = a0, - a10 = a1, - da0 = da, - da1 = da, - ap = padAngle.apply(this, arguments) / 2, - rp = (ap > epsilon$1) && (padRadius ? +padRadius.apply(this, arguments) : Math.sqrt(r0 * r0 + r1 * r1)), - rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)), - rc0 = rc, - rc1 = rc, - t0, - t1; - - // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0. - if (rp > epsilon$1) { - var p0 = asin(rp / r0 * Math.sin(ap)), - p1 = asin(rp / r1 * Math.sin(ap)); - if ((da0 -= p0 * 2) > epsilon$1) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0; - else da0 = 0, a00 = a10 = (a0 + a1) / 2; - if ((da1 -= p1 * 2) > epsilon$1) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1; - else da1 = 0, a01 = a11 = (a0 + a1) / 2; - } - - var x01 = r1 * Math.cos(a01), - y01 = r1 * Math.sin(a01), - x10 = r0 * Math.cos(a10), - y10 = r0 * Math.sin(a10); - - // Apply rounded corners? - if (rc > epsilon$1) { - var x11 = r1 * Math.cos(a11), - y11 = r1 * Math.sin(a11), - x00 = r0 * Math.cos(a00), - y00 = r0 * Math.sin(a00); - - // Restrict the corner radius according to the sector angle. - if (da < pi$2) { - var oc = da0 > epsilon$1 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10], - ax = x01 - oc[0], - ay = y01 - oc[1], - bx = x11 - oc[0], - by = y11 - oc[1], - kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), - lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); - rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); - rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); - } - } - - // Is the sector collapsed to a line? - if (!(da1 > epsilon$1)) context.moveTo(x01, y01); - - // Does the sector’s outer ring have rounded corners? - else if (rc1 > epsilon$1) { - t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw); - t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw); - - context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); - - // Have the corners merged? - if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw); - - // Otherwise, draw the two corners and the ring. - else { - context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw); - context.arc(0, 0, r1, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw); - context.arc(t1.cx, t1.cy, rc1, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw); - } - } - - // Or is the outer ring just a circular arc? - else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); - - // Is there no inner ring, and it’s a circular sector? - // Or perhaps it’s an annular sector collapsed due to padding? - if (!(r0 > epsilon$1) || !(da0 > epsilon$1)) context.lineTo(x10, y10); - - // Does the sector’s inner ring (or point) have rounded corners? - else if (rc0 > epsilon$1) { - t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw); - t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw); - - context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); - - // Have the corners merged? - if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw); - - // Otherwise, draw the two corners and the ring. - else { - context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw); - context.arc(0, 0, r0, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw); - context.arc(t1.cx, t1.cy, rc0, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw); - } - } - - // Or is the inner ring just a circular arc? - else context.arc(0, 0, r0, a10, a00, cw); - } - - context.closePath(); - - if (buffer) return context = null, buffer + "" || null; - } - - arc.centroid = function() { - var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, - a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$2 / 2; - return [Math.cos(a) * r, Math.sin(a) * r]; - }; - - arc.innerRadius = function(_) { - return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : innerRadius; - }; - - arc.outerRadius = function(_) { - return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : outerRadius; - }; - - arc.cornerRadius = function(_) { - return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : cornerRadius; - }; - - arc.padRadius = function(_) { - return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), arc) : padRadius; - }; - - arc.startAngle = function(_) { - return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : startAngle; - }; - - arc.endAngle = function(_) { - return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : endAngle; - }; - - arc.padAngle = function(_) { - return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : padAngle; - }; - - arc.context = function(_) { - return arguments.length ? ((context = _ == null ? null : _), arc) : context; - }; - - return arc; -}; - -function Linear(context) { - this._context = context; -} - -Linear.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; // proceed - default: this._context.lineTo(x, y); break; - } - } -}; - -var curveLinear = function(context) { - return new Linear(context); -}; - -function x(p) { - return p[0]; -} - -function y(p) { - return p[1]; -} - -var line = function() { - var x$$1 = x, - y$$1 = y, - defined = constant$2(true), - context = null, - curve = curveLinear, - output = null; - - function line(data) { - var i, - n = data.length, - d, - defined0 = false, - buffer; - - if (context == null) output = curve(buffer = path()); - - for (i = 0; i <= n; ++i) { - if (!(i < n && defined(d = data[i], i, data)) === defined0) { - if (defined0 = !defined0) output.lineStart(); - else output.lineEnd(); - } - if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data)); - } - - if (buffer) return output = null, buffer + "" || null; - } - - line.x = function(_) { - return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$2(+_), line) : x$$1; - }; - - line.y = function(_) { - return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$2(+_), line) : y$$1; - }; - - line.defined = function(_) { - return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), line) : defined; - }; - - line.curve = function(_) { - return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve; - }; - - line.context = function(_) { - return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context; - }; - - return line; -}; - -var area$1 = function() { - var x0 = x, - x1 = null, - y0 = constant$2(0), - y1 = y, - defined = constant$2(true), - context = null, - curve = curveLinear, - output = null; - - function area(data) { - var i, - j, - k, - n = data.length, - d, - defined0 = false, - buffer, - x0z = new Array(n), - y0z = new Array(n); - - if (context == null) output = curve(buffer = path()); - - for (i = 0; i <= n; ++i) { - if (!(i < n && defined(d = data[i], i, data)) === defined0) { - if (defined0 = !defined0) { - j = i; - output.areaStart(); - output.lineStart(); - } else { - output.lineEnd(); - output.lineStart(); - for (k = i - 1; k >= j; --k) { - output.point(x0z[k], y0z[k]); - } - output.lineEnd(); - output.areaEnd(); - } - } - if (defined0) { - x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data); - output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]); - } - } - - if (buffer) return output = null, buffer + "" || null; - } - - function arealine() { - return line().defined(defined).curve(curve).context(context); - } - - area.x = function(_) { - return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), x1 = null, area) : x0; - }; - - area.x0 = function(_) { - return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), area) : x0; - }; - - area.x1 = function(_) { - return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : x1; - }; - - area.y = function(_) { - return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), y1 = null, area) : y0; - }; - - area.y0 = function(_) { - return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), area) : y0; - }; - - area.y1 = function(_) { - return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : y1; - }; - - area.lineX0 = - area.lineY0 = function() { - return arealine().x(x0).y(y0); - }; - - area.lineY1 = function() { - return arealine().x(x0).y(y1); - }; - - area.lineX1 = function() { - return arealine().x(x1).y(y0); - }; - - area.defined = function(_) { - return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), area) : defined; - }; - - area.curve = function(_) { - return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve; - }; - - area.context = function(_) { - return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context; - }; - - return area; -}; - -var descending$1 = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; -}; - -var identity$1 = function(d) { - return d; -}; - -var pie = function() { - var value = identity$1, - sortValues = descending$1, - sort = null, - startAngle = constant$2(0), - endAngle = constant$2(tau$2), - padAngle = constant$2(0); - - function pie(data) { - var i, - n = data.length, - j, - k, - sum = 0, - index = new Array(n), - arcs = new Array(n), - a0 = +startAngle.apply(this, arguments), - da = Math.min(tau$2, Math.max(-tau$2, endAngle.apply(this, arguments) - a0)), - a1, - p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)), - pa = p * (da < 0 ? -1 : 1), - v; - - for (i = 0; i < n; ++i) { - if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) { - sum += v; - } - } - - // Optionally sort the arcs by previously-computed values or by data. - if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); }); - else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); }); - - // Compute the arcs! They are stored in the original data's order. - for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) { - j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = { - data: data[j], - index: i, - value: v, - startAngle: a0, - endAngle: a1, - padAngle: p - }; - } - - return arcs; - } - - pie.value = function(_) { - return arguments.length ? (value = typeof _ === "function" ? _ : constant$2(+_), pie) : value; - }; - - pie.sortValues = function(_) { - return arguments.length ? (sortValues = _, sort = null, pie) : sortValues; - }; - - pie.sort = function(_) { - return arguments.length ? (sort = _, sortValues = null, pie) : sort; - }; - - pie.startAngle = function(_) { - return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : startAngle; - }; - - pie.endAngle = function(_) { - return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : endAngle; - }; - - pie.padAngle = function(_) { - return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : padAngle; - }; - - return pie; -}; - -var curveRadialLinear = curveRadial(curveLinear); - -function Radial(curve) { - this._curve = curve; -} - -Radial.prototype = { - areaStart: function() { - this._curve.areaStart(); - }, - areaEnd: function() { - this._curve.areaEnd(); - }, - lineStart: function() { - this._curve.lineStart(); - }, - lineEnd: function() { - this._curve.lineEnd(); - }, - point: function(a, r) { - this._curve.point(r * Math.sin(a), r * -Math.cos(a)); - } -}; - -function curveRadial(curve) { - - function radial(context) { - return new Radial(curve(context)); - } - - radial._curve = curve; - - return radial; -} - -function radialLine(l) { - var c = l.curve; - - l.angle = l.x, delete l.x; - l.radius = l.y, delete l.y; - - l.curve = function(_) { - return arguments.length ? c(curveRadial(_)) : c()._curve; - }; - - return l; -} - -var radialLine$1 = function() { - return radialLine(line().curve(curveRadialLinear)); -}; - -var radialArea = function() { - var a = area$1().curve(curveRadialLinear), - c = a.curve, - x0 = a.lineX0, - x1 = a.lineX1, - y0 = a.lineY0, - y1 = a.lineY1; - - a.angle = a.x, delete a.x; - a.startAngle = a.x0, delete a.x0; - a.endAngle = a.x1, delete a.x1; - a.radius = a.y, delete a.y; - a.innerRadius = a.y0, delete a.y0; - a.outerRadius = a.y1, delete a.y1; - a.lineStartAngle = function() { return radialLine(x0()); }, delete a.lineX0; - a.lineEndAngle = function() { return radialLine(x1()); }, delete a.lineX1; - a.lineInnerRadius = function() { return radialLine(y0()); }, delete a.lineY0; - a.lineOuterRadius = function() { return radialLine(y1()); }, delete a.lineY1; - - a.curve = function(_) { - return arguments.length ? c(curveRadial(_)) : c()._curve; - }; - - return a; -}; - -var circle = { - draw: function(context, size) { - var r = Math.sqrt(size / pi$2); - context.moveTo(r, 0); - context.arc(0, 0, r, 0, tau$2); - } -}; - -var cross$1 = { - draw: function(context, size) { - var r = Math.sqrt(size / 5) / 2; - context.moveTo(-3 * r, -r); - context.lineTo(-r, -r); - context.lineTo(-r, -3 * r); - context.lineTo(r, -3 * r); - context.lineTo(r, -r); - context.lineTo(3 * r, -r); - context.lineTo(3 * r, r); - context.lineTo(r, r); - context.lineTo(r, 3 * r); - context.lineTo(-r, 3 * r); - context.lineTo(-r, r); - context.lineTo(-3 * r, r); - context.closePath(); - } -}; - -var tan30 = Math.sqrt(1 / 3); -var tan30_2 = tan30 * 2; - -var diamond = { - draw: function(context, size) { - var y = Math.sqrt(size / tan30_2), - x = y * tan30; - context.moveTo(0, -y); - context.lineTo(x, 0); - context.lineTo(0, y); - context.lineTo(-x, 0); - context.closePath(); - } -}; - -var ka = 0.89081309152928522810; -var kr = Math.sin(pi$2 / 10) / Math.sin(7 * pi$2 / 10); -var kx = Math.sin(tau$2 / 10) * kr; -var ky = -Math.cos(tau$2 / 10) * kr; - -var star = { - draw: function(context, size) { - var r = Math.sqrt(size * ka), - x = kx * r, - y = ky * r; - context.moveTo(0, -r); - context.lineTo(x, y); - for (var i = 1; i < 5; ++i) { - var a = tau$2 * i / 5, - c = Math.cos(a), - s = Math.sin(a); - context.lineTo(s * r, -c * r); - context.lineTo(c * x - s * y, s * x + c * y); - } - context.closePath(); - } -}; - -var square = { - draw: function(context, size) { - var w = Math.sqrt(size), - x = -w / 2; - context.rect(x, x, w, w); - } -}; - -var sqrt3 = Math.sqrt(3); - -var triangle = { - draw: function(context, size) { - var y = -Math.sqrt(size / (sqrt3 * 3)); - context.moveTo(0, y * 2); - context.lineTo(-sqrt3 * y, -y); - context.lineTo(sqrt3 * y, -y); - context.closePath(); - } -}; - -var c = -0.5; -var s = Math.sqrt(3) / 2; -var k = 1 / Math.sqrt(12); -var a = (k / 2 + 1) * 3; - -var wye = { - draw: function(context, size) { - var r = Math.sqrt(size / a), - x0 = r / 2, - y0 = r * k, - x1 = x0, - y1 = r * k + r, - x2 = -x1, - y2 = y1; - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - context.lineTo(c * x0 - s * y0, s * x0 + c * y0); - context.lineTo(c * x1 - s * y1, s * x1 + c * y1); - context.lineTo(c * x2 - s * y2, s * x2 + c * y2); - context.lineTo(c * x0 + s * y0, c * y0 - s * x0); - context.lineTo(c * x1 + s * y1, c * y1 - s * x1); - context.lineTo(c * x2 + s * y2, c * y2 - s * x2); - context.closePath(); - } -}; - -var symbols = [ - circle, - cross$1, - diamond, - square, - star, - triangle, - wye -]; - -var symbol = function() { - var type = constant$2(circle), - size = constant$2(64), - context = null; - - function symbol() { - var buffer; - if (!context) context = buffer = path(); - type.apply(this, arguments).draw(context, +size.apply(this, arguments)); - if (buffer) return context = null, buffer + "" || null; - } - - symbol.type = function(_) { - return arguments.length ? (type = typeof _ === "function" ? _ : constant$2(_), symbol) : type; - }; - - symbol.size = function(_) { - return arguments.length ? (size = typeof _ === "function" ? _ : constant$2(+_), symbol) : size; - }; - - symbol.context = function(_) { - return arguments.length ? (context = _ == null ? null : _, symbol) : context; - }; - - return symbol; -}; - -var noop = function() {}; - -function point(that, x, y) { - that._context.bezierCurveTo( - (2 * that._x0 + that._x1) / 3, - (2 * that._y0 + that._y1) / 3, - (that._x0 + 2 * that._x1) / 3, - (that._y0 + 2 * that._y1) / 3, - (that._x0 + 4 * that._x1 + x) / 6, - (that._y0 + 4 * that._y1 + y) / 6 - ); -} - -function Basis(context) { - this._context = context; -} - -Basis.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = - this._y0 = this._y1 = NaN; - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 3: point(this, this._x1, this._y1); // proceed - case 2: this._context.lineTo(this._x1, this._y1); break; - } - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; break; - case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed - default: point(this, x, y); break; - } - this._x0 = this._x1, this._x1 = x; - this._y0 = this._y1, this._y1 = y; - } -}; - -var basis = function(context) { - return new Basis(context); -}; - -function BasisClosed(context) { - this._context = context; -} - -BasisClosed.prototype = { - areaStart: noop, - areaEnd: noop, - lineStart: function() { - this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = - this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN; - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 1: { - this._context.moveTo(this._x2, this._y2); - this._context.closePath(); - break; - } - case 2: { - this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3); - this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3); - this._context.closePath(); - break; - } - case 3: { - this.point(this._x2, this._y2); - this.point(this._x3, this._y3); - this.point(this._x4, this._y4); - break; - } - } - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._x2 = x, this._y2 = y; break; - case 1: this._point = 2; this._x3 = x, this._y3 = y; break; - case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break; - default: point(this, x, y); break; - } - this._x0 = this._x1, this._x1 = x; - this._y0 = this._y1, this._y1 = y; - } -}; - -var basisClosed = function(context) { - return new BasisClosed(context); -}; - -function BasisOpen(context) { - this._context = context; -} - -BasisOpen.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = - this._y0 = this._y1 = NaN; - this._point = 0; - }, - lineEnd: function() { - if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; break; - case 1: this._point = 2; break; - case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break; - case 3: this._point = 4; // proceed - default: point(this, x, y); break; - } - this._x0 = this._x1, this._x1 = x; - this._y0 = this._y1, this._y1 = y; - } -}; - -var basisOpen = function(context) { - return new BasisOpen(context); -}; - -function Bundle(context, beta) { - this._basis = new Basis(context); - this._beta = beta; -} - -Bundle.prototype = { - lineStart: function() { - this._x = []; - this._y = []; - this._basis.lineStart(); - }, - lineEnd: function() { - var x = this._x, - y = this._y, - j = x.length - 1; - - if (j > 0) { - var x0 = x[0], - y0 = y[0], - dx = x[j] - x0, - dy = y[j] - y0, - i = -1, - t; - - while (++i <= j) { - t = i / j; - this._basis.point( - this._beta * x[i] + (1 - this._beta) * (x0 + t * dx), - this._beta * y[i] + (1 - this._beta) * (y0 + t * dy) - ); - } - } - - this._x = this._y = null; - this._basis.lineEnd(); - }, - point: function(x, y) { - this._x.push(+x); - this._y.push(+y); - } -}; - -var bundle = (function custom(beta) { - - function bundle(context) { - return beta === 1 ? new Basis(context) : new Bundle(context, beta); - } - - bundle.beta = function(beta) { - return custom(+beta); - }; - - return bundle; -})(0.85); - -function point$1(that, x, y) { - that._context.bezierCurveTo( - that._x1 + that._k * (that._x2 - that._x0), - that._y1 + that._k * (that._y2 - that._y0), - that._x2 + that._k * (that._x1 - x), - that._y2 + that._k * (that._y1 - y), - that._x2, - that._y2 - ); -} - -function Cardinal(context, tension) { - this._context = context; - this._k = (1 - tension) / 6; -} - -Cardinal.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = this._x2 = - this._y0 = this._y1 = this._y2 = NaN; - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 2: this._context.lineTo(this._x2, this._y2); break; - case 3: point$1(this, this._x1, this._y1); break; - } - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; this._x1 = x, this._y1 = y; break; - case 2: this._point = 3; // proceed - default: point$1(this, x, y); break; - } - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var cardinal = (function custom(tension) { - - function cardinal(context) { - return new Cardinal(context, tension); - } - - cardinal.tension = function(tension) { - return custom(+tension); - }; - - return cardinal; -})(0); - -function CardinalClosed(context, tension) { - this._context = context; - this._k = (1 - tension) / 6; -} - -CardinalClosed.prototype = { - areaStart: noop, - areaEnd: noop, - lineStart: function() { - this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = - this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 1: { - this._context.moveTo(this._x3, this._y3); - this._context.closePath(); - break; - } - case 2: { - this._context.lineTo(this._x3, this._y3); - this._context.closePath(); - break; - } - case 3: { - this.point(this._x3, this._y3); - this.point(this._x4, this._y4); - this.point(this._x5, this._y5); - break; - } - } - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._x3 = x, this._y3 = y; break; - case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; - case 2: this._point = 3; this._x5 = x, this._y5 = y; break; - default: point$1(this, x, y); break; - } - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var cardinalClosed = (function custom(tension) { - - function cardinal(context) { - return new CardinalClosed(context, tension); - } - - cardinal.tension = function(tension) { - return custom(+tension); - }; - - return cardinal; -})(0); - -function CardinalOpen(context, tension) { - this._context = context; - this._k = (1 - tension) / 6; -} - -CardinalOpen.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = this._x2 = - this._y0 = this._y1 = this._y2 = NaN; - this._point = 0; - }, - lineEnd: function() { - if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; break; - case 1: this._point = 2; break; - case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; - case 3: this._point = 4; // proceed - default: point$1(this, x, y); break; - } - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var cardinalOpen = (function custom(tension) { - - function cardinal(context) { - return new CardinalOpen(context, tension); - } - - cardinal.tension = function(tension) { - return custom(+tension); - }; - - return cardinal; -})(0); - -function point$2(that, x, y) { - var x1 = that._x1, - y1 = that._y1, - x2 = that._x2, - y2 = that._y2; - - if (that._l01_a > epsilon$1) { - var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a, - n = 3 * that._l01_a * (that._l01_a + that._l12_a); - x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n; - y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n; - } - - if (that._l23_a > epsilon$1) { - var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a, - m = 3 * that._l23_a * (that._l23_a + that._l12_a); - x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m; - y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m; - } - - that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2); -} - -function CatmullRom(context, alpha) { - this._context = context; - this._alpha = alpha; -} - -CatmullRom.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = this._x2 = - this._y0 = this._y1 = this._y2 = NaN; - this._l01_a = this._l12_a = this._l23_a = - this._l01_2a = this._l12_2a = this._l23_2a = - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 2: this._context.lineTo(this._x2, this._y2); break; - case 3: this.point(this._x2, this._y2); break; - } - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - - if (this._point) { - var x23 = this._x2 - x, - y23 = this._y2 - y; - this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); - } - - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; break; - case 2: this._point = 3; // proceed - default: point$2(this, x, y); break; - } - - this._l01_a = this._l12_a, this._l12_a = this._l23_a; - this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var catmullRom = (function custom(alpha) { - - function catmullRom(context) { - return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0); - } - - catmullRom.alpha = function(alpha) { - return custom(+alpha); - }; - - return catmullRom; -})(0.5); - -function CatmullRomClosed(context, alpha) { - this._context = context; - this._alpha = alpha; -} - -CatmullRomClosed.prototype = { - areaStart: noop, - areaEnd: noop, - lineStart: function() { - this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = - this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; - this._l01_a = this._l12_a = this._l23_a = - this._l01_2a = this._l12_2a = this._l23_2a = - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 1: { - this._context.moveTo(this._x3, this._y3); - this._context.closePath(); - break; - } - case 2: { - this._context.lineTo(this._x3, this._y3); - this._context.closePath(); - break; - } - case 3: { - this.point(this._x3, this._y3); - this.point(this._x4, this._y4); - this.point(this._x5, this._y5); - break; - } - } - }, - point: function(x, y) { - x = +x, y = +y; - - if (this._point) { - var x23 = this._x2 - x, - y23 = this._y2 - y; - this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); - } - - switch (this._point) { - case 0: this._point = 1; this._x3 = x, this._y3 = y; break; - case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; - case 2: this._point = 3; this._x5 = x, this._y5 = y; break; - default: point$2(this, x, y); break; - } - - this._l01_a = this._l12_a, this._l12_a = this._l23_a; - this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var catmullRomClosed = (function custom(alpha) { - - function catmullRom(context) { - return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0); - } - - catmullRom.alpha = function(alpha) { - return custom(+alpha); - }; - - return catmullRom; -})(0.5); - -function CatmullRomOpen(context, alpha) { - this._context = context; - this._alpha = alpha; -} - -CatmullRomOpen.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = this._x2 = - this._y0 = this._y1 = this._y2 = NaN; - this._l01_a = this._l12_a = this._l23_a = - this._l01_2a = this._l12_2a = this._l23_2a = - this._point = 0; - }, - lineEnd: function() { - if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - - if (this._point) { - var x23 = this._x2 - x, - y23 = this._y2 - y; - this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); - } - - switch (this._point) { - case 0: this._point = 1; break; - case 1: this._point = 2; break; - case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; - case 3: this._point = 4; // proceed - default: point$2(this, x, y); break; - } - - this._l01_a = this._l12_a, this._l12_a = this._l23_a; - this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; - this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; - this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; - } -}; - -var catmullRomOpen = (function custom(alpha) { - - function catmullRom(context) { - return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0); - } - - catmullRom.alpha = function(alpha) { - return custom(+alpha); - }; - - return catmullRom; -})(0.5); - -function LinearClosed(context) { - this._context = context; -} - -LinearClosed.prototype = { - areaStart: noop, - areaEnd: noop, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._point) this._context.closePath(); - }, - point: function(x, y) { - x = +x, y = +y; - if (this._point) this._context.lineTo(x, y); - else this._point = 1, this._context.moveTo(x, y); - } -}; - -var linearClosed = function(context) { - return new LinearClosed(context); -}; - -function sign(x) { - return x < 0 ? -1 : 1; -} - -// Calculate the slopes of the tangents (Hermite-type interpolation) based on -// the following paper: Steffen, M. 1990. A Simple Method for Monotonic -// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO. -// NOV(II), P. 443, 1990. -function slope3(that, x2, y2) { - var h0 = that._x1 - that._x0, - h1 = x2 - that._x1, - s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0), - s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0), - p = (s0 * h1 + s1 * h0) / (h0 + h1); - return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0; -} - -// Calculate a one-sided slope. -function slope2(that, t) { - var h = that._x1 - that._x0; - return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t; -} - -// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations -// "you can express cubic Hermite interpolation in terms of cubic Bézier curves -// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1". -function point$3(that, t0, t1) { - var x0 = that._x0, - y0 = that._y0, - x1 = that._x1, - y1 = that._y1, - dx = (x1 - x0) / 3; - that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1); -} - -function MonotoneX(context) { - this._context = context; -} - -MonotoneX.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x0 = this._x1 = - this._y0 = this._y1 = - this._t0 = NaN; - this._point = 0; - }, - lineEnd: function() { - switch (this._point) { - case 2: this._context.lineTo(this._x1, this._y1); break; - case 3: point$3(this, this._t0, slope2(this, this._t0)); break; - } - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - this._line = 1 - this._line; - }, - point: function(x, y) { - var t1 = NaN; - - x = +x, y = +y; - if (x === this._x1 && y === this._y1) return; // Ignore coincident points. - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; break; - case 2: this._point = 3; point$3(this, slope2(this, t1 = slope3(this, x, y)), t1); break; - default: point$3(this, this._t0, t1 = slope3(this, x, y)); break; - } - - this._x0 = this._x1, this._x1 = x; - this._y0 = this._y1, this._y1 = y; - this._t0 = t1; - } -}; - -function MonotoneY(context) { - this._context = new ReflectContext(context); -} - -(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) { - MonotoneX.prototype.point.call(this, y, x); -}; - -function ReflectContext(context) { - this._context = context; -} - -ReflectContext.prototype = { - moveTo: function(x, y) { this._context.moveTo(y, x); }, - closePath: function() { this._context.closePath(); }, - lineTo: function(x, y) { this._context.lineTo(y, x); }, - bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); } -}; - -function monotoneX(context) { - return new MonotoneX(context); -} - -function monotoneY(context) { - return new MonotoneY(context); -} - -function Natural(context) { - this._context = context; -} - -Natural.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x = []; - this._y = []; - }, - lineEnd: function() { - var x = this._x, - y = this._y, - n = x.length; - - if (n) { - this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]); - if (n === 2) { - this._context.lineTo(x[1], y[1]); - } else { - var px = controlPoints(x), - py = controlPoints(y); - for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) { - this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]); - } - } - } - - if (this._line || (this._line !== 0 && n === 1)) this._context.closePath(); - this._line = 1 - this._line; - this._x = this._y = null; - }, - point: function(x, y) { - this._x.push(+x); - this._y.push(+y); - } -}; - -// See https://www.particleincell.com/2012/bezier-splines/ for derivation. -function controlPoints(x) { - var i, - n = x.length - 1, - m, - a = new Array(n), - b = new Array(n), - r = new Array(n); - a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1]; - for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1]; - a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n]; - for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1]; - a[n - 1] = r[n - 1] / b[n - 1]; - for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i]; - b[n - 1] = (x[n] + a[n - 1]) / 2; - for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1]; - return [a, b]; -} - -var natural = function(context) { - return new Natural(context); -}; - -function Step(context, t) { - this._context = context; - this._t = t; -} - -Step.prototype = { - areaStart: function() { - this._line = 0; - }, - areaEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._x = this._y = NaN; - this._point = 0; - }, - lineEnd: function() { - if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y); - if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); - if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line; - }, - point: function(x, y) { - x = +x, y = +y; - switch (this._point) { - case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; - case 1: this._point = 2; // proceed - default: { - if (this._t <= 0) { - this._context.lineTo(this._x, y); - this._context.lineTo(x, y); - } else { - var x1 = this._x * (1 - this._t) + x * this._t; - this._context.lineTo(x1, this._y); - this._context.lineTo(x1, y); - } - break; - } - } - this._x = x, this._y = y; - } -}; - -var step = function(context) { - return new Step(context, 0.5); -}; - -function stepBefore(context) { - return new Step(context, 0); -} - -function stepAfter(context) { - return new Step(context, 1); -} - -var slice$2 = Array.prototype.slice; - -var none = function(series, order) { - if (!((n = series.length) > 1)) return; - for (var i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) { - s0 = s1, s1 = series[order[i]]; - for (var j = 0; j < m; ++j) { - s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1]; - } - } -}; - -var none$1 = function(series) { - var n = series.length, o = new Array(n); - while (--n >= 0) o[n] = n; - return o; -}; - -function stackValue(d, key) { - return d[key]; -} - -var stack = function() { - var keys = constant$2([]), - order = none$1, - offset = none, - value = stackValue; - - function stack(data) { - var kz = keys.apply(this, arguments), - i, - m = data.length, - n = kz.length, - sz = new Array(n), - oz; - - for (i = 0; i < n; ++i) { - for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) { - si[j] = sij = [0, +value(data[j], ki, j, data)]; - sij.data = data[j]; - } - si.key = ki; - } - - for (i = 0, oz = order(sz); i < n; ++i) { - sz[oz[i]].index = i; - } - - offset(sz, oz); - return sz; - } - - stack.keys = function(_) { - return arguments.length ? (keys = typeof _ === "function" ? _ : constant$2(slice$2.call(_)), stack) : keys; - }; - - stack.value = function(_) { - return arguments.length ? (value = typeof _ === "function" ? _ : constant$2(+_), stack) : value; - }; - - stack.order = function(_) { - return arguments.length ? (order = _ == null ? none$1 : typeof _ === "function" ? _ : constant$2(slice$2.call(_)), stack) : order; - }; - - stack.offset = function(_) { - return arguments.length ? (offset = _ == null ? none : _, stack) : offset; - }; - - return stack; -}; - -var expand = function(series, order) { - if (!((n = series.length) > 0)) return; - for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) { - for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0; - if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y; - } - none(series, order); -}; - -var silhouette = function(series, order) { - if (!((n = series.length) > 0)) return; - for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) { - for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0; - s0[j][1] += s0[j][0] = -y / 2; - } - none(series, order); -}; - -var wiggle = function(series, order) { - if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return; - for (var y = 0, j = 1, s0, m, n; j < m; ++j) { - for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) { - var si = series[order[i]], - sij0 = si[j][1] || 0, - sij1 = si[j - 1][1] || 0, - s3 = (sij0 - sij1) / 2; - for (var k = 0; k < i; ++k) { - var sk = series[order[k]], - skj0 = sk[j][1] || 0, - skj1 = sk[j - 1][1] || 0; - s3 += skj0 - skj1; - } - s1 += sij0, s2 += s3 * sij0; - } - s0[j - 1][1] += s0[j - 1][0] = y; - if (s1) y -= s2 / s1; - } - s0[j - 1][1] += s0[j - 1][0] = y; - none(series, order); -}; - -var ascending$1 = function(series) { - var sums = series.map(sum$1); - return none$1(series).sort(function(a, b) { return sums[a] - sums[b]; }); -}; - -function sum$1(series) { - var s = 0, i = -1, n = series.length, v; - while (++i < n) if (v = +series[i][1]) s += v; - return s; -} - -var descending$2 = function(series) { - return ascending$1(series).reverse(); -}; - -var insideOut = function(series) { - var n = series.length, - i, - j, - sums = series.map(sum$1), - order = none$1(series).sort(function(a, b) { return sums[b] - sums[a]; }), - top = 0, - bottom = 0, - tops = [], - bottoms = []; - - for (i = 0; i < n; ++i) { - j = order[i]; - if (top < bottom) { - top += sums[j]; - tops.push(j); - } else { - bottom += sums[j]; - bottoms.push(j); - } - } - - return bottoms.reverse().concat(tops); -}; - -var reverse = function(series) { - return none$1(series).reverse(); -}; - -var define = function(constructor, factory, prototype) { - constructor.prototype = factory.prototype = prototype; - prototype.constructor = constructor; -}; - -function extend(parent, definition) { - var prototype = Object.create(parent.prototype); - for (var key in definition) prototype[key] = definition[key]; - return prototype; -} - -function Color() {} - -var darker = 0.7; -var brighter = 1 / darker; - -var reHex3 = /^#([0-9a-f]{3})$/; -var reHex6 = /^#([0-9a-f]{6})$/; -var reRgbInteger = /^rgb\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*\)$/; -var reRgbPercent = /^rgb\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/; -var reRgbaInteger = /^rgba\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/; -var reRgbaPercent = /^rgba\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/; -var reHslPercent = /^hsl\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/; -var reHslaPercent = /^hsla\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/; - -var named = { - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - grey: 0x808080, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - rebeccapurple: 0x663399, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 -}; - -define(Color, color, { - displayable: function() { - return this.rgb().displayable(); - }, - toString: function() { - return this.rgb() + ""; - } -}); - -function color(format) { - var m; - format = (format + "").trim().toLowerCase(); - return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00 - : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000 - : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) - : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) - : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) - : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) - : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) - : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) - : named.hasOwnProperty(format) ? rgbn(named[format]) - : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) - : null; -} - -function rgbn(n) { - return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); -} - -function rgba(r, g, b, a) { - if (a <= 0) r = g = b = NaN; - return new Rgb(r, g, b, a); -} - -function rgbConvert(o) { - if (!(o instanceof Color)) o = color(o); - if (!o) return new Rgb; - o = o.rgb(); - return new Rgb(o.r, o.g, o.b, o.opacity); -} - -function rgb(r, g, b, opacity) { - return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); -} - -function Rgb(r, g, b, opacity) { - this.r = +r; - this.g = +g; - this.b = +b; - this.opacity = +opacity; -} - -define(Rgb, rgb, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - rgb: function() { - return this; - }, - displayable: function() { - return (0 <= this.r && this.r <= 255) - && (0 <= this.g && this.g <= 255) - && (0 <= this.b && this.b <= 255) - && (0 <= this.opacity && this.opacity <= 1); - }, - toString: function() { - var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); - return (a === 1 ? "rgb(" : "rgba(") - + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.b) || 0)) - + (a === 1 ? ")" : ", " + a + ")"); - } -})); - -function hsla(h, s, l, a) { - if (a <= 0) h = s = l = NaN; - else if (l <= 0 || l >= 1) h = s = NaN; - else if (s <= 0) h = NaN; - return new Hsl(h, s, l, a); -} - -function hslConvert(o) { - if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Color)) o = color(o); - if (!o) return new Hsl; - if (o instanceof Hsl) return o; - o = o.rgb(); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - h = NaN, - s = max - min, - l = (max + min) / 2; - if (s) { - if (r === max) h = (g - b) / s + (g < b) * 6; - else if (g === max) h = (b - r) / s + 2; - else h = (r - g) / s + 4; - s /= l < 0.5 ? max + min : 2 - max - min; - h *= 60; - } else { - s = l > 0 && l < 1 ? 0 : h; - } - return new Hsl(h, s, l, o.opacity); -} - -function hsl(h, s, l, opacity) { - return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); -} - -function Hsl(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; -} - -define(Hsl, hsl, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - rgb: function() { - var h = this.h % 360 + (this.h < 0) * 360, - s = isNaN(h) || isNaN(this.s) ? 0 : this.s, - l = this.l, - m2 = l + (l < 0.5 ? l : 1 - l) * s, - m1 = 2 * l - m2; - return new Rgb( - hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), - hsl2rgb(h, m1, m2), - hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), - this.opacity - ); - }, - displayable: function() { - return (0 <= this.s && this.s <= 1 || isNaN(this.s)) - && (0 <= this.l && this.l <= 1) - && (0 <= this.opacity && this.opacity <= 1); - } -})); - -/* From FvD 13.37, CSS Color Module Level 3 */ -function hsl2rgb(h, m1, m2) { - return (h < 60 ? m1 + (m2 - m1) * h / 60 - : h < 180 ? m2 - : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 - : m1) * 255; -} - -var deg2rad = Math.PI / 180; -var rad2deg = 180 / Math.PI; - -var Kn = 18; -var Xn = 0.950470; -var Yn = 1; -var Zn = 1.088830; -var t0 = 4 / 29; -var t1 = 6 / 29; -var t2 = 3 * t1 * t1; -var t3 = t1 * t1 * t1; - -function labConvert(o) { - if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity); - if (o instanceof Hcl) { - var h = o.h * deg2rad; - return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity); - } - if (!(o instanceof Rgb)) o = rgbConvert(o); - var b = rgb2xyz(o.r), - a = rgb2xyz(o.g), - l = rgb2xyz(o.b), - x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), - y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn), - z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn); - return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity); -} - -function lab(l, a, b, opacity) { - return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity); -} - -function Lab(l, a, b, opacity) { - this.l = +l; - this.a = +a; - this.b = +b; - this.opacity = +opacity; -} - -define(Lab, lab, extend(Color, { - brighter: function(k) { - return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); - }, - darker: function(k) { - return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); - }, - rgb: function() { - var y = (this.l + 16) / 116, - x = isNaN(this.a) ? y : y + this.a / 500, - z = isNaN(this.b) ? y : y - this.b / 200; - y = Yn * lab2xyz(y); - x = Xn * lab2xyz(x); - z = Zn * lab2xyz(z); - return new Rgb( - xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB - xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), - xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z), - this.opacity - ); - } -})); - -function xyz2lab(t) { - return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; -} - -function lab2xyz(t) { - return t > t1 ? t * t * t : t2 * (t - t0); -} - -function xyz2rgb(x) { - return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); -} - -function rgb2xyz(x) { - return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); -} - -function hclConvert(o) { - if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity); - if (!(o instanceof Lab)) o = labConvert(o); - var h = Math.atan2(o.b, o.a) * rad2deg; - return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity); -} - -function hcl(h, c, l, opacity) { - return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity); -} - -function Hcl(h, c, l, opacity) { - this.h = +h; - this.c = +c; - this.l = +l; - this.opacity = +opacity; -} - -define(Hcl, hcl, extend(Color, { - brighter: function(k) { - return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity); - }, - darker: function(k) { - return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity); - }, - rgb: function() { - return labConvert(this).rgb(); - } -})); - -var A = -0.14861; -var B = +1.78277; -var C = -0.29227; -var D = -0.90649; -var E = +1.97294; -var ED = E * D; -var EB = E * B; -var BC_DA = B * C - D * A; - -function cubehelixConvert(o) { - if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Rgb)) o = rgbConvert(o); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB), - bl = b - l, - k = (E * (g - l) - C * bl) / D, - s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1 - h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN; - return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity); -} - -function cubehelix(h, s, l, opacity) { - return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity); -} - -function Cubehelix(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; -} - -define(Cubehelix, cubehelix, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Cubehelix(this.h, this.s, this.l * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Cubehelix(this.h, this.s, this.l * k, this.opacity); - }, - rgb: function() { - var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad, - l = +this.l, - a = isNaN(this.s) ? 0 : this.s * l * (1 - l), - cosh = Math.cos(h), - sinh = Math.sin(h); - return new Rgb( - 255 * (l + a * (A * cosh + B * sinh)), - 255 * (l + a * (C * cosh + D * sinh)), - 255 * (l + a * (E * cosh)), - this.opacity - ); - } -})); - -function basis$1(t1, v0, v1, v2, v3) { - var t2 = t1 * t1, t3 = t2 * t1; - return ((1 - 3 * t1 + 3 * t2 - t3) * v0 - + (4 - 6 * t2 + 3 * t3) * v1 - + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2 - + t3 * v3) / 6; -} - -var basis$2 = function(values) { - var n = values.length - 1; - return function(t) { - var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n), - v1 = values[i], - v2 = values[i + 1], - v0 = i > 0 ? values[i - 1] : 2 * v1 - v2, - v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1; - return basis$1((t - i / n) * n, v0, v1, v2, v3); - }; -}; - -var basisClosed$1 = function(values) { - var n = values.length; - return function(t) { - var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n), - v0 = values[(i + n - 1) % n], - v1 = values[i % n], - v2 = values[(i + 1) % n], - v3 = values[(i + 2) % n]; - return basis$1((t - i / n) * n, v0, v1, v2, v3); - }; -}; - -var constant$3 = function(x) { - return function() { - return x; - }; -}; - -function linear$1(a, d) { - return function(t) { - return a + t * d; - }; -} - -function exponential$1(a, b, y) { - return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { - return Math.pow(a + t * b, y); - }; -} - -function hue(a, b) { - var d = b - a; - return d ? linear$1(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a); -} - -function gamma(y) { - return (y = +y) === 1 ? nogamma : function(a, b) { - return b - a ? exponential$1(a, b, y) : constant$3(isNaN(a) ? b : a); - }; -} - -function nogamma(a, b) { - var d = b - a; - return d ? linear$1(a, d) : constant$3(isNaN(a) ? b : a); -} - -var interpolateRgb = (function rgbGamma(y) { - var color$$1 = gamma(y); - - function rgb$$1(start, end) { - var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r), - g = color$$1(start.g, end.g), - b = color$$1(start.b, end.b), - opacity = color$$1(start.opacity, end.opacity); - return function(t) { - start.r = r(t); - start.g = g(t); - start.b = b(t); - start.opacity = opacity(t); - return start + ""; - }; - } - - rgb$$1.gamma = rgbGamma; - - return rgb$$1; -})(1); - -function rgbSpline(spline) { - return function(colors) { - var n = colors.length, - r = new Array(n), - g = new Array(n), - b = new Array(n), - i, color$$1; - for (i = 0; i < n; ++i) { - color$$1 = rgb(colors[i]); - r[i] = color$$1.r || 0; - g[i] = color$$1.g || 0; - b[i] = color$$1.b || 0; - } - r = spline(r); - g = spline(g); - b = spline(b); - color$$1.opacity = 1; - return function(t) { - color$$1.r = r(t); - color$$1.g = g(t); - color$$1.b = b(t); - return color$$1 + ""; - }; - }; -} - -var rgbBasis = rgbSpline(basis$2); -var rgbBasisClosed = rgbSpline(basisClosed$1); - -var array$1 = function(a, b) { - var nb = b ? b.length : 0, - na = a ? Math.min(nb, a.length) : 0, - x = new Array(nb), - c = new Array(nb), - i; - - for (i = 0; i < na; ++i) x[i] = interpolate(a[i], b[i]); - for (; i < nb; ++i) c[i] = b[i]; - - return function(t) { - for (i = 0; i < na; ++i) c[i] = x[i](t); - return c; - }; -}; - -var date = function(a, b) { - var d = new Date; - return a = +a, b -= a, function(t) { - return d.setTime(a + b * t), d; - }; -}; - -var interpolateNumber = function(a, b) { - return a = +a, b -= a, function(t) { - return a + b * t; - }; -}; - -var object = function(a, b) { - var i = {}, - c = {}, - k; - - if (a === null || typeof a !== "object") a = {}; - if (b === null || typeof b !== "object") b = {}; - - for (k in b) { - if (k in a) { - i[k] = interpolate(a[k], b[k]); - } else { - c[k] = b[k]; - } - } - - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; -}; - -var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; -var reB = new RegExp(reA.source, "g"); - -function zero(b) { - return function() { - return b; - }; -} - -function one(b) { - return function(t) { - return b(t) + ""; - }; -} - -var interpolateString = function(a, b) { - var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b - am, // current match in a - bm, // current match in b - bs, // string preceding current number in b, if any - i = -1, // index in s - s = [], // string constants and placeholders - q = []; // number interpolators - - // Coerce inputs to strings. - a = a + "", b = b + ""; - - // Interpolate pairs of numbers in a & b. - while ((am = reA.exec(a)) - && (bm = reB.exec(b))) { - if ((bs = bm.index) > bi) { // a string precedes the next number in b - bs = b.slice(bi, bs); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match - if (s[i]) s[i] += bm; // coalesce with previous string - else s[++i] = bm; - } else { // interpolate non-matching numbers - s[++i] = null; - q.push({i: i, x: interpolateNumber(am, bm)}); - } - bi = reB.lastIndex; - } - - // Add remains of b. - if (bi < b.length) { - bs = b.slice(bi); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - - // Special optimization for only a single match. - // Otherwise, interpolate each of the numbers and rejoin the string. - return s.length < 2 ? (q[0] - ? one(q[0].x) - : zero(b)) - : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); -}; - -var interpolate = function(a, b) { - var t = typeof b, c; - return b == null || t === "boolean" ? constant$3(b) - : (t === "number" ? interpolateNumber - : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString) - : b instanceof color ? interpolateRgb - : b instanceof Date ? date - : Array.isArray(b) ? array$1 - : isNaN(b) ? object - : interpolateNumber)(a, b); -}; - -var interpolateRound = function(a, b) { - return a = +a, b -= a, function(t) { - return Math.round(a + b * t); - }; -}; - -var degrees = 180 / Math.PI; - -var identity$2 = { - translateX: 0, - translateY: 0, - rotate: 0, - skewX: 0, - scaleX: 1, - scaleY: 1 -}; - -var decompose = function(a, b, c, d, e, f) { - var scaleX, scaleY, skewX; - if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; - if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; - if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; - if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; - return { - translateX: e, - translateY: f, - rotate: Math.atan2(b, a) * degrees, - skewX: Math.atan(skewX) * degrees, - scaleX: scaleX, - scaleY: scaleY - }; -}; - -var cssNode; -var cssRoot; -var cssView; -var svgNode; - -function parseCss(value) { - if (value === "none") return identity$2; - if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; - cssNode.style.transform = value; - value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); - cssRoot.removeChild(cssNode); - value = value.slice(7, -1).split(","); - return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); -} - -function parseSvg(value) { - if (value == null) return identity$2; - if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); - svgNode.setAttribute("transform", value); - if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2; - value = value.matrix; - return decompose(value.a, value.b, value.c, value.d, value.e, value.f); -} - -function interpolateTransform(parse, pxComma, pxParen, degParen) { - - function pop(s) { - return s.length ? s.pop() + " " : ""; - } - - function translate(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push("translate(", null, pxComma, null, pxParen); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb || yb) { - s.push("translate(" + xb + pxComma + yb + pxParen); - } - } - - function rotate(a, b, s, q) { - if (a !== b) { - if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path - q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "rotate(" + b + degParen); - } - } - - function skewX(a, b, s, q) { - if (a !== b) { - q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "skewX(" + b + degParen); - } - } - - function scale(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push(pop(s) + "scale(", null, ",", null, ")"); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb !== 1 || yb !== 1) { - s.push(pop(s) + "scale(" + xb + "," + yb + ")"); - } - } - - return function(a, b) { - var s = [], // string constants and placeholders - q = []; // number interpolators - a = parse(a), b = parse(b); - translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); - rotate(a.rotate, b.rotate, s, q); - skewX(a.skewX, b.skewX, s, q); - scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); - a = b = null; // gc - return function(t) { - var i = -1, n = q.length, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - }; -} - -var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); -var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); - -var rho = Math.SQRT2; -var rho2 = 2; -var rho4 = 4; -var epsilon2 = 1e-12; - -function cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; -} - -function sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; -} - -function tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); -} - -// p0 = [ux0, uy0, w0] -// p1 = [ux1, uy1, w1] -var interpolateZoom = function(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], - ux1 = p1[0], uy1 = p1[1], w1 = p1[2], - dx = ux1 - ux0, - dy = uy1 - uy0, - d2 = dx * dx + dy * dy, - i, - S; - - // Special case for u0 ≅ u1. - if (d2 < epsilon2) { - S = Math.log(w1 / w0) / rho; - i = function(t) { - return [ - ux0 + t * dx, - uy0 + t * dy, - w0 * Math.exp(rho * t * S) - ]; - }; - } - - // General case. - else { - var d1 = Math.sqrt(d2), - b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), - b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), - r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), - r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); - S = (r1 - r0) / rho; - i = function(t) { - var s = t * S, - coshr0 = cosh(r0), - u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); - return [ - ux0 + u * dx, - uy0 + u * dy, - w0 * coshr0 / cosh(rho * s + r0) - ]; - }; - } - - i.duration = S * 1000; - - return i; -}; - -function hsl$1(hue$$1) { - return function(start, end) { - var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h), - s = nogamma(start.s, end.s), - l = nogamma(start.l, end.l), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.h = h(t); - start.s = s(t); - start.l = l(t); - start.opacity = opacity(t); - return start + ""; - }; - } -} - -var hsl$2 = hsl$1(hue); -var hslLong = hsl$1(nogamma); - -function lab$1(start, end) { - var l = nogamma((start = lab(start)).l, (end = lab(end)).l), - a = nogamma(start.a, end.a), - b = nogamma(start.b, end.b), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.l = l(t); - start.a = a(t); - start.b = b(t); - start.opacity = opacity(t); - return start + ""; - }; -} - -function hcl$1(hue$$1) { - return function(start, end) { - var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h), - c = nogamma(start.c, end.c), - l = nogamma(start.l, end.l), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.h = h(t); - start.c = c(t); - start.l = l(t); - start.opacity = opacity(t); - return start + ""; - }; - } -} - -var hcl$2 = hcl$1(hue); -var hclLong = hcl$1(nogamma); - -function cubehelix$1(hue$$1) { - return (function cubehelixGamma(y) { - y = +y; - - function cubehelix$$1(start, end) { - var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h), - s = nogamma(start.s, end.s), - l = nogamma(start.l, end.l), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.h = h(t); - start.s = s(t); - start.l = l(Math.pow(t, y)); - start.opacity = opacity(t); - return start + ""; - }; - } - - cubehelix$$1.gamma = cubehelixGamma; - - return cubehelix$$1; - })(1); -} - -var cubehelix$2 = cubehelix$1(hue); -var cubehelixLong = cubehelix$1(nogamma); - -var quantize = function(interpolator, n) { - var samples = new Array(n); - for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1)); - return samples; -}; - -var noop$1 = {value: function() {}}; - -function dispatch() { - for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { - if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); - _[t] = []; - } - return new Dispatch(_); -} - -function Dispatch(_) { - this._ = _; -} - -function parseTypenames(typenames, types) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); - return {type: t, name: name}; - }); -} - -Dispatch.prototype = dispatch.prototype = { - constructor: Dispatch, - on: function(typename, callback) { - var _ = this._, - T = parseTypenames(typename + "", _), - t, - i = -1, - n = T.length; - - // If no callback was specified, return the callback of the given type and name. - if (arguments.length < 2) { - while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; - return; - } - - // If a type was specified, set the callback for the given type and name. - // Otherwise, if a null callback was specified, remove callbacks of the given name. - if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); - while (++i < n) { - if (t = (typename = T[i]).type) _[t] = set$2(_[t], typename.name, callback); - else if (callback == null) for (t in _) _[t] = set$2(_[t], typename.name, null); - } - - return this; - }, - copy: function() { - var copy = {}, _ = this._; - for (var t in _) copy[t] = _[t].slice(); - return new Dispatch(copy); - }, - call: function(type, that) { - if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - }, - apply: function(type, that, args) { - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - } -}; - -function get(type, name) { - for (var i = 0, n = type.length, c; i < n; ++i) { - if ((c = type[i]).name === name) { - return c.value; - } - } -} - -function set$2(type, name, callback) { - for (var i = 0, n = type.length; i < n; ++i) { - if (type[i].name === name) { - type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1)); - break; - } - } - if (callback != null) type.push({name: name, value: callback}); - return type; -} - -function objectConverter(columns) { - return new Function("d", "return {" + columns.map(function(name, i) { - return JSON.stringify(name) + ": d[" + i + "]"; - }).join(",") + "}"); -} - -function customConverter(columns, f) { - var object = objectConverter(columns); - return function(row, i) { - return f(object(row), i, columns); - }; -} - -// Compute unique columns in order of discovery. -function inferColumns(rows) { - var columnSet = Object.create(null), - columns = []; - - rows.forEach(function(row) { - for (var column in row) { - if (!(column in columnSet)) { - columns.push(columnSet[column] = column); - } - } - }); - - return columns; -} - -var dsv = function(delimiter) { - var reFormat = new RegExp("[\"" + delimiter + "\n]"), - delimiterCode = delimiter.charCodeAt(0); - - function parse(text, f) { - var convert, columns, rows = parseRows(text, function(row, i) { - if (convert) return convert(row, i - 1); - columns = row, convert = f ? customConverter(row, f) : objectConverter(row); - }); - rows.columns = columns; - return rows; - } - - function parseRows(text, f) { - var EOL = {}, // sentinel value for end-of-line - EOF = {}, // sentinel value for end-of-file - rows = [], // output rows - N = text.length, - I = 0, // current character index - n = 0, // the current line number - t, // the current token - eol; // is the current token followed by EOL? - - function token() { - if (I >= N) return EOF; // special case: end of file - if (eol) return eol = false, EOL; // special case: end of line - - // special case: quotes - var j = I, c; - if (text.charCodeAt(j) === 34) { - var i = j; - while (i++ < N) { - if (text.charCodeAt(i) === 34) { - if (text.charCodeAt(i + 1) !== 34) break; - ++i; - } - } - I = i + 2; - c = text.charCodeAt(i + 1); - if (c === 13) { - eol = true; - if (text.charCodeAt(i + 2) === 10) ++I; - } else if (c === 10) { - eol = true; - } - return text.slice(j + 1, i).replace(/""/g, "\""); - } - - // common case: find next delimiter or newline - while (I < N) { - var k = 1; - c = text.charCodeAt(I++); - if (c === 10) eol = true; // \n - else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \r|\r\n - else if (c !== delimiterCode) continue; - return text.slice(j, I - k); - } - - // special case: last token before EOF - return text.slice(j); - } - - while ((t = token()) !== EOF) { - var a = []; - while (t !== EOL && t !== EOF) { - a.push(t); - t = token(); - } - if (f && (a = f(a, n++)) == null) continue; - rows.push(a); - } - - return rows; - } - - function format(rows, columns) { - if (columns == null) columns = inferColumns(rows); - return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) { - return columns.map(function(column) { - return formatValue(row[column]); - }).join(delimiter); - })).join("\n"); - } - - function formatRows(rows) { - return rows.map(formatRow).join("\n"); - } - - function formatRow(row) { - return row.map(formatValue).join(delimiter); - } - - function formatValue(text) { - return text == null ? "" - : reFormat.test(text += "") ? "\"" + text.replace(/\"/g, "\"\"") + "\"" - : text; - } - - return { - parse: parse, - parseRows: parseRows, - format: format, - formatRows: formatRows - }; -}; - -var csv = dsv(","); - -var csvParse = csv.parse; -var csvParseRows = csv.parseRows; -var csvFormat = csv.format; -var csvFormatRows = csv.formatRows; - -var tsv = dsv("\t"); - -var tsvParse = tsv.parse; -var tsvParseRows = tsv.parseRows; -var tsvFormat = tsv.format; -var tsvFormatRows = tsv.formatRows; - -var request = function(url, callback) { - var request, - event = dispatch("beforesend", "progress", "load", "error"), - mimeType, - headers = map$1(), - xhr = new XMLHttpRequest, - user = null, - password = null, - response, - responseType, - timeout = 0; - - // If IE does not support CORS, use XDomainRequest. - if (typeof XDomainRequest !== "undefined" - && !("withCredentials" in xhr) - && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest; - - "onload" in xhr - ? xhr.onload = xhr.onerror = xhr.ontimeout = respond - : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); }; - - function respond(o) { - var status = xhr.status, result; - if (!status && hasResponse(xhr) - || status >= 200 && status < 300 - || status === 304) { - if (response) { - try { - result = response.call(request, xhr); - } catch (e) { - event.call("error", request, e); - return; - } - } else { - result = xhr; - } - event.call("load", request, result); - } else { - event.call("error", request, o); - } - } - - xhr.onprogress = function(e) { - event.call("progress", request, e); - }; - - request = { - header: function(name, value) { - name = (name + "").toLowerCase(); - if (arguments.length < 2) return headers.get(name); - if (value == null) headers.remove(name); - else headers.set(name, value + ""); - return request; - }, - - // If mimeType is non-null and no Accept header is set, a default is used. - mimeType: function(value) { - if (!arguments.length) return mimeType; - mimeType = value == null ? null : value + ""; - return request; - }, - - // Specifies what type the response value should take; - // for instance, arraybuffer, blob, document, or text. - responseType: function(value) { - if (!arguments.length) return responseType; - responseType = value; - return request; - }, - - timeout: function(value) { - if (!arguments.length) return timeout; - timeout = +value; - return request; - }, - - user: function(value) { - return arguments.length < 1 ? user : (user = value == null ? null : value + "", request); - }, - - password: function(value) { - return arguments.length < 1 ? password : (password = value == null ? null : value + "", request); - }, - - // Specify how to convert the response content to a specific type; - // changes the callback value on "load" events. - response: function(value) { - response = value; - return request; - }, - - // Alias for send("GET", …). - get: function(data, callback) { - return request.send("GET", data, callback); - }, - - // Alias for send("POST", …). - post: function(data, callback) { - return request.send("POST", data, callback); - }, - - // If callback is non-null, it will be used for error and load events. - send: function(method, data, callback) { - xhr.open(method, url, true, user, password); - if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*"); - if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); }); - if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType); - if (responseType != null) xhr.responseType = responseType; - if (timeout > 0) xhr.timeout = timeout; - if (callback == null && typeof data === "function") callback = data, data = null; - if (callback != null && callback.length === 1) callback = fixCallback(callback); - if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); }); - event.call("beforesend", request, xhr); - xhr.send(data == null ? null : data); - return request; - }, - - abort: function() { - xhr.abort(); - return request; - }, - - on: function() { - var value = event.on.apply(event, arguments); - return value === event ? request : value; - } - }; - - if (callback != null) { - if (typeof callback !== "function") throw new Error("invalid callback: " + callback); - return request.get(callback); - } - - return request; -}; - -function fixCallback(callback) { - return function(error, xhr) { - callback(error == null ? xhr : null); - }; -} - -function hasResponse(xhr) { - var type = xhr.responseType; - return type && type !== "text" - ? xhr.response // null on error - : xhr.responseText; // "" on error -} - -var type = function(defaultMimeType, response) { - return function(url, callback) { - var r = request(url).mimeType(defaultMimeType).response(response); - if (callback != null) { - if (typeof callback !== "function") throw new Error("invalid callback: " + callback); - return r.get(callback); - } - return r; - }; -}; - -var html = type("text/html", function(xhr) { - return document.createRange().createContextualFragment(xhr.responseText); -}); - -var json = type("application/json", function(xhr) { - return JSON.parse(xhr.responseText); -}); - -var text = type("text/plain", function(xhr) { - return xhr.responseText; -}); - -var xml = type("application/xml", function(xhr) { - var xml = xhr.responseXML; - if (!xml) throw new Error("parse error"); - return xml; -}); - -var dsv$1 = function(defaultMimeType, parse) { - return function(url, row, callback) { - if (arguments.length < 3) callback = row, row = null; - var r = request(url).mimeType(defaultMimeType); - r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; }; - r.row(row); - return callback ? r.get(callback) : r; - }; -}; - -function responseOf(parse, row) { - return function(request$$1) { - return parse(request$$1.responseText, row); - }; -} - -var csv$1 = dsv$1("text/csv", csvParse); - -var tsv$1 = dsv$1("text/tab-separated-values", tsvParse); - -var frame = 0; -var timeout = 0; -var interval = 0; -var pokeDelay = 1000; -var taskHead; -var taskTail; -var clockLast = 0; -var clockNow = 0; -var clockSkew = 0; -var clock = typeof performance === "object" && performance.now ? performance : Date; -var setFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : function(f) { setTimeout(f, 17); }; - -function now() { - return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); -} - -function clearNow() { - clockNow = 0; -} - -function Timer() { - this._call = - this._time = - this._next = null; -} - -Timer.prototype = timer.prototype = { - constructor: Timer, - restart: function(callback, delay, time) { - if (typeof callback !== "function") throw new TypeError("callback is not a function"); - time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); - if (!this._next && taskTail !== this) { - if (taskTail) taskTail._next = this; - else taskHead = this; - taskTail = this; - } - this._call = callback; - this._time = time; - sleep(); - }, - stop: function() { - if (this._call) { - this._call = null; - this._time = Infinity; - sleep(); - } - } -}; - -function timer(callback, delay, time) { - var t = new Timer; - t.restart(callback, delay, time); - return t; -} - -function timerFlush() { - now(); // Get the current time, if not already set. - ++frame; // Pretend we’ve set an alarm, if we haven’t already. - var t = taskHead, e; - while (t) { - if ((e = clockNow - t._time) >= 0) t._call.call(null, e); - t = t._next; - } - --frame; -} - -function wake() { - clockNow = (clockLast = clock.now()) + clockSkew; - frame = timeout = 0; - try { - timerFlush(); - } finally { - frame = 0; - nap(); - clockNow = 0; - } -} - -function poke$1() { - var now = clock.now(), delay = now - clockLast; - if (delay > pokeDelay) clockSkew -= delay, clockLast = now; -} - -function nap() { - var t0, t1 = taskHead, t2, time = Infinity; - while (t1) { - if (t1._call) { - if (time > t1._time) time = t1._time; - t0 = t1, t1 = t1._next; - } else { - t2 = t1._next, t1._next = null; - t1 = t0 ? t0._next = t2 : taskHead = t2; - } - } - taskTail = t0; - sleep(time); -} - -function sleep(time) { - if (frame) return; // Soonest alarm already set, or will be. - if (timeout) timeout = clearTimeout(timeout); - var delay = time - clockNow; - if (delay > 24) { - if (time < Infinity) timeout = setTimeout(wake, delay); - if (interval) interval = clearInterval(interval); - } else { - if (!interval) interval = setInterval(poke$1, pokeDelay); - frame = 1, setFrame(wake); - } -} - -var timeout$1 = function(callback, delay, time) { - var t = new Timer; - delay = delay == null ? 0 : +delay; - t.restart(function(elapsed) { - t.stop(); - callback(elapsed + delay); - }, delay, time); - return t; -}; - -var interval$1 = function(callback, delay, time) { - var t = new Timer, total = delay; - if (delay == null) return t.restart(callback, delay, time), t; - delay = +delay, time = time == null ? now() : +time; - t.restart(function tick(elapsed) { - elapsed += total; - t.restart(tick, total += delay, time); - callback(elapsed); - }, delay, time); - return t; -}; - -var t0$1 = new Date; -var t1$1 = new Date; - -function newInterval(floori, offseti, count, field) { - - function interval(date) { - return floori(date = new Date(+date)), date; - } - - interval.floor = interval; - - interval.ceil = function(date) { - return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; - }; - - interval.round = function(date) { - var d0 = interval(date), - d1 = interval.ceil(date); - return date - d0 < d1 - date ? d0 : d1; - }; - - interval.offset = function(date, step) { - return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; - }; - - interval.range = function(start, stop, step) { - var range = []; - start = interval.ceil(start); - step = step == null ? 1 : Math.floor(step); - if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date - do range.push(new Date(+start)); while (offseti(start, step), floori(start), start < stop) - return range; - }; - - interval.filter = function(test) { - return newInterval(function(date) { - if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); - }, function(date, step) { - if (date >= date) while (--step >= 0) while (offseti(date, 1), !test(date)) {} // eslint-disable-line no-empty - }); - }; - - if (count) { - interval.count = function(start, end) { - t0$1.setTime(+start), t1$1.setTime(+end); - floori(t0$1), floori(t1$1); - return Math.floor(count(t0$1, t1$1)); - }; - - interval.every = function(step) { - step = Math.floor(step); - return !isFinite(step) || !(step > 0) ? null - : !(step > 1) ? interval - : interval.filter(field - ? function(d) { return field(d) % step === 0; } - : function(d) { return interval.count(0, d) % step === 0; }); - }; - } - - return interval; -} - -var millisecond = newInterval(function() { - // noop -}, function(date, step) { - date.setTime(+date + step); -}, function(start, end) { - return end - start; -}); - -// An optimized implementation for this simple case. -millisecond.every = function(k) { - k = Math.floor(k); - if (!isFinite(k) || !(k > 0)) return null; - if (!(k > 1)) return millisecond; - return newInterval(function(date) { - date.setTime(Math.floor(date / k) * k); - }, function(date, step) { - date.setTime(+date + step * k); - }, function(start, end) { - return (end - start) / k; - }); -}; - -var milliseconds = millisecond.range; - -var durationSecond = 1e3; -var durationMinute = 6e4; -var durationHour = 36e5; -var durationDay = 864e5; -var durationWeek = 6048e5; - -var second = newInterval(function(date) { - date.setTime(Math.floor(date / durationSecond) * durationSecond); -}, function(date, step) { - date.setTime(+date + step * durationSecond); -}, function(start, end) { - return (end - start) / durationSecond; -}, function(date) { - return date.getUTCSeconds(); -}); - -var seconds = second.range; - -var minute = newInterval(function(date) { - date.setTime(Math.floor(date / durationMinute) * durationMinute); -}, function(date, step) { - date.setTime(+date + step * durationMinute); -}, function(start, end) { - return (end - start) / durationMinute; -}, function(date) { - return date.getMinutes(); -}); - -var minutes = minute.range; - -var hour = newInterval(function(date) { - var offset = date.getTimezoneOffset() * durationMinute % durationHour; - if (offset < 0) offset += durationHour; - date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset); -}, function(date, step) { - date.setTime(+date + step * durationHour); -}, function(start, end) { - return (end - start) / durationHour; -}, function(date) { - return date.getHours(); -}); - -var hours = hour.range; - -var day = newInterval(function(date) { - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setDate(date.getDate() + step); -}, function(start, end) { - return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay; -}, function(date) { - return date.getDate() - 1; -}); - -var days = day.range; - -function weekday(i) { - return newInterval(function(date) { - date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); - date.setHours(0, 0, 0, 0); - }, function(date, step) { - date.setDate(date.getDate() + step * 7); - }, function(start, end) { - return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; - }); -} - -var sunday = weekday(0); -var monday = weekday(1); -var tuesday = weekday(2); -var wednesday = weekday(3); -var thursday = weekday(4); -var friday = weekday(5); -var saturday = weekday(6); - -var sundays = sunday.range; -var mondays = monday.range; -var tuesdays = tuesday.range; -var wednesdays = wednesday.range; -var thursdays = thursday.range; -var fridays = friday.range; -var saturdays = saturday.range; - -var month = newInterval(function(date) { - date.setDate(1); - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setMonth(date.getMonth() + step); -}, function(start, end) { - return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; -}, function(date) { - return date.getMonth(); -}); - -var months = month.range; - -var year = newInterval(function(date) { - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setFullYear(date.getFullYear() + step); -}, function(start, end) { - return end.getFullYear() - start.getFullYear(); -}, function(date) { - return date.getFullYear(); -}); - -// An optimized implementation for this simple case. -year.every = function(k) { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { - date.setFullYear(Math.floor(date.getFullYear() / k) * k); - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); - }, function(date, step) { - date.setFullYear(date.getFullYear() + step * k); - }); -}; - -var years = year.range; - -var utcMinute = newInterval(function(date) { - date.setUTCSeconds(0, 0); -}, function(date, step) { - date.setTime(+date + step * durationMinute); -}, function(start, end) { - return (end - start) / durationMinute; -}, function(date) { - return date.getUTCMinutes(); -}); - -var utcMinutes = utcMinute.range; - -var utcHour = newInterval(function(date) { - date.setUTCMinutes(0, 0, 0); -}, function(date, step) { - date.setTime(+date + step * durationHour); -}, function(start, end) { - return (end - start) / durationHour; -}, function(date) { - return date.getUTCHours(); -}); - -var utcHours = utcHour.range; - -var utcDay = newInterval(function(date) { - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCDate(date.getUTCDate() + step); -}, function(start, end) { - return (end - start) / durationDay; -}, function(date) { - return date.getUTCDate() - 1; -}); - -var utcDays = utcDay.range; - -function utcWeekday(i) { - return newInterval(function(date) { - date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); - date.setUTCHours(0, 0, 0, 0); - }, function(date, step) { - date.setUTCDate(date.getUTCDate() + step * 7); - }, function(start, end) { - return (end - start) / durationWeek; - }); -} - -var utcSunday = utcWeekday(0); -var utcMonday = utcWeekday(1); -var utcTuesday = utcWeekday(2); -var utcWednesday = utcWeekday(3); -var utcThursday = utcWeekday(4); -var utcFriday = utcWeekday(5); -var utcSaturday = utcWeekday(6); - -var utcSundays = utcSunday.range; -var utcMondays = utcMonday.range; -var utcTuesdays = utcTuesday.range; -var utcWednesdays = utcWednesday.range; -var utcThursdays = utcThursday.range; -var utcFridays = utcFriday.range; -var utcSaturdays = utcSaturday.range; - -var utcMonth = newInterval(function(date) { - date.setUTCDate(1); - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCMonth(date.getUTCMonth() + step); -}, function(start, end) { - return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; -}, function(date) { - return date.getUTCMonth(); -}); - -var utcMonths = utcMonth.range; - -var utcYear = newInterval(function(date) { - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCFullYear(date.getUTCFullYear() + step); -}, function(start, end) { - return end.getUTCFullYear() - start.getUTCFullYear(); -}, function(date) { - return date.getUTCFullYear(); -}); - -// An optimized implementation for this simple case. -utcYear.every = function(k) { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { - date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); - }, function(date, step) { - date.setUTCFullYear(date.getUTCFullYear() + step * k); - }); -}; - -var utcYears = utcYear.range; - -// Computes the decimal coefficient and exponent of the specified number x with -// significant digits p, where x is positive and p is in [1, 21] or undefined. -// For example, formatDecimal(1.23) returns ["123", 0]. -var formatDecimal = function(x, p) { - if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity - var i, coefficient = x.slice(0, i); - - // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ - // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). - return [ - coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, - +x.slice(i + 1) - ]; -}; - -var exponent$1 = function(x) { - return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN; -}; - -var formatGroup = function(grouping, thousands) { - return function(value, width) { - var i = value.length, - t = [], - j = 0, - g = grouping[0], - length = 0; - - while (i > 0 && g > 0) { - if (length + g + 1 > width) g = Math.max(1, width - length); - t.push(value.substring(i -= g, i + g)); - if ((length += g + 1) > width) break; - g = grouping[j = (j + 1) % grouping.length]; - } - - return t.reverse().join(thousands); - }; -}; - -var formatDefault = function(x, p) { - x = x.toPrecision(p); - - out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) { - switch (x[i]) { - case ".": i0 = i1 = i; break; - case "0": if (i0 === 0) i0 = i; i1 = i; break; - case "e": break out; - default: if (i0 > 0) i0 = 0; break; - } - } - - return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x; -}; - -var prefixExponent; - -var formatPrefixAuto = function(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1], - i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, - n = coefficient.length; - return i === n ? coefficient - : i > n ? coefficient + new Array(i - n + 1).join("0") - : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) - : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! -}; - -var formatRounded = function(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1]; - return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient - : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) - : coefficient + new Array(exponent - coefficient.length + 2).join("0"); -}; - -var formatTypes = { - "": formatDefault, - "%": function(x, p) { return (x * 100).toFixed(p); }, - "b": function(x) { return Math.round(x).toString(2); }, - "c": function(x) { return x + ""; }, - "d": function(x) { return Math.round(x).toString(10); }, - "e": function(x, p) { return x.toExponential(p); }, - "f": function(x, p) { return x.toFixed(p); }, - "g": function(x, p) { return x.toPrecision(p); }, - "o": function(x) { return Math.round(x).toString(8); }, - "p": function(x, p) { return formatRounded(x * 100, p); }, - "r": formatRounded, - "s": formatPrefixAuto, - "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, - "x": function(x) { return Math.round(x).toString(16); } -}; - -// [[fill]align][sign][symbol][0][width][,][.precision][type] -var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i; - -var formatSpecifier = function(specifier) { - return new FormatSpecifier(specifier); -}; - -function FormatSpecifier(specifier) { - if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); - - var match, - fill = match[1] || " ", - align = match[2] || ">", - sign = match[3] || "-", - symbol = match[4] || "", - zero = !!match[5], - width = match[6] && +match[6], - comma = !!match[7], - precision = match[8] && +match[8].slice(1), - type = match[9] || ""; - - // The "n" type is an alias for ",g". - if (type === "n") comma = true, type = "g"; - - // Map invalid types to the default format. - else if (!formatTypes[type]) type = ""; - - // If zero fill is specified, padding goes after sign and before digits. - if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; - - this.fill = fill; - this.align = align; - this.sign = sign; - this.symbol = symbol; - this.zero = zero; - this.width = width; - this.comma = comma; - this.precision = precision; - this.type = type; -} - -FormatSpecifier.prototype.toString = function() { - return this.fill - + this.align - + this.sign - + this.symbol - + (this.zero ? "0" : "") - + (this.width == null ? "" : Math.max(1, this.width | 0)) - + (this.comma ? "," : "") - + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0)) - + this.type; -}; - -var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"]; - -function identity$3(x) { - return x; -} - -var formatLocale = function(locale) { - var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3, - currency = locale.currency, - decimal = locale.decimal; - - function newFormat(specifier) { - specifier = formatSpecifier(specifier); - - var fill = specifier.fill, - align = specifier.align, - sign = specifier.sign, - symbol = specifier.symbol, - zero = specifier.zero, - width = specifier.width, - comma = specifier.comma, - precision = specifier.precision, - type = specifier.type; - - // Compute the prefix and suffix. - // For SI-prefix, the suffix is lazily computed. - var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", - suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? "%" : ""; - - // What format function should we use? - // Is this an integer type? - // Can this type generate exponential notation? - var formatType = formatTypes[type], - maybeSuffix = !type || /[defgprs%]/.test(type); - - // Set the default precision if not specified, - // or clamp the specified precision to the supported range. - // For significant precision, it must be in [1, 21]. - // For fixed precision, it must be in [0, 20]. - precision = precision == null ? (type ? 6 : 12) - : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) - : Math.max(0, Math.min(20, precision)); - - function format(value) { - var valuePrefix = prefix, - valueSuffix = suffix, - i, n, c; - - if (type === "c") { - valueSuffix = formatType(value) + valueSuffix; - value = ""; - } else { - value = +value; - - // Convert negative to positive, and compute the prefix. - // Note that -0 is not less than 0, but 1 / -0 is! - var valueNegative = (value < 0 || 1 / value < 0) && (value *= -1, true); - - // Perform the initial formatting. - value = formatType(value, precision); - - // If the original value was negative, it may be rounded to zero during - // formatting; treat this as (positive) zero. - if (valueNegative) { - i = -1, n = value.length; - valueNegative = false; - while (++i < n) { - if (c = value.charCodeAt(i), (48 < c && c < 58) - || (type === "x" && 96 < c && c < 103) - || (type === "X" && 64 < c && c < 71)) { - valueNegative = true; - break; - } - } - } - - // Compute the prefix and suffix. - valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - valueSuffix = valueSuffix + (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + (valueNegative && sign === "(" ? ")" : ""); - - // Break the formatted value into the integer “value” part that can be - // grouped, and fractional or exponential “suffix” part that is not. - if (maybeSuffix) { - i = -1, n = value.length; - while (++i < n) { - if (c = value.charCodeAt(i), 48 > c || c > 57) { - valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; - value = value.slice(0, i); - break; - } - } - } - } - - // If the fill character is not "0", grouping is applied before padding. - if (comma && !zero) value = group(value, Infinity); - - // Compute the padding. - var length = valuePrefix.length + value.length + valueSuffix.length, - padding = length < width ? new Array(width - length + 1).join(fill) : ""; - - // If the fill character is "0", grouping is applied after padding. - if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; - - // Reconstruct the final output based on the desired alignment. - switch (align) { - case "<": return valuePrefix + value + valueSuffix + padding; - case "=": return valuePrefix + padding + value + valueSuffix; - case "^": return padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); - } - return padding + valuePrefix + value + valueSuffix; - } - - format.toString = function() { - return specifier + ""; - }; - - return format; - } - - function formatPrefix(specifier, value) { - var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), - e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3, - k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3]; - return function(value) { - return f(k * value) + prefix; - }; - } - - return { - format: newFormat, - formatPrefix: formatPrefix - }; -}; - -var locale$1; - - - -defaultLocale({ - decimal: ".", - thousands: ",", - grouping: [3], - currency: ["$", ""] -}); - -function defaultLocale(definition) { - locale$1 = formatLocale(definition); - exports.format = locale$1.format; - exports.formatPrefix = locale$1.formatPrefix; - return locale$1; -} - -var precisionFixed = function(step) { - return Math.max(0, -exponent$1(Math.abs(step))); -}; - -var precisionPrefix = function(step, value) { - return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step))); -}; - -var precisionRound = function(step, max) { - step = Math.abs(step), max = Math.abs(max) - step; - return Math.max(0, exponent$1(max) - exponent$1(step)) + 1; -}; - -function localDate(d) { - if (0 <= d.y && d.y < 100) { - var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); - date.setFullYear(d.y); - return date; - } - return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); -} - -function utcDate(d) { - if (0 <= d.y && d.y < 100) { - var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); - date.setUTCFullYear(d.y); - return date; - } - return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); -} - -function newYear(y) { - return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0}; -} - -function formatLocale$1(locale) { - var locale_dateTime = locale.dateTime, - locale_date = locale.date, - locale_time = locale.time, - locale_periods = locale.periods, - locale_weekdays = locale.days, - locale_shortWeekdays = locale.shortDays, - locale_months = locale.months, - locale_shortMonths = locale.shortMonths; - - var periodRe = formatRe(locale_periods), - periodLookup = formatLookup(locale_periods), - weekdayRe = formatRe(locale_weekdays), - weekdayLookup = formatLookup(locale_weekdays), - shortWeekdayRe = formatRe(locale_shortWeekdays), - shortWeekdayLookup = formatLookup(locale_shortWeekdays), - monthRe = formatRe(locale_months), - monthLookup = formatLookup(locale_months), - shortMonthRe = formatRe(locale_shortMonths), - shortMonthLookup = formatLookup(locale_shortMonths); - - var formats = { - "a": formatShortWeekday, - "A": formatWeekday, - "b": formatShortMonth, - "B": formatMonth, - "c": null, - "d": formatDayOfMonth, - "e": formatDayOfMonth, - "H": formatHour24, - "I": formatHour12, - "j": formatDayOfYear, - "L": formatMilliseconds, - "m": formatMonthNumber, - "M": formatMinutes, - "p": formatPeriod, - "S": formatSeconds, - "U": formatWeekNumberSunday, - "w": formatWeekdayNumber, - "W": formatWeekNumberMonday, - "x": null, - "X": null, - "y": formatYear, - "Y": formatFullYear, - "Z": formatZone, - "%": formatLiteralPercent - }; - - var utcFormats = { - "a": formatUTCShortWeekday, - "A": formatUTCWeekday, - "b": formatUTCShortMonth, - "B": formatUTCMonth, - "c": null, - "d": formatUTCDayOfMonth, - "e": formatUTCDayOfMonth, - "H": formatUTCHour24, - "I": formatUTCHour12, - "j": formatUTCDayOfYear, - "L": formatUTCMilliseconds, - "m": formatUTCMonthNumber, - "M": formatUTCMinutes, - "p": formatUTCPeriod, - "S": formatUTCSeconds, - "U": formatUTCWeekNumberSunday, - "w": formatUTCWeekdayNumber, - "W": formatUTCWeekNumberMonday, - "x": null, - "X": null, - "y": formatUTCYear, - "Y": formatUTCFullYear, - "Z": formatUTCZone, - "%": formatLiteralPercent - }; - - var parses = { - "a": parseShortWeekday, - "A": parseWeekday, - "b": parseShortMonth, - "B": parseMonth, - "c": parseLocaleDateTime, - "d": parseDayOfMonth, - "e": parseDayOfMonth, - "H": parseHour24, - "I": parseHour24, - "j": parseDayOfYear, - "L": parseMilliseconds, - "m": parseMonthNumber, - "M": parseMinutes, - "p": parsePeriod, - "S": parseSeconds, - "U": parseWeekNumberSunday, - "w": parseWeekdayNumber, - "W": parseWeekNumberMonday, - "x": parseLocaleDate, - "X": parseLocaleTime, - "y": parseYear, - "Y": parseFullYear, - "Z": parseZone, - "%": parseLiteralPercent - }; - - // These recursive directive definitions must be deferred. - formats.x = newFormat(locale_date, formats); - formats.X = newFormat(locale_time, formats); - formats.c = newFormat(locale_dateTime, formats); - utcFormats.x = newFormat(locale_date, utcFormats); - utcFormats.X = newFormat(locale_time, utcFormats); - utcFormats.c = newFormat(locale_dateTime, utcFormats); - - function newFormat(specifier, formats) { - return function(date) { - var string = [], - i = -1, - j = 0, - n = specifier.length, - c, - pad, - format; - - if (!(date instanceof Date)) date = new Date(+date); - - while (++i < n) { - if (specifier.charCodeAt(i) === 37) { - string.push(specifier.slice(j, i)); - if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); - else pad = c === "e" ? " " : "0"; - if (format = formats[c]) c = format(date, pad); - string.push(c); - j = i + 1; - } - } - - string.push(specifier.slice(j, i)); - return string.join(""); - }; - } - - function newParse(specifier, newDate) { - return function(string) { - var d = newYear(1900), - i = parseSpecifier(d, specifier, string += "", 0); - if (i != string.length) return null; - - // The am-pm flag is 0 for AM, and 1 for PM. - if ("p" in d) d.H = d.H % 12 + d.p * 12; - - // Convert day-of-week and week-of-year to day-of-year. - if ("W" in d || "U" in d) { - if (!("w" in d)) d.w = "W" in d ? 1 : 0; - var day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay(); - d.m = 0; - d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7; - } - - // If a time zone is specified, all fields are interpreted as UTC and then - // offset according to the specified time zone. - if ("Z" in d) { - d.H += d.Z / 100 | 0; - d.M += d.Z % 100; - return utcDate(d); - } - - // Otherwise, all fields are in local time. - return newDate(d); - }; - } - - function parseSpecifier(d, specifier, string, j) { - var i = 0, - n = specifier.length, - m = string.length, - c, - parse; - - while (i < n) { - if (j >= m) return -1; - c = specifier.charCodeAt(i++); - if (c === 37) { - c = specifier.charAt(i++); - parse = parses[c in pads ? specifier.charAt(i++) : c]; - if (!parse || ((j = parse(d, string, j)) < 0)) return -1; - } else if (c != string.charCodeAt(j++)) { - return -1; - } - } - - return j; - } - - function parsePeriod(d, string, i) { - var n = periodRe.exec(string.slice(i)); - return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseShortWeekday(d, string, i) { - var n = shortWeekdayRe.exec(string.slice(i)); - return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseWeekday(d, string, i) { - var n = weekdayRe.exec(string.slice(i)); - return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseShortMonth(d, string, i) { - var n = shortMonthRe.exec(string.slice(i)); - return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseMonth(d, string, i) { - var n = monthRe.exec(string.slice(i)); - return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseLocaleDateTime(d, string, i) { - return parseSpecifier(d, locale_dateTime, string, i); - } - - function parseLocaleDate(d, string, i) { - return parseSpecifier(d, locale_date, string, i); - } - - function parseLocaleTime(d, string, i) { - return parseSpecifier(d, locale_time, string, i); - } - - function formatShortWeekday(d) { - return locale_shortWeekdays[d.getDay()]; - } - - function formatWeekday(d) { - return locale_weekdays[d.getDay()]; - } - - function formatShortMonth(d) { - return locale_shortMonths[d.getMonth()]; - } - - function formatMonth(d) { - return locale_months[d.getMonth()]; - } - - function formatPeriod(d) { - return locale_periods[+(d.getHours() >= 12)]; - } - - function formatUTCShortWeekday(d) { - return locale_shortWeekdays[d.getUTCDay()]; - } - - function formatUTCWeekday(d) { - return locale_weekdays[d.getUTCDay()]; - } - - function formatUTCShortMonth(d) { - return locale_shortMonths[d.getUTCMonth()]; - } - - function formatUTCMonth(d) { - return locale_months[d.getUTCMonth()]; - } - - function formatUTCPeriod(d) { - return locale_periods[+(d.getUTCHours() >= 12)]; - } - - return { - format: function(specifier) { - var f = newFormat(specifier += "", formats); - f.toString = function() { return specifier; }; - return f; - }, - parse: function(specifier) { - var p = newParse(specifier += "", localDate); - p.toString = function() { return specifier; }; - return p; - }, - utcFormat: function(specifier) { - var f = newFormat(specifier += "", utcFormats); - f.toString = function() { return specifier; }; - return f; - }, - utcParse: function(specifier) { - var p = newParse(specifier, utcDate); - p.toString = function() { return specifier; }; - return p; - } - }; -} - -var pads = {"-": "", "_": " ", "0": "0"}; -var numberRe = /^\s*\d+/; -var percentRe = /^%/; -var requoteRe = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - -function pad(value, fill, width) { - var sign = value < 0 ? "-" : "", - string = (sign ? -value : value) + "", - length = string.length; - return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); -} - -function requote(s) { - return s.replace(requoteRe, "\\$&"); -} - -function formatRe(names) { - return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); -} - -function formatLookup(names) { - var map = {}, i = -1, n = names.length; - while (++i < n) map[names[i].toLowerCase()] = i; - return map; -} - -function parseWeekdayNumber(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 1)); - return n ? (d.w = +n[0], i + n[0].length) : -1; -} - -function parseWeekNumberSunday(d, string, i) { - var n = numberRe.exec(string.slice(i)); - return n ? (d.U = +n[0], i + n[0].length) : -1; -} - -function parseWeekNumberMonday(d, string, i) { - var n = numberRe.exec(string.slice(i)); - return n ? (d.W = +n[0], i + n[0].length) : -1; -} - -function parseFullYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 4)); - return n ? (d.y = +n[0], i + n[0].length) : -1; -} - -function parseYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; -} - -function parseZone(d, string, i) { - var n = /^(Z)|([+-]\d\d)(?:\:?(\d\d))?/.exec(string.slice(i, i + 6)); - return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; -} - -function parseMonthNumber(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.m = n[0] - 1, i + n[0].length) : -1; -} - -function parseDayOfMonth(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.d = +n[0], i + n[0].length) : -1; -} - -function parseDayOfYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 3)); - return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; -} - -function parseHour24(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.H = +n[0], i + n[0].length) : -1; -} - -function parseMinutes(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.M = +n[0], i + n[0].length) : -1; -} - -function parseSeconds(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.S = +n[0], i + n[0].length) : -1; -} - -function parseMilliseconds(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 3)); - return n ? (d.L = +n[0], i + n[0].length) : -1; -} - -function parseLiteralPercent(d, string, i) { - var n = percentRe.exec(string.slice(i, i + 1)); - return n ? i + n[0].length : -1; -} - -function formatDayOfMonth(d, p) { - return pad(d.getDate(), p, 2); -} - -function formatHour24(d, p) { - return pad(d.getHours(), p, 2); -} - -function formatHour12(d, p) { - return pad(d.getHours() % 12 || 12, p, 2); -} - -function formatDayOfYear(d, p) { - return pad(1 + day.count(year(d), d), p, 3); -} - -function formatMilliseconds(d, p) { - return pad(d.getMilliseconds(), p, 3); -} - -function formatMonthNumber(d, p) { - return pad(d.getMonth() + 1, p, 2); -} - -function formatMinutes(d, p) { - return pad(d.getMinutes(), p, 2); -} - -function formatSeconds(d, p) { - return pad(d.getSeconds(), p, 2); -} - -function formatWeekNumberSunday(d, p) { - return pad(sunday.count(year(d), d), p, 2); -} - -function formatWeekdayNumber(d) { - return d.getDay(); -} - -function formatWeekNumberMonday(d, p) { - return pad(monday.count(year(d), d), p, 2); -} - -function formatYear(d, p) { - return pad(d.getFullYear() % 100, p, 2); -} - -function formatFullYear(d, p) { - return pad(d.getFullYear() % 10000, p, 4); -} - -function formatZone(d) { - var z = d.getTimezoneOffset(); - return (z > 0 ? "-" : (z *= -1, "+")) - + pad(z / 60 | 0, "0", 2) - + pad(z % 60, "0", 2); -} - -function formatUTCDayOfMonth(d, p) { - return pad(d.getUTCDate(), p, 2); -} - -function formatUTCHour24(d, p) { - return pad(d.getUTCHours(), p, 2); -} - -function formatUTCHour12(d, p) { - return pad(d.getUTCHours() % 12 || 12, p, 2); -} - -function formatUTCDayOfYear(d, p) { - return pad(1 + utcDay.count(utcYear(d), d), p, 3); -} - -function formatUTCMilliseconds(d, p) { - return pad(d.getUTCMilliseconds(), p, 3); -} - -function formatUTCMonthNumber(d, p) { - return pad(d.getUTCMonth() + 1, p, 2); -} - -function formatUTCMinutes(d, p) { - return pad(d.getUTCMinutes(), p, 2); -} - -function formatUTCSeconds(d, p) { - return pad(d.getUTCSeconds(), p, 2); -} - -function formatUTCWeekNumberSunday(d, p) { - return pad(utcSunday.count(utcYear(d), d), p, 2); -} - -function formatUTCWeekdayNumber(d) { - return d.getUTCDay(); -} - -function formatUTCWeekNumberMonday(d, p) { - return pad(utcMonday.count(utcYear(d), d), p, 2); -} - -function formatUTCYear(d, p) { - return pad(d.getUTCFullYear() % 100, p, 2); -} - -function formatUTCFullYear(d, p) { - return pad(d.getUTCFullYear() % 10000, p, 4); -} - -function formatUTCZone() { - return "+0000"; -} - -function formatLiteralPercent() { - return "%"; -} - -var locale$2; - - - - - -defaultLocale$1({ - dateTime: "%x, %X", - date: "%-m/%-d/%Y", - time: "%-I:%M:%S %p", - periods: ["AM", "PM"], - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -}); - -function defaultLocale$1(definition) { - locale$2 = formatLocale$1(definition); - exports.timeFormat = locale$2.format; - exports.timeParse = locale$2.parse; - exports.utcFormat = locale$2.utcFormat; - exports.utcParse = locale$2.utcParse; - return locale$2; -} - -var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ"; - -function formatIsoNative(date) { - return date.toISOString(); -} - -var formatIso = Date.prototype.toISOString - ? formatIsoNative - : exports.utcFormat(isoSpecifier); - -function parseIsoNative(string) { - var date = new Date(string); - return isNaN(date) ? null : date; -} - -var parseIso = +new Date("2000-01-01T00:00:00.000Z") - ? parseIsoNative - : exports.utcParse(isoSpecifier); - -var array$2 = Array.prototype; - -var map$3 = array$2.map; -var slice$3 = array$2.slice; - -var implicit = {name: "implicit"}; - -function ordinal(range) { - var index = map$1(), - domain = [], - unknown = implicit; - - range = range == null ? [] : slice$3.call(range); - - function scale(d) { - var key = d + "", i = index.get(key); - if (!i) { - if (unknown !== implicit) return unknown; - index.set(key, i = domain.push(d)); - } - return range[(i - 1) % range.length]; - } - - scale.domain = function(_) { - if (!arguments.length) return domain.slice(); - domain = [], index = map$1(); - var i = -1, n = _.length, d, key; - while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d)); - return scale; - }; - - scale.range = function(_) { - return arguments.length ? (range = slice$3.call(_), scale) : range.slice(); - }; - - scale.unknown = function(_) { - return arguments.length ? (unknown = _, scale) : unknown; - }; - - scale.copy = function() { - return ordinal() - .domain(domain) - .range(range) - .unknown(unknown); - }; - - return scale; -} - -function band() { - var scale = ordinal().unknown(undefined), - domain = scale.domain, - ordinalRange = scale.range, - range$$1 = [0, 1], - step, - bandwidth, - round = false, - paddingInner = 0, - paddingOuter = 0, - align = 0.5; - - delete scale.unknown; - - function rescale() { - var n = domain().length, - reverse = range$$1[1] < range$$1[0], - start = range$$1[reverse - 0], - stop = range$$1[1 - reverse]; - step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); - if (round) step = Math.floor(step); - start += (stop - start - step * (n - paddingInner)) * align; - bandwidth = step * (1 - paddingInner); - if (round) start = Math.round(start), bandwidth = Math.round(bandwidth); - var values = range(n).map(function(i) { return start + step * i; }); - return ordinalRange(reverse ? values.reverse() : values); - } - - scale.domain = function(_) { - return arguments.length ? (domain(_), rescale()) : domain(); - }; - - scale.range = function(_) { - return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice(); - }; - - scale.rangeRound = function(_) { - return range$$1 = [+_[0], +_[1]], round = true, rescale(); - }; - - scale.bandwidth = function() { - return bandwidth; - }; - - scale.step = function() { - return step; - }; - - scale.round = function(_) { - return arguments.length ? (round = !!_, rescale()) : round; - }; - - scale.padding = function(_) { - return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; - }; - - scale.paddingInner = function(_) { - return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; - }; - - scale.paddingOuter = function(_) { - return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter; - }; - - scale.align = function(_) { - return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align; - }; - - scale.copy = function() { - return band() - .domain(domain()) - .range(range$$1) - .round(round) - .paddingInner(paddingInner) - .paddingOuter(paddingOuter) - .align(align); - }; - - return rescale(); -} - -function pointish(scale) { - var copy = scale.copy; - - scale.padding = scale.paddingOuter; - delete scale.paddingInner; - delete scale.paddingOuter; - - scale.copy = function() { - return pointish(copy()); - }; - - return scale; -} - -function point$4() { - return pointish(band().paddingInner(1)); -} - -var constant$4 = function(x) { - return function() { - return x; - }; -}; - -var number$1 = function(x) { - return +x; -}; - -var unit = [0, 1]; - -function deinterpolateLinear(a, b) { - return (b -= (a = +a)) - ? function(x) { return (x - a) / b; } - : constant$4(b); -} - -function deinterpolateClamp(deinterpolate) { - return function(a, b) { - var d = deinterpolate(a = +a, b = +b); - return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); }; - }; -} - -function reinterpolateClamp(reinterpolate) { - return function(a, b) { - var r = reinterpolate(a = +a, b = +b); - return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); }; - }; -} - -function bimap(domain, range$$1, deinterpolate, reinterpolate) { - var d0 = domain[0], d1 = domain[1], r0 = range$$1[0], r1 = range$$1[1]; - if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0); - else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1); - return function(x) { return r0(d0(x)); }; -} - -function polymap(domain, range$$1, deinterpolate, reinterpolate) { - var j = Math.min(domain.length, range$$1.length) - 1, - d = new Array(j), - r = new Array(j), - i = -1; - - // Reverse descending domains. - if (domain[j] < domain[0]) { - domain = domain.slice().reverse(); - range$$1 = range$$1.slice().reverse(); - } - - while (++i < j) { - d[i] = deinterpolate(domain[i], domain[i + 1]); - r[i] = reinterpolate(range$$1[i], range$$1[i + 1]); - } - - return function(x) { - var i = bisectRight(domain, x, 1, j) - 1; - return r[i](d[i](x)); - }; -} - -function copy(source, target) { - return target - .domain(source.domain()) - .range(source.range()) - .interpolate(source.interpolate()) - .clamp(source.clamp()); -} - -// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. -// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b]. -function continuous(deinterpolate, reinterpolate) { - var domain = unit, - range$$1 = unit, - interpolate$$1 = interpolate, - clamp = false, - piecewise, - output, - input; - - function rescale() { - piecewise = Math.min(domain.length, range$$1.length) > 2 ? polymap : bimap; - output = input = null; - return scale; - } - - function scale(x) { - return (output || (output = piecewise(domain, range$$1, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x); - } - - scale.invert = function(y) { - return (input || (input = piecewise(range$$1, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y); - }; - - scale.domain = function(_) { - return arguments.length ? (domain = map$3.call(_, number$1), rescale()) : domain.slice(); - }; - - scale.range = function(_) { - return arguments.length ? (range$$1 = slice$3.call(_), rescale()) : range$$1.slice(); - }; - - scale.rangeRound = function(_) { - return range$$1 = slice$3.call(_), interpolate$$1 = interpolateRound, rescale(); - }; - - scale.clamp = function(_) { - return arguments.length ? (clamp = !!_, rescale()) : clamp; - }; - - scale.interpolate = function(_) { - return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1; - }; - - return rescale(); -} - -var tickFormat = function(domain, count, specifier) { - var start = domain[0], - stop = domain[domain.length - 1], - step = tickStep(start, stop, count == null ? 10 : count), - precision; - specifier = formatSpecifier(specifier == null ? ",f" : specifier); - switch (specifier.type) { - case "s": { - var value = Math.max(Math.abs(start), Math.abs(stop)); - if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; - return exports.formatPrefix(specifier, value); - } - case "": - case "e": - case "g": - case "p": - case "r": { - if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); - break; - } - case "f": - case "%": { - if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; - break; - } - } - return exports.format(specifier); -}; - -function linearish(scale) { - var domain = scale.domain; - - scale.ticks = function(count) { - var d = domain(); - return ticks(d[0], d[d.length - 1], count == null ? 10 : count); - }; - - scale.tickFormat = function(count, specifier) { - return tickFormat(domain(), count, specifier); - }; - - scale.nice = function(count) { - var d = domain(), - i = d.length - 1, - n = count == null ? 10 : count, - start = d[0], - stop = d[i], - step = tickStep(start, stop, n); - - if (step) { - step = tickStep(Math.floor(start / step) * step, Math.ceil(stop / step) * step, n); - d[0] = Math.floor(start / step) * step; - d[i] = Math.ceil(stop / step) * step; - domain(d); - } - - return scale; - }; - - return scale; -} - -function linear$2() { - var scale = continuous(deinterpolateLinear, interpolateNumber); - - scale.copy = function() { - return copy(scale, linear$2()); - }; - - return linearish(scale); -} - -function identity$4() { - var domain = [0, 1]; - - function scale(x) { - return +x; - } - - scale.invert = scale; - - scale.domain = scale.range = function(_) { - return arguments.length ? (domain = map$3.call(_, number$1), scale) : domain.slice(); - }; - - scale.copy = function() { - return identity$4().domain(domain); - }; - - return linearish(scale); -} - -var nice = function(domain, interval) { - domain = domain.slice(); - - var i0 = 0, - i1 = domain.length - 1, - x0 = domain[i0], - x1 = domain[i1], - t; - - if (x1 < x0) { - t = i0, i0 = i1, i1 = t; - t = x0, x0 = x1, x1 = t; - } - - domain[i0] = interval.floor(x0); - domain[i1] = interval.ceil(x1); - return domain; -}; - -function deinterpolate(a, b) { - return (b = Math.log(b / a)) - ? function(x) { return Math.log(x / a) / b; } - : constant$4(b); -} - -function reinterpolate(a, b) { - return a < 0 - ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); } - : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); }; -} - -function pow10(x) { - return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x; -} - -function powp(base) { - return base === 10 ? pow10 - : base === Math.E ? Math.exp - : function(x) { return Math.pow(base, x); }; -} - -function logp(base) { - return base === Math.E ? Math.log - : base === 10 && Math.log10 - || base === 2 && Math.log2 - || (base = Math.log(base), function(x) { return Math.log(x) / base; }); -} - -function reflect(f) { - return function(x) { - return -f(-x); - }; -} - -function log() { - var scale = continuous(deinterpolate, reinterpolate).domain([1, 10]), - domain = scale.domain, - base = 10, - logs = logp(10), - pows = powp(10); - - function rescale() { - logs = logp(base), pows = powp(base); - if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows); - return scale; - } - - scale.base = function(_) { - return arguments.length ? (base = +_, rescale()) : base; - }; - - scale.domain = function(_) { - return arguments.length ? (domain(_), rescale()) : domain(); - }; - - scale.ticks = function(count) { - var d = domain(), - u = d[0], - v = d[d.length - 1], - r; - - if (r = v < u) i = u, u = v, v = i; - - var i = logs(u), - j = logs(v), - p, - k, - t, - n = count == null ? 10 : +count, - z = []; - - if (!(base % 1) && j - i < n) { - i = Math.round(i) - 1, j = Math.round(j) + 1; - if (u > 0) for (; i < j; ++i) { - for (k = 1, p = pows(i); k < base; ++k) { - t = p * k; - if (t < u) continue; - if (t > v) break; - z.push(t); - } - } else for (; i < j; ++i) { - for (k = base - 1, p = pows(i); k >= 1; --k) { - t = p * k; - if (t < u) continue; - if (t > v) break; - z.push(t); - } - } - } else { - z = ticks(i, j, Math.min(j - i, n)).map(pows); - } - - return r ? z.reverse() : z; - }; - - scale.tickFormat = function(count, specifier) { - if (specifier == null) specifier = base === 10 ? ".0e" : ","; - if (typeof specifier !== "function") specifier = exports.format(specifier); - if (count === Infinity) return specifier; - if (count == null) count = 10; - var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate? - return function(d) { - var i = d / pows(Math.round(logs(d))); - if (i * base < base - 0.5) i *= base; - return i <= k ? specifier(d) : ""; - }; - }; - - scale.nice = function() { - return domain(nice(domain(), { - floor: function(x) { return pows(Math.floor(logs(x))); }, - ceil: function(x) { return pows(Math.ceil(logs(x))); } - })); - }; - - scale.copy = function() { - return copy(scale, log().base(base)); - }; - - return scale; -} - -function raise(x, exponent) { - return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent); -} - -function pow() { - var exponent = 1, - scale = continuous(deinterpolate, reinterpolate), - domain = scale.domain; - - function deinterpolate(a, b) { - return (b = raise(b, exponent) - (a = raise(a, exponent))) - ? function(x) { return (raise(x, exponent) - a) / b; } - : constant$4(b); - } - - function reinterpolate(a, b) { - b = raise(b, exponent) - (a = raise(a, exponent)); - return function(t) { return raise(a + b * t, 1 / exponent); }; - } - - scale.exponent = function(_) { - return arguments.length ? (exponent = +_, domain(domain())) : exponent; - }; - - scale.copy = function() { - return copy(scale, pow().exponent(exponent)); - }; - - return linearish(scale); -} - -function sqrt() { - return pow().exponent(0.5); -} - -function quantile$$1() { - var domain = [], - range$$1 = [], - thresholds = []; - - function rescale() { - var i = 0, n = Math.max(1, range$$1.length); - thresholds = new Array(n - 1); - while (++i < n) thresholds[i - 1] = threshold(domain, i / n); - return scale; - } - - function scale(x) { - if (!isNaN(x = +x)) return range$$1[bisectRight(thresholds, x)]; - } - - scale.invertExtent = function(y) { - var i = range$$1.indexOf(y); - return i < 0 ? [NaN, NaN] : [ - i > 0 ? thresholds[i - 1] : domain[0], - i < thresholds.length ? thresholds[i] : domain[domain.length - 1] - ]; - }; - - scale.domain = function(_) { - if (!arguments.length) return domain.slice(); - domain = []; - for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d); - domain.sort(ascending); - return rescale(); - }; - - scale.range = function(_) { - return arguments.length ? (range$$1 = slice$3.call(_), rescale()) : range$$1.slice(); - }; - - scale.quantiles = function() { - return thresholds.slice(); - }; - - scale.copy = function() { - return quantile$$1() - .domain(domain) - .range(range$$1); - }; - - return scale; -} - -function quantize$1() { - var x0 = 0, - x1 = 1, - n = 1, - domain = [0.5], - range$$1 = [0, 1]; - - function scale(x) { - if (x <= x) return range$$1[bisectRight(domain, x, 0, n)]; - } - - function rescale() { - var i = -1; - domain = new Array(n); - while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); - return scale; - } - - scale.domain = function(_) { - return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1]; - }; - - scale.range = function(_) { - return arguments.length ? (n = (range$$1 = slice$3.call(_)).length - 1, rescale()) : range$$1.slice(); - }; - - scale.invertExtent = function(y) { - var i = range$$1.indexOf(y); - return i < 0 ? [NaN, NaN] - : i < 1 ? [x0, domain[0]] - : i >= n ? [domain[n - 1], x1] - : [domain[i - 1], domain[i]]; - }; - - scale.copy = function() { - return quantize$1() - .domain([x0, x1]) - .range(range$$1); - }; - - return linearish(scale); -} - -function threshold$1() { - var domain = [0.5], - range$$1 = [0, 1], - n = 1; - - function scale(x) { - if (x <= x) return range$$1[bisectRight(domain, x, 0, n)]; - } - - scale.domain = function(_) { - return arguments.length ? (domain = slice$3.call(_), n = Math.min(domain.length, range$$1.length - 1), scale) : domain.slice(); - }; - - scale.range = function(_) { - return arguments.length ? (range$$1 = slice$3.call(_), n = Math.min(domain.length, range$$1.length - 1), scale) : range$$1.slice(); - }; - - scale.invertExtent = function(y) { - var i = range$$1.indexOf(y); - return [domain[i - 1], domain[i]]; - }; - - scale.copy = function() { - return threshold$1() - .domain(domain) - .range(range$$1); - }; - - return scale; -} - -var durationSecond$1 = 1000; -var durationMinute$1 = durationSecond$1 * 60; -var durationHour$1 = durationMinute$1 * 60; -var durationDay$1 = durationHour$1 * 24; -var durationWeek$1 = durationDay$1 * 7; -var durationMonth = durationDay$1 * 30; -var durationYear = durationDay$1 * 365; - -function date$1(t) { - return new Date(t); -} - -function number$2(t) { - return t instanceof Date ? +t : +new Date(+t); -} - -function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) { - var scale = continuous(deinterpolateLinear, interpolateNumber), - invert = scale.invert, - domain = scale.domain; - - var formatMillisecond = format(".%L"), - formatSecond = format(":%S"), - formatMinute = format("%I:%M"), - formatHour = format("%I %p"), - formatDay = format("%a %d"), - formatWeek = format("%b %d"), - formatMonth = format("%B"), - formatYear = format("%Y"); - - var tickIntervals = [ - [second$$1, 1, durationSecond$1], - [second$$1, 5, 5 * durationSecond$1], - [second$$1, 15, 15 * durationSecond$1], - [second$$1, 30, 30 * durationSecond$1], - [minute$$1, 1, durationMinute$1], - [minute$$1, 5, 5 * durationMinute$1], - [minute$$1, 15, 15 * durationMinute$1], - [minute$$1, 30, 30 * durationMinute$1], - [ hour$$1, 1, durationHour$1 ], - [ hour$$1, 3, 3 * durationHour$1 ], - [ hour$$1, 6, 6 * durationHour$1 ], - [ hour$$1, 12, 12 * durationHour$1 ], - [ day$$1, 1, durationDay$1 ], - [ day$$1, 2, 2 * durationDay$1 ], - [ week, 1, durationWeek$1 ], - [ month$$1, 1, durationMonth ], - [ month$$1, 3, 3 * durationMonth ], - [ year$$1, 1, durationYear ] - ]; - - function tickFormat(date) { - return (second$$1(date) < date ? formatMillisecond - : minute$$1(date) < date ? formatSecond - : hour$$1(date) < date ? formatMinute - : day$$1(date) < date ? formatHour - : month$$1(date) < date ? (week(date) < date ? formatDay : formatWeek) - : year$$1(date) < date ? formatMonth - : formatYear)(date); - } - - function tickInterval(interval, start, stop, step) { - if (interval == null) interval = 10; - - // If a desired tick count is specified, pick a reasonable tick interval - // based on the extent of the domain and a rough estimate of tick size. - // Otherwise, assume interval is already a time interval and use it. - if (typeof interval === "number") { - var target = Math.abs(stop - start) / interval, - i = bisector(function(i) { return i[2]; }).right(tickIntervals, target); - if (i === tickIntervals.length) { - step = tickStep(start / durationYear, stop / durationYear, interval); - interval = year$$1; - } else if (i) { - i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i]; - step = i[1]; - interval = i[0]; - } else { - step = tickStep(start, stop, interval); - interval = millisecond$$1; - } - } - - return step == null ? interval : interval.every(step); - } - - scale.invert = function(y) { - return new Date(invert(y)); - }; - - scale.domain = function(_) { - return arguments.length ? domain(map$3.call(_, number$2)) : domain().map(date$1); - }; - - scale.ticks = function(interval, step) { - var d = domain(), - t0 = d[0], - t1 = d[d.length - 1], - r = t1 < t0, - t; - if (r) t = t0, t0 = t1, t1 = t; - t = tickInterval(interval, t0, t1, step); - t = t ? t.range(t0, t1 + 1) : []; // inclusive stop - return r ? t.reverse() : t; - }; - - scale.tickFormat = function(count, specifier) { - return specifier == null ? tickFormat : format(specifier); - }; - - scale.nice = function(interval, step) { - var d = domain(); - return (interval = tickInterval(interval, d[0], d[d.length - 1], step)) - ? domain(nice(d, interval)) - : scale; - }; - - scale.copy = function() { - return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format)); - }; - - return scale; -} - -var time = function() { - return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]); -}; - -var utcTime = function() { - return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]); -}; - -var colors = function(s) { - return s.match(/.{6}/g).map(function(x) { - return "#" + x; - }); -}; - -var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"); - -var category20b = colors("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"); - -var category20c = colors("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"); - -var category20 = colors("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"); - -var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0)); - -var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); - -var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); - -var rainbow = cubehelix(); - -var rainbow$1 = function(t) { - if (t < 0 || t > 1) t -= Math.floor(t); - var ts = Math.abs(t - 0.5); - rainbow.h = 360 * t - 100; - rainbow.s = 1.5 - 1.5 * ts; - rainbow.l = 0.8 - 0.9 * ts; - return rainbow + ""; -}; - -function ramp(range) { - var n = range.length; - return function(t) { - return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))]; - }; -} - -var viridis = ramp(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")); - -var magma = ramp(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")); - -var inferno = ramp(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")); - -var plasma = ramp(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")); - -function sequential(interpolator) { - var x0 = 0, - x1 = 1, - clamp = false; - - function scale(x) { - var t = (x - x0) / (x1 - x0); - return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t); - } - - scale.domain = function(_) { - return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1]; - }; - - scale.clamp = function(_) { - return arguments.length ? (clamp = !!_, scale) : clamp; - }; - - scale.interpolator = function(_) { - return arguments.length ? (interpolator = _, scale) : interpolator; - }; - - scale.copy = function() { - return sequential(interpolator).domain([x0, x1]).clamp(clamp); - }; - - return linearish(scale); -} - -var xhtml = "http://www.w3.org/1999/xhtml"; - -var namespaces = { - svg: "http://www.w3.org/2000/svg", - xhtml: xhtml, - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" -}; - -var namespace = function(name) { - var prefix = name += "", i = prefix.indexOf(":"); - if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); - return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; -}; - -function creatorInherit(name) { - return function() { - var document = this.ownerDocument, - uri = this.namespaceURI; - return uri === xhtml && document.documentElement.namespaceURI === xhtml - ? document.createElement(name) - : document.createElementNS(uri, name); - }; -} - -function creatorFixed(fullname) { - return function() { - return this.ownerDocument.createElementNS(fullname.space, fullname.local); - }; -} - -var creator = function(name) { - var fullname = namespace(name); - return (fullname.local - ? creatorFixed - : creatorInherit)(fullname); -}; - -var nextId = 0; - -function local() { - return new Local; -} - -function Local() { - this._ = "@" + (++nextId).toString(36); -} - -Local.prototype = local.prototype = { - constructor: Local, - get: function(node) { - var id = this._; - while (!(id in node)) if (!(node = node.parentNode)) return; - return node[id]; - }, - set: function(node, value) { - return node[this._] = value; - }, - remove: function(node) { - return this._ in node && delete node[this._]; - }, - toString: function() { - return this._; - } -}; - -var matcher = function(selector) { - return function() { - return this.matches(selector); - }; -}; - -if (typeof document !== "undefined") { - var element = document.documentElement; - if (!element.matches) { - var vendorMatches = element.webkitMatchesSelector - || element.msMatchesSelector - || element.mozMatchesSelector - || element.oMatchesSelector; - matcher = function(selector) { - return function() { - return vendorMatches.call(this, selector); - }; - }; - } -} - -var matcher$1 = matcher; - -var filterEvents = {}; - -exports.event = null; - -if (typeof document !== "undefined") { - var element$1 = document.documentElement; - if (!("onmouseenter" in element$1)) { - filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; - } -} - -function filterContextListener(listener, index, group) { - listener = contextListener(listener, index, group); - return function(event) { - var related = event.relatedTarget; - if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { - listener.call(this, event); - } - }; -} - -function contextListener(listener, index, group) { - return function(event1) { - var event0 = exports.event; // Events can be reentrant (e.g., focus). - exports.event = event1; - try { - listener.call(this, this.__data__, index, group); - } finally { - exports.event = event0; - } - }; -} - -function parseTypenames$1(typenames) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - return {type: t, name: name}; - }); -} - -function onRemove(typename) { - return function() { - var on = this.__on; - if (!on) return; - for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { - if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - } else { - on[++i] = o; - } - } - if (++i) on.length = i; - else delete this.__on; - }; -} - -function onAdd(typename, value, capture) { - var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; - return function(d, i, group) { - var on = this.__on, o, listener = wrap(value, i, group); - if (on) for (var j = 0, m = on.length; j < m; ++j) { - if ((o = on[j]).type === typename.type && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - this.addEventListener(o.type, o.listener = listener, o.capture = capture); - o.value = value; - return; - } - } - this.addEventListener(typename.type, listener, capture); - o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; - if (!on) this.__on = [o]; - else on.push(o); - }; -} - -var selection_on = function(typename, value, capture) { - var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t; - - if (arguments.length < 2) { - var on = this.node().__on; - if (on) for (var j = 0, m = on.length, o; j < m; ++j) { - for (i = 0, o = on[j]; i < n; ++i) { - if ((t = typenames[i]).type === o.type && t.name === o.name) { - return o.value; - } - } - } - return; - } - - on = value ? onAdd : onRemove; - if (capture == null) capture = false; - for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); - return this; -}; - -function customEvent(event1, listener, that, args) { - var event0 = exports.event; - event1.sourceEvent = exports.event; - exports.event = event1; - try { - return listener.apply(that, args); - } finally { - exports.event = event0; - } -} - -var sourceEvent = function() { - var current = exports.event, source; - while (source = current.sourceEvent) current = source; - return current; -}; - -var point$5 = function(node, event) { - var svg = node.ownerSVGElement || node; - - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - point.x = event.clientX, point.y = event.clientY; - point = point.matrixTransform(node.getScreenCTM().inverse()); - return [point.x, point.y]; - } - - var rect = node.getBoundingClientRect(); - return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; -}; - -var mouse = function(node) { - var event = sourceEvent(); - if (event.changedTouches) event = event.changedTouches[0]; - return point$5(node, event); -}; - -function none$2() {} - -var selector = function(selector) { - return selector == null ? none$2 : function() { - return this.querySelector(selector); - }; -}; - -var selection_select = function(select) { - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - } - } - } - - return new Selection(subgroups, this._parents); -}; - -function empty() { - return []; -} - -var selectorAll = function(selector) { - return selector == null ? empty : function() { - return this.querySelectorAll(selector); - }; -}; - -var selection_selectAll = function(select) { - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - subgroups.push(select.call(node, node.__data__, i, group)); - parents.push(node); - } - } - } - - return new Selection(subgroups, parents); -}; - -var selection_filter = function(match) { - if (typeof match !== "function") match = matcher$1(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Selection(subgroups, this._parents); -}; - -var sparse = function(update) { - return new Array(update.length); -}; - -var selection_enter = function() { - return new Selection(this._enter || this._groups.map(sparse), this._parents); -}; - -function EnterNode(parent, datum) { - this.ownerDocument = parent.ownerDocument; - this.namespaceURI = parent.namespaceURI; - this._next = null; - this._parent = parent; - this.__data__ = datum; -} - -EnterNode.prototype = { - constructor: EnterNode, - appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, - insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, - querySelector: function(selector) { return this._parent.querySelector(selector); }, - querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } -}; - -var constant$5 = function(x) { - return function() { - return x; - }; -}; - -var keyPrefix = "$"; // Protect against keys like “__proto__”. - -function bindIndex(parent, group, enter, update, exit, data) { - var i = 0, - node, - groupLength = group.length, - dataLength = data.length; - - // Put any non-null nodes that fit into update. - // Put any null nodes into enter. - // Put any remaining data into enter. - for (; i < dataLength; ++i) { - if (node = group[i]) { - node.__data__ = data[i]; - update[i] = node; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Put any non-null nodes that don’t fit into exit. - for (; i < groupLength; ++i) { - if (node = group[i]) { - exit[i] = node; - } - } -} - -function bindKey(parent, group, enter, update, exit, data, key) { - var i, - node, - nodeByKeyValue = {}, - groupLength = group.length, - dataLength = data.length, - keyValues = new Array(groupLength), - keyValue; - - // Compute the key for each node. - // If multiple nodes have the same key, the duplicates are added to exit. - for (i = 0; i < groupLength; ++i) { - if (node = group[i]) { - keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); - if (keyValue in nodeByKeyValue) { - exit[i] = node; - } else { - nodeByKeyValue[keyValue] = node; - } - } - } - - // Compute the key for each datum. - // If there a node associated with this key, join and add it to update. - // If there is not (or the key is a duplicate), add it to enter. - for (i = 0; i < dataLength; ++i) { - keyValue = keyPrefix + key.call(parent, data[i], i, data); - if (node = nodeByKeyValue[keyValue]) { - update[i] = node; - node.__data__ = data[i]; - nodeByKeyValue[keyValue] = null; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Add any remaining nodes that were not bound to data to exit. - for (i = 0; i < groupLength; ++i) { - if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { - exit[i] = node; - } - } -} - -var selection_data = function(value, key) { - if (!value) { - data = new Array(this.size()), j = -1; - this.each(function(d) { data[++j] = d; }); - return data; - } - - var bind = key ? bindKey : bindIndex, - parents = this._parents, - groups = this._groups; - - if (typeof value !== "function") value = constant$5(value); - - for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { - var parent = parents[j], - group = groups[j], - groupLength = group.length, - data = value.call(parent, parent && parent.__data__, j, parents), - dataLength = data.length, - enterGroup = enter[j] = new Array(dataLength), - updateGroup = update[j] = new Array(dataLength), - exitGroup = exit[j] = new Array(groupLength); - - bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); - - // Now connect the enter nodes to their following update node, such that - // appendChild can insert the materialized enter node before this node, - // rather than at the end of the parent node. - for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { - if (previous = enterGroup[i0]) { - if (i0 >= i1) i1 = i0 + 1; - while (!(next = updateGroup[i1]) && ++i1 < dataLength); - previous._next = next || null; - } - } - } - - update = new Selection(update, parents); - update._enter = enter; - update._exit = exit; - return update; -}; - -var selection_exit = function() { - return new Selection(this._exit || this._groups.map(sparse), this._parents); -}; - -var selection_merge = function(selection) { - - for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Selection(merges, this._parents); -}; - -var selection_order = function() { - - for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { - for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - - return this; -}; - -var selection_sort = function(compare) { - if (!compare) compare = ascending$2; - - function compareNode(a, b) { - return a && b ? compare(a.__data__, b.__data__) : !a - !b; - } - - for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group[i]) { - sortgroup[i] = node; - } - } - sortgroup.sort(compareNode); - } - - return new Selection(sortgroups, this._parents).order(); -}; - -function ascending$2(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -} - -var selection_call = function() { - var callback = arguments[0]; - arguments[0] = this; - callback.apply(null, arguments); - return this; -}; - -var selection_nodes = function() { - var nodes = new Array(this.size()), i = -1; - this.each(function() { nodes[++i] = this; }); - return nodes; -}; - -var selection_node = function() { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { - var node = group[i]; - if (node) return node; - } - } - - return null; -}; - -var selection_size = function() { - var size = 0; - this.each(function() { ++size; }); - return size; -}; - -var selection_empty = function() { - return !this.node(); -}; - -var selection_each = function(callback) { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { - if (node = group[i]) callback.call(node, node.__data__, i, group); - } - } - - return this; -}; - -function attrRemove(name) { - return function() { - this.removeAttribute(name); - }; -} - -function attrRemoveNS(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; -} - -function attrConstant(name, value) { - return function() { - this.setAttribute(name, value); - }; -} - -function attrConstantNS(fullname, value) { - return function() { - this.setAttributeNS(fullname.space, fullname.local, value); - }; -} - -function attrFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttribute(name); - else this.setAttribute(name, v); - }; -} - -function attrFunctionNS(fullname, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttributeNS(fullname.space, fullname.local); - else this.setAttributeNS(fullname.space, fullname.local, v); - }; -} - -var selection_attr = function(name, value) { - var fullname = namespace(name); - - if (arguments.length < 2) { - var node = this.node(); - return fullname.local - ? node.getAttributeNS(fullname.space, fullname.local) - : node.getAttribute(fullname); - } - - return this.each((value == null - ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" - ? (fullname.local ? attrFunctionNS : attrFunction) - : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); -}; - -var window = function(node) { - return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node - || (node.document && node) // node is a Window - || node.defaultView; // node is a Document -}; - -function styleRemove(name) { - return function() { - this.style.removeProperty(name); - }; -} - -function styleConstant(name, value, priority) { - return function() { - this.style.setProperty(name, value, priority); - }; -} - -function styleFunction(name, value, priority) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.style.removeProperty(name); - else this.style.setProperty(name, v, priority); - }; -} - -var selection_style = function(name, value, priority) { - var node; - return arguments.length > 1 - ? this.each((value == null - ? styleRemove : typeof value === "function" - ? styleFunction - : styleConstant)(name, value, priority == null ? "" : priority)) - : window(node = this.node()) - .getComputedStyle(node, null) - .getPropertyValue(name); -}; - -function propertyRemove(name) { - return function() { - delete this[name]; - }; -} - -function propertyConstant(name, value) { - return function() { - this[name] = value; - }; -} - -function propertyFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) delete this[name]; - else this[name] = v; - }; -} - -var selection_property = function(name, value) { - return arguments.length > 1 - ? this.each((value == null - ? propertyRemove : typeof value === "function" - ? propertyFunction - : propertyConstant)(name, value)) - : this.node()[name]; -}; - -function classArray(string) { - return string.trim().split(/^|\s+/); -} - -function classList(node) { - return node.classList || new ClassList(node); -} - -function ClassList(node) { - this._node = node; - this._names = classArray(node.getAttribute("class") || ""); -} - -ClassList.prototype = { - add: function(name) { - var i = this._names.indexOf(name); - if (i < 0) { - this._names.push(name); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - remove: function(name) { - var i = this._names.indexOf(name); - if (i >= 0) { - this._names.splice(i, 1); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - contains: function(name) { - return this._names.indexOf(name) >= 0; - } -}; - -function classedAdd(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.add(names[i]); -} - -function classedRemove(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.remove(names[i]); -} - -function classedTrue(names) { - return function() { - classedAdd(this, names); - }; -} - -function classedFalse(names) { - return function() { - classedRemove(this, names); - }; -} - -function classedFunction(names, value) { - return function() { - (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); - }; -} - -var selection_classed = function(name, value) { - var names = classArray(name + ""); - - if (arguments.length < 2) { - var list = classList(this.node()), i = -1, n = names.length; - while (++i < n) if (!list.contains(names[i])) return false; - return true; - } - - return this.each((typeof value === "function" - ? classedFunction : value - ? classedTrue - : classedFalse)(names, value)); -}; - -function textRemove() { - this.textContent = ""; -} - -function textConstant(value) { - return function() { - this.textContent = value; - }; -} - -function textFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - }; -} - -var selection_text = function(value) { - return arguments.length - ? this.each(value == null - ? textRemove : (typeof value === "function" - ? textFunction - : textConstant)(value)) - : this.node().textContent; -}; - -function htmlRemove() { - this.innerHTML = ""; -} - -function htmlConstant(value) { - return function() { - this.innerHTML = value; - }; -} - -function htmlFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - }; -} - -var selection_html = function(value) { - return arguments.length - ? this.each(value == null - ? htmlRemove : (typeof value === "function" - ? htmlFunction - : htmlConstant)(value)) - : this.node().innerHTML; -}; - -function raise$1() { - if (this.nextSibling) this.parentNode.appendChild(this); -} - -var selection_raise = function() { - return this.each(raise$1); -}; - -function lower() { - if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); -} - -var selection_lower = function() { - return this.each(lower); -}; - -var selection_append = function(name) { - var create = typeof name === "function" ? name : creator(name); - return this.select(function() { - return this.appendChild(create.apply(this, arguments)); - }); -}; - -function constantNull() { - return null; -} - -var selection_insert = function(name, before) { - var create = typeof name === "function" ? name : creator(name), - select = before == null ? constantNull : typeof before === "function" ? before : selector(before); - return this.select(function() { - return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); - }); -}; - -function remove() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); -} - -var selection_remove = function() { - return this.each(remove); -}; - -var selection_datum = function(value) { - return arguments.length - ? this.property("__data__", value) - : this.node().__data__; -}; - -function dispatchEvent(node, type, params) { - var window$$1 = window(node), - event = window$$1.CustomEvent; - - if (event) { - event = new event(type, params); - } else { - event = window$$1.document.createEvent("Event"); - if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; - else event.initEvent(type, false, false); - } - - node.dispatchEvent(event); -} - -function dispatchConstant(type, params) { - return function() { - return dispatchEvent(this, type, params); - }; -} - -function dispatchFunction(type, params) { - return function() { - return dispatchEvent(this, type, params.apply(this, arguments)); - }; -} - -var selection_dispatch = function(type, params) { - return this.each((typeof params === "function" - ? dispatchFunction - : dispatchConstant)(type, params)); -}; - -var root = [null]; - -function Selection(groups, parents) { - this._groups = groups; - this._parents = parents; -} - -function selection() { - return new Selection([[document.documentElement]], root); -} - -Selection.prototype = selection.prototype = { - constructor: Selection, - select: selection_select, - selectAll: selection_selectAll, - filter: selection_filter, - data: selection_data, - enter: selection_enter, - exit: selection_exit, - merge: selection_merge, - order: selection_order, - sort: selection_sort, - call: selection_call, - nodes: selection_nodes, - node: selection_node, - size: selection_size, - empty: selection_empty, - each: selection_each, - attr: selection_attr, - style: selection_style, - property: selection_property, - classed: selection_classed, - text: selection_text, - html: selection_html, - raise: selection_raise, - lower: selection_lower, - append: selection_append, - insert: selection_insert, - remove: selection_remove, - datum: selection_datum, - on: selection_on, - dispatch: selection_dispatch -}; - -var select = function(selector) { - return typeof selector === "string" - ? new Selection([[document.querySelector(selector)]], [document.documentElement]) - : new Selection([[selector]], root); -}; - -var selectAll = function(selector) { - return typeof selector === "string" - ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) - : new Selection([selector == null ? [] : selector], root); -}; - -var touch = function(node, touches, identifier) { - if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches; - - for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) { - if ((touch = touches[i]).identifier === identifier) { - return point$5(node, touch); - } - } - - return null; -}; - -var touches = function(node, touches) { - if (touches == null) touches = sourceEvent().touches; - - for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) { - points[i] = point$5(node, touches[i]); - } - - return points; -}; - -var emptyOn = dispatch("start", "end", "interrupt"); -var emptyTween = []; - -var CREATED = 0; -var SCHEDULED = 1; -var STARTING = 2; -var STARTED = 3; -var RUNNING = 4; -var ENDING = 5; -var ENDED = 6; - -var schedule = function(node, name, id, index, group, timing) { - var schedules = node.__transition; - if (!schedules) node.__transition = {}; - else if (id in schedules) return; - create(node, id, { - name: name, - index: index, // For context during callback. - group: group, // For context during callback. - on: emptyOn, - tween: emptyTween, - time: timing.time, - delay: timing.delay, - duration: timing.duration, - ease: timing.ease, - timer: null, - state: CREATED - }); -}; - -function init(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id]) || schedule.state > CREATED) throw new Error("too late"); - return schedule; -} - -function set$3(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id]) || schedule.state > STARTING) throw new Error("too late"); - return schedule; -} - -function get$1(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id])) throw new Error("too late"); - return schedule; -} - -function create(node, id, self) { - var schedules = node.__transition, - tween; - - // Initialize the self timer when the transition is created. - // Note the actual delay is not known until the first callback! - schedules[id] = self; - self.timer = timer(schedule, 0, self.time); - - function schedule(elapsed) { - self.state = SCHEDULED; - self.timer.restart(start, self.delay, self.time); - - // If the elapsed delay is less than our first sleep, start immediately. - if (self.delay <= elapsed) start(elapsed - self.delay); - } - - function start(elapsed) { - var i, j, n, o; - - // If the state is not SCHEDULED, then we previously errored on start. - if (self.state !== SCHEDULED) return stop(); - - for (i in schedules) { - o = schedules[i]; - if (o.name !== self.name) continue; - - // While this element already has a starting transition during this frame, - // defer starting an interrupting transition until that transition has a - // chance to tick (and possibly end); see d3/d3-transition#54! - if (o.state === STARTED) return timeout$1(start); - - // Interrupt the active transition, if any. - // Dispatch the interrupt event. - if (o.state === RUNNING) { - o.state = ENDED; - o.timer.stop(); - o.on.call("interrupt", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - - // Cancel any pre-empted transitions. No interrupt event is dispatched - // because the cancelled transitions never started. Note that this also - // removes this transition from the pending list! - else if (+i < id) { - o.state = ENDED; - o.timer.stop(); - delete schedules[i]; - } - } - - // Defer the first tick to end of the current frame; see d3/d3#1576. - // Note the transition may be canceled after start and before the first tick! - // Note this must be scheduled before the start event; see d3/d3-transition#16! - // Assuming this is successful, subsequent callbacks go straight to tick. - timeout$1(function() { - if (self.state === STARTED) { - self.state = RUNNING; - self.timer.restart(tick, self.delay, self.time); - tick(elapsed); - } - }); - - // Dispatch the start event. - // Note this must be done before the tween are initialized. - self.state = STARTING; - self.on.call("start", node, node.__data__, self.index, self.group); - if (self.state !== STARTING) return; // interrupted - self.state = STARTED; - - // Initialize the tween, deleting null tween. - tween = new Array(n = self.tween.length); - for (i = 0, j = -1; i < n; ++i) { - if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { - tween[++j] = o; - } - } - tween.length = j + 1; - } - - function tick(elapsed) { - var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), - i = -1, - n = tween.length; - - while (++i < n) { - tween[i].call(null, t); - } - - // Dispatch the end event. - if (self.state === ENDING) { - self.on.call("end", node, node.__data__, self.index, self.group); - stop(); - } - } - - function stop() { - self.state = ENDED; - self.timer.stop(); - delete schedules[id]; - for (var i in schedules) return; // eslint-disable-line no-unused-vars - delete node.__transition; - } -} - -var interrupt = function(node, name) { - var schedules = node.__transition, - schedule, - active, - empty = true, - i; - - if (!schedules) return; - - name = name == null ? null : name + ""; - - for (i in schedules) { - if ((schedule = schedules[i]).name !== name) { empty = false; continue; } - active = schedule.state > STARTING && schedule.state < ENDING; - schedule.state = ENDED; - schedule.timer.stop(); - if (active) schedule.on.call("interrupt", node, node.__data__, schedule.index, schedule.group); - delete schedules[i]; - } - - if (empty) delete node.__transition; -}; - -var selection_interrupt = function(name) { - return this.each(function() { - interrupt(this, name); - }); -}; - -function tweenRemove(id, name) { - var tween0, tween1; - return function() { - var schedule = set$3(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = tween0 = tween; - for (var i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1 = tween1.slice(); - tween1.splice(i, 1); - break; - } - } - } - - schedule.tween = tween1; - }; -} - -function tweenFunction(id, name, value) { - var tween0, tween1; - if (typeof value !== "function") throw new Error; - return function() { - var schedule = set$3(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = (tween0 = tween).slice(); - for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1[i] = t; - break; - } - } - if (i === n) tween1.push(t); - } - - schedule.tween = tween1; - }; -} - -var transition_tween = function(name, value) { - var id = this._id; - - name += ""; - - if (arguments.length < 2) { - var tween = get$1(this.node(), id).tween; - for (var i = 0, n = tween.length, t; i < n; ++i) { - if ((t = tween[i]).name === name) { - return t.value; - } - } - return null; - } - - return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); -}; - -function tweenValue(transition, name, value) { - var id = transition._id; - - transition.each(function() { - var schedule = set$3(this, id); - (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments); - }); - - return function(node) { - return get$1(node, id).value[name]; - }; -} - -var interpolate$1 = function(a, b) { - var c; - return (typeof b === "number" ? interpolateNumber - : b instanceof color ? interpolateRgb - : (c = color(b)) ? (b = c, interpolateRgb) - : interpolateString)(a, b); -}; - -function attrRemove$1(name) { - return function() { - this.removeAttribute(name); - }; -} - -function attrRemoveNS$1(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; -} - -function attrConstant$1(name, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = this.getAttribute(name); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function attrConstantNS$1(fullname, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = this.getAttributeNS(fullname.space, fullname.local); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function attrFunction$1(name, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var value0, value1 = value(this); - if (value1 == null) return void this.removeAttribute(name); - value0 = this.getAttribute(name); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -function attrFunctionNS$1(fullname, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var value0, value1 = value(this); - if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); - value0 = this.getAttributeNS(fullname.space, fullname.local); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -var transition_attr = function(name, value) { - var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1; - return this.attrTween(name, typeof value === "function" - ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) - : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) - : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value)); -}; - -function attrTweenNS(fullname, value) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.setAttributeNS(fullname.space, fullname.local, i(t)); - }; - } - tween._value = value; - return tween; -} - -function attrTween(name, value) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.setAttribute(name, i(t)); - }; - } - tween._value = value; - return tween; -} - -var transition_attrTween = function(name, value) { - var key = "attr." + name; - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - var fullname = namespace(name); - return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); -}; - -function delayFunction(id, value) { - return function() { - init(this, id).delay = +value.apply(this, arguments); - }; -} - -function delayConstant(id, value) { - return value = +value, function() { - init(this, id).delay = value; - }; -} - -var transition_delay = function(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? delayFunction - : delayConstant)(id, value)) - : get$1(this.node(), id).delay; -}; - -function durationFunction(id, value) { - return function() { - set$3(this, id).duration = +value.apply(this, arguments); - }; -} - -function durationConstant(id, value) { - return value = +value, function() { - set$3(this, id).duration = value; - }; -} - -var transition_duration = function(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? durationFunction - : durationConstant)(id, value)) - : get$1(this.node(), id).duration; -}; - -function easeConstant(id, value) { - if (typeof value !== "function") throw new Error; - return function() { - set$3(this, id).ease = value; - }; -} - -var transition_ease = function(value) { - var id = this._id; - - return arguments.length - ? this.each(easeConstant(id, value)) - : get$1(this.node(), id).ease; -}; - -var transition_filter = function(match) { - if (typeof match !== "function") match = matcher$1(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Transition(subgroups, this._parents, this._name, this._id); -}; - -var transition_merge = function(transition) { - if (transition._id !== this._id) throw new Error; - - for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Transition(merges, this._parents, this._name, this._id); -}; - -function start$1(name) { - return (name + "").trim().split(/^|\s+/).every(function(t) { - var i = t.indexOf("."); - if (i >= 0) t = t.slice(0, i); - return !t || t === "start"; - }); -} - -function onFunction(id, name, listener) { - var on0, on1, sit = start$1(name) ? init : set$3; - return function() { - var schedule = sit(this, id), - on = schedule.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); - - schedule.on = on1; - }; -} - -var transition_on = function(name, listener) { - var id = this._id; - - return arguments.length < 2 - ? get$1(this.node(), id).on.on(name) - : this.each(onFunction(id, name, listener)); -}; - -function removeFunction(id) { - return function() { - var parent = this.parentNode; - for (var i in this.__transition) if (+i !== id) return; - if (parent) parent.removeChild(this); - }; -} - -var transition_remove = function() { - return this.on("end.remove", removeFunction(this._id)); -}; - -var transition_select = function(select$$1) { - var name = this._name, - id = this._id; - - if (typeof select$$1 !== "function") select$$1 = selector(select$$1); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - schedule(subgroup[i], name, id, i, subgroup, get$1(node, id)); - } - } - } - - return new Transition(subgroups, this._parents, name, id); -}; - -var transition_selectAll = function(select$$1) { - var name = this._name, - id = this._id; - - if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) { - if (child = children[k]) { - schedule(child, name, id, k, children, inherit); - } - } - subgroups.push(children); - parents.push(node); - } - } - } - - return new Transition(subgroups, parents, name, id); -}; - -var Selection$1 = selection.prototype.constructor; - -var transition_selection = function() { - return new Selection$1(this._groups, this._parents); -}; - -function styleRemove$1(name, interpolate$$1) { - var value00, - value10, - interpolate0; - return function() { - var style = window(this).getComputedStyle(this, null), - value0 = style.getPropertyValue(name), - value1 = (this.style.removeProperty(name), style.getPropertyValue(name)); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -function styleRemoveEnd(name) { - return function() { - this.style.removeProperty(name); - }; -} - -function styleConstant$1(name, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = window(this).getComputedStyle(this, null).getPropertyValue(name); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function styleFunction$1(name, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var style = window(this).getComputedStyle(this, null), - value0 = style.getPropertyValue(name), - value1 = value(this); - if (value1 == null) value1 = (this.style.removeProperty(name), style.getPropertyValue(name)); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -var transition_style = function(name, value, priority) { - var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1; - return value == null ? this - .styleTween(name, styleRemove$1(name, i)) - .on("end.style." + name, styleRemoveEnd(name)) - : this.styleTween(name, typeof value === "function" - ? styleFunction$1(name, i, tweenValue(this, "style." + name, value)) - : styleConstant$1(name, i, value), priority); -}; - -function styleTween(name, value, priority) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.style.setProperty(name, i(t), priority); - }; - } - tween._value = value; - return tween; -} - -var transition_styleTween = function(name, value, priority) { - var key = "style." + (name += ""); - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); -}; - -function textConstant$1(value) { - return function() { - this.textContent = value; - }; -} - -function textFunction$1(value) { - return function() { - var value1 = value(this); - this.textContent = value1 == null ? "" : value1; - }; -} - -var transition_text = function(value) { - return this.tween("text", typeof value === "function" - ? textFunction$1(tweenValue(this, "text", value)) - : textConstant$1(value == null ? "" : value + "")); -}; - -var transition_transition = function() { - var name = this._name, - id0 = this._id, - id1 = newId(); - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - var inherit = get$1(node, id0); - schedule(node, name, id1, i, group, { - time: inherit.time + inherit.delay + inherit.duration, - delay: 0, - duration: inherit.duration, - ease: inherit.ease - }); - } - } - } - - return new Transition(groups, this._parents, name, id1); -}; - -var id = 0; - -function Transition(groups, parents, name, id) { - this._groups = groups; - this._parents = parents; - this._name = name; - this._id = id; -} - -function transition(name) { - return selection().transition(name); -} - -function newId() { - return ++id; -} - -var selection_prototype = selection.prototype; - -Transition.prototype = transition.prototype = { - constructor: Transition, - select: transition_select, - selectAll: transition_selectAll, - filter: transition_filter, - merge: transition_merge, - selection: transition_selection, - transition: transition_transition, - call: selection_prototype.call, - nodes: selection_prototype.nodes, - node: selection_prototype.node, - size: selection_prototype.size, - empty: selection_prototype.empty, - each: selection_prototype.each, - on: transition_on, - attr: transition_attr, - attrTween: transition_attrTween, - style: transition_style, - styleTween: transition_styleTween, - text: transition_text, - remove: transition_remove, - tween: transition_tween, - delay: transition_delay, - duration: transition_duration, - ease: transition_ease -}; - -var defaultTiming = { - time: null, // Set on use. - delay: 0, - duration: 250, - ease: cubicInOut -}; - -function inherit(node, id) { - var timing; - while (!(timing = node.__transition) || !(timing = timing[id])) { - if (!(node = node.parentNode)) { - return defaultTiming.time = now(), defaultTiming; - } - } - return timing; -} - -var selection_transition = function(name) { - var id, - timing; - - if (name instanceof Transition) { - id = name._id, name = name._name; - } else { - id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; - } - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - schedule(node, name, id, i, group, timing || inherit(node, id)); - } - } - } - - return new Transition(groups, this._parents, name, id); -}; - -selection.prototype.interrupt = selection_interrupt; -selection.prototype.transition = selection_transition; - -var root$1 = [null]; - -var active = function(node, name) { - var schedules = node.__transition, - schedule, - i; - - if (schedules) { - name = name == null ? null : name + ""; - for (i in schedules) { - if ((schedule = schedules[i]).state > SCHEDULED && schedule.name === name) { - return new Transition([[node]], root$1, name, +i); - } - } - } - - return null; -}; - -var slice$4 = Array.prototype.slice; - -var identity$5 = function(x) { - return x; -}; - -var top = 1; -var right = 2; -var bottom = 3; -var left = 4; -var epsilon$2 = 1e-6; - -function translateX(scale0, scale1, d) { - var x = scale0(d); - return "translate(" + (isFinite(x) ? x : scale1(d)) + ",0)"; -} - -function translateY(scale0, scale1, d) { - var y = scale0(d); - return "translate(0," + (isFinite(y) ? y : scale1(d)) + ")"; -} - -function center(scale) { - var offset = scale.bandwidth() / 2; - if (scale.round()) offset = Math.round(offset); - return function(d) { - return scale(d) + offset; - }; -} - -function entering() { - return !this.__axis; -} - -function axis(orient, scale) { - var tickArguments = [], - tickValues = null, - tickFormat = null, - tickSizeInner = 6, - tickSizeOuter = 6, - tickPadding = 3; - - function axis(context) { - var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues, - format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$5) : tickFormat, - spacing = Math.max(tickSizeInner, 0) + tickPadding, - transform = orient === top || orient === bottom ? translateX : translateY, - range = scale.range(), - range0 = range[0] + 0.5, - range1 = range[range.length - 1] + 0.5, - position = (scale.bandwidth ? center : identity$5)(scale.copy()), - selection = context.selection ? context.selection() : context, - path = selection.selectAll(".domain").data([null]), - tick = selection.selectAll(".tick").data(values, scale).order(), - tickExit = tick.exit(), - tickEnter = tick.enter().append("g").attr("class", "tick"), - line = tick.select("line"), - text = tick.select("text"), - k = orient === top || orient === left ? -1 : 1, - x, y = orient === left || orient === right ? (x = "x", "y") : (x = "y", "x"); - - path = path.merge(path.enter().insert("path", ".tick") - .attr("class", "domain") - .attr("stroke", "#000")); - - tick = tick.merge(tickEnter); - - line = line.merge(tickEnter.append("line") - .attr("stroke", "#000") - .attr(x + "2", k * tickSizeInner) - .attr(y + "1", 0.5) - .attr(y + "2", 0.5)); - - text = text.merge(tickEnter.append("text") - .attr("fill", "#000") - .attr(x, k * spacing) - .attr(y, 0.5) - .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em")); - - if (context !== selection) { - path = path.transition(context); - tick = tick.transition(context); - line = line.transition(context); - text = text.transition(context); - - tickExit = tickExit.transition(context) - .attr("opacity", epsilon$2) - .attr("transform", function(d) { return transform(position, this.parentNode.__axis || position, d); }); - - tickEnter - .attr("opacity", epsilon$2) - .attr("transform", function(d) { return transform(this.parentNode.__axis || position, position, d); }); - } - - tickExit.remove(); - - path - .attr("d", orient === left || orient == right - ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter - : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter); - - tick - .attr("opacity", 1) - .attr("transform", function(d) { return transform(position, position, d); }); - - line - .attr(x + "2", k * tickSizeInner); - - text - .attr(x, k * spacing) - .text(format); - - selection.filter(entering) - .attr("fill", "none") - .attr("font-size", 10) - .attr("font-family", "sans-serif") - .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle"); - - selection - .each(function() { this.__axis = position; }); - } - - axis.scale = function(_) { - return arguments.length ? (scale = _, axis) : scale; - }; - - axis.ticks = function() { - return tickArguments = slice$4.call(arguments), axis; - }; - - axis.tickArguments = function(_) { - return arguments.length ? (tickArguments = _ == null ? [] : slice$4.call(_), axis) : tickArguments.slice(); - }; - - axis.tickValues = function(_) { - return arguments.length ? (tickValues = _ == null ? null : slice$4.call(_), axis) : tickValues && tickValues.slice(); - }; - - axis.tickFormat = function(_) { - return arguments.length ? (tickFormat = _, axis) : tickFormat; - }; - - axis.tickSize = function(_) { - return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner; - }; - - axis.tickSizeInner = function(_) { - return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner; - }; - - axis.tickSizeOuter = function(_) { - return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter; - }; - - axis.tickPadding = function(_) { - return arguments.length ? (tickPadding = +_, axis) : tickPadding; - }; - - return axis; -} - -function axisTop(scale) { - return axis(top, scale); -} - -function axisRight(scale) { - return axis(right, scale); -} - -function axisBottom(scale) { - return axis(bottom, scale); -} - -function axisLeft(scale) { - return axis(left, scale); -} - -function defaultSeparation(a, b) { - return a.parent === b.parent ? 1 : 2; -} - -function meanX(children) { - return children.reduce(meanXReduce, 0) / children.length; -} - -function meanXReduce(x, c) { - return x + c.x; -} - -function maxY(children) { - return 1 + children.reduce(maxYReduce, 0); -} - -function maxYReduce(y, c) { - return Math.max(y, c.y); -} - -function leafLeft(node) { - var children; - while (children = node.children) node = children[0]; - return node; -} - -function leafRight(node) { - var children; - while (children = node.children) node = children[children.length - 1]; - return node; -} - -var cluster = function() { - var separation = defaultSeparation, - dx = 1, - dy = 1, - nodeSize = false; - - function cluster(root) { - var previousNode, - x = 0; - - // First walk, computing the initial x & y values. - root.eachAfter(function(node) { - var children = node.children; - if (children) { - node.x = meanX(children); - node.y = maxY(children); - } else { - node.x = previousNode ? x += separation(node, previousNode) : 0; - node.y = 0; - previousNode = node; - } - }); - - var left = leafLeft(root), - right = leafRight(root), - x0 = left.x - separation(left, right) / 2, - x1 = right.x + separation(right, left) / 2; - - // Second walk, normalizing x & y to the desired size. - return root.eachAfter(nodeSize ? function(node) { - node.x = (node.x - root.x) * dx; - node.y = (root.y - node.y) * dy; - } : function(node) { - node.x = (node.x - x0) / (x1 - x0) * dx; - node.y = (1 - (root.y ? node.y / root.y : 1)) * dy; - }); - } - - cluster.separation = function(x) { - return arguments.length ? (separation = x, cluster) : separation; - }; - - cluster.size = function(x) { - return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]); - }; - - cluster.nodeSize = function(x) { - return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null); - }; - - return cluster; -}; - -var node_each = function(callback) { - var node = this, current, next = [node], children, i, n; - do { - current = next.reverse(), next = []; - while (node = current.pop()) { - callback(node), children = node.children; - if (children) for (i = 0, n = children.length; i < n; ++i) { - next.push(children[i]); - } - } - } while (next.length); - return this; -}; - -var node_eachBefore = function(callback) { - var node = this, nodes = [node], children, i; - while (node = nodes.pop()) { - callback(node), children = node.children; - if (children) for (i = children.length - 1; i >= 0; --i) { - nodes.push(children[i]); - } - } - return this; -}; - -var node_eachAfter = function(callback) { - var node = this, nodes = [node], next = [], children, i, n; - while (node = nodes.pop()) { - next.push(node), children = node.children; - if (children) for (i = 0, n = children.length; i < n; ++i) { - nodes.push(children[i]); - } - } - while (node = next.pop()) { - callback(node); - } - return this; -}; - -var node_sum = function(value) { - return this.eachAfter(function(node) { - var sum = +value(node.data) || 0, - children = node.children, - i = children && children.length; - while (--i >= 0) sum += children[i].value; - node.value = sum; - }); -}; - -var node_sort = function(compare) { - return this.eachBefore(function(node) { - if (node.children) { - node.children.sort(compare); - } - }); -}; - -var node_path = function(end) { - var start = this, - ancestor = leastCommonAncestor(start, end), - nodes = [start]; - while (start !== ancestor) { - start = start.parent; - nodes.push(start); - } - var k = nodes.length; - while (end !== ancestor) { - nodes.splice(k, 0, end); - end = end.parent; - } - return nodes; -}; - -function leastCommonAncestor(a, b) { - if (a === b) return a; - var aNodes = a.ancestors(), - bNodes = b.ancestors(), - c = null; - a = aNodes.pop(); - b = bNodes.pop(); - while (a === b) { - c = a; - a = aNodes.pop(); - b = bNodes.pop(); - } - return c; -} - -var node_ancestors = function() { - var node = this, nodes = [node]; - while (node = node.parent) { - nodes.push(node); - } - return nodes; -}; - -var node_descendants = function() { - var nodes = []; - this.each(function(node) { - nodes.push(node); - }); - return nodes; -}; - -var node_leaves = function() { - var leaves = []; - this.eachBefore(function(node) { - if (!node.children) { - leaves.push(node); - } - }); - return leaves; -}; - -var node_links = function() { - var root = this, links = []; - root.each(function(node) { - if (node !== root) { // Don’t include the root’s parent, if any. - links.push({source: node.parent, target: node}); - } - }); - return links; -}; - -function hierarchy(data, children) { - var root = new Node(data), - valued = +data.value && (root.value = data.value), - node, - nodes = [root], - child, - childs, - i, - n; - - if (children == null) children = defaultChildren; - - while (node = nodes.pop()) { - if (valued) node.value = +node.data.value; - if ((childs = children(node.data)) && (n = childs.length)) { - node.children = new Array(n); - for (i = n - 1; i >= 0; --i) { - nodes.push(child = node.children[i] = new Node(childs[i])); - child.parent = node; - child.depth = node.depth + 1; - } - } - } - - return root.eachBefore(computeHeight); -} - -function node_copy() { - return hierarchy(this).eachBefore(copyData); -} - -function defaultChildren(d) { - return d.children; -} - -function copyData(node) { - node.data = node.data.data; -} - -function computeHeight(node) { - var height = 0; - do node.height = height; - while ((node = node.parent) && (node.height < ++height)); -} - -function Node(data) { - this.data = data; - this.depth = - this.height = 0; - this.parent = null; -} - -Node.prototype = hierarchy.prototype = { - constructor: Node, - each: node_each, - eachAfter: node_eachAfter, - eachBefore: node_eachBefore, - sum: node_sum, - sort: node_sort, - path: node_path, - ancestors: node_ancestors, - descendants: node_descendants, - leaves: node_leaves, - links: node_links, - copy: node_copy -}; - -function Node$2(value) { - this._ = value; - this.next = null; -} - -var shuffle$1 = function(array) { - var i, - n = (array = array.slice()).length, - head = null, - node = head; - - while (n) { - var next = new Node$2(array[n - 1]); - if (node) node = node.next = next; - else node = head = next; - array[i] = array[--n]; - } - - return { - head: head, - tail: node - }; -}; - -var enclose = function(circles) { - return encloseN(shuffle$1(circles), []); -}; - -function encloses(a, b) { - var dx = b.x - a.x, - dy = b.y - a.y, - dr = a.r - b.r; - return dr * dr + 1e-6 > dx * dx + dy * dy; -} - -// Returns the smallest circle that contains circles L and intersects circles B. -function encloseN(L, B) { - var circle, - l0 = null, - l1 = L.head, - l2, - p1; - - switch (B.length) { - case 1: circle = enclose1(B[0]); break; - case 2: circle = enclose2(B[0], B[1]); break; - case 3: circle = enclose3(B[0], B[1], B[2]); break; - } - - while (l1) { - p1 = l1._, l2 = l1.next; - if (!circle || !encloses(circle, p1)) { - - // Temporarily truncate L before l1. - if (l0) L.tail = l0, l0.next = null; - else L.head = L.tail = null; - - B.push(p1); - circle = encloseN(L, B); // Note: reorders L! - B.pop(); - - // Move l1 to the front of L and reconnect the truncated list L. - if (L.head) l1.next = L.head, L.head = l1; - else l1.next = null, L.head = L.tail = l1; - l0 = L.tail, l0.next = l2; - - } else { - l0 = l1; - } - l1 = l2; - } - - L.tail = l0; - return circle; -} - -function enclose1(a) { - return { - x: a.x, - y: a.y, - r: a.r - }; -} - -function enclose2(a, b) { - var x1 = a.x, y1 = a.y, r1 = a.r, - x2 = b.x, y2 = b.y, r2 = b.r, - x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1, - l = Math.sqrt(x21 * x21 + y21 * y21); - return { - x: (x1 + x2 + x21 / l * r21) / 2, - y: (y1 + y2 + y21 / l * r21) / 2, - r: (l + r1 + r2) / 2 - }; -} - -function enclose3(a, b, c) { - var x1 = a.x, y1 = a.y, r1 = a.r, - x2 = b.x, y2 = b.y, r2 = b.r, - x3 = c.x, y3 = c.y, r3 = c.r, - a2 = 2 * (x1 - x2), - b2 = 2 * (y1 - y2), - c2 = 2 * (r2 - r1), - d2 = x1 * x1 + y1 * y1 - r1 * r1 - x2 * x2 - y2 * y2 + r2 * r2, - a3 = 2 * (x1 - x3), - b3 = 2 * (y1 - y3), - c3 = 2 * (r3 - r1), - d3 = x1 * x1 + y1 * y1 - r1 * r1 - x3 * x3 - y3 * y3 + r3 * r3, - ab = a3 * b2 - a2 * b3, - xa = (b2 * d3 - b3 * d2) / ab - x1, - xb = (b3 * c2 - b2 * c3) / ab, - ya = (a3 * d2 - a2 * d3) / ab - y1, - yb = (a2 * c3 - a3 * c2) / ab, - A = xb * xb + yb * yb - 1, - B = 2 * (xa * xb + ya * yb + r1), - C = xa * xa + ya * ya - r1 * r1, - r = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A); - return { - x: xa + xb * r + x1, - y: ya + yb * r + y1, - r: r - }; -} - -function place(a, b, c) { - var ax = a.x, - ay = a.y, - da = b.r + c.r, - db = a.r + c.r, - dx = b.x - ax, - dy = b.y - ay, - dc = dx * dx + dy * dy; - if (dc) { - var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc), - y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); - c.x = ax + x * dx + y * dy; - c.y = ay + x * dy - y * dx; - } else { - c.x = ax + db; - c.y = ay; - } -} - -function intersects(a, b) { - var dx = b.x - a.x, - dy = b.y - a.y, - dr = a.r + b.r; - return dr * dr > dx * dx + dy * dy; -} - -function distance2(circle, x, y) { - var dx = circle.x - x, - dy = circle.y - y; - return dx * dx + dy * dy; -} - -function Node$1(circle) { - this._ = circle; - this.next = null; - this.previous = null; -} - -function packEnclose(circles) { - if (!(n = circles.length)) return 0; - - var a, b, c, n; - - // Place the first circle. - a = circles[0], a.x = 0, a.y = 0; - if (!(n > 1)) return a.r; - - // Place the second circle. - b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0; - if (!(n > 2)) return a.r + b.r; - - // Place the third circle. - place(b, a, c = circles[2]); - - // Initialize the weighted centroid. - var aa = a.r * a.r, - ba = b.r * b.r, - ca = c.r * c.r, - oa = aa + ba + ca, - ox = aa * a.x + ba * b.x + ca * c.x, - oy = aa * a.y + ba * b.y + ca * c.y, - cx, cy, i, j, k, sj, sk; - - // Initialize the front-chain using the first three circles a, b and c. - a = new Node$1(a), b = new Node$1(b), c = new Node$1(c); - a.next = c.previous = b; - b.next = a.previous = c; - c.next = b.previous = a; - - // Attempt to place each remaining circle… - pack: for (i = 3; i < n; ++i) { - place(a._, b._, c = circles[i]), c = new Node$1(c); - - // If there are only three elements in the front-chain… - if ((k = a.previous) === (j = b.next)) { - // If the new circle intersects the third circle, - // rotate the front chain to try the next position. - if (intersects(j._, c._)) { - a = b, b = j, --i; - continue pack; - } - } - - // Find the closest intersecting circle on the front-chain, if any. - else { - sj = j._.r, sk = k._.r; - do { - if (sj <= sk) { - if (intersects(j._, c._)) { - b = j, a.next = b, b.previous = a, --i; - continue pack; - } - j = j.next, sj += j._.r; - } else { - if (intersects(k._, c._)) { - a = k, a.next = b, b.previous = a, --i; - continue pack; - } - k = k.previous, sk += k._.r; - } - } while (j !== k.next); - } - - // Success! Insert the new circle c between a and b. - c.previous = a, c.next = b, a.next = b.previous = b = c; - - // Update the weighted centroid. - oa += ca = c._.r * c._.r; - ox += ca * c._.x; - oy += ca * c._.y; - - // Compute the new closest circle a to centroid. - aa = distance2(a._, cx = ox / oa, cy = oy / oa); - while ((c = c.next) !== b) { - if ((ca = distance2(c._, cx, cy)) < aa) { - a = c, aa = ca; - } - } - b = a.next; - } - - // Compute the enclosing circle of the front chain. - a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a); - - // Translate the circles to put the enclosing circle around the origin. - for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y; - - return c.r; -} - -var siblings = function(circles) { - packEnclose(circles); - return circles; -}; - -function optional(f) { - return f == null ? null : required(f); -} - -function required(f) { - if (typeof f !== "function") throw new Error; - return f; -} - -function constantZero() { - return 0; -} - -var constant$6 = function(x) { - return function() { - return x; - }; -}; - -function defaultRadius(d) { - return Math.sqrt(d.value); -} - -var index = function() { - var radius = null, - dx = 1, - dy = 1, - padding = constantZero; - - function pack(root) { - root.x = dx / 2, root.y = dy / 2; - if (radius) { - root.eachBefore(radiusLeaf(radius)) - .eachAfter(packChildren(padding, 0.5)) - .eachBefore(translateChild(1)); - } else { - root.eachBefore(radiusLeaf(defaultRadius)) - .eachAfter(packChildren(constantZero, 1)) - .eachAfter(packChildren(padding, root.r / Math.min(dx, dy))) - .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r))); - } - return root; - } - - pack.radius = function(x) { - return arguments.length ? (radius = optional(x), pack) : radius; - }; - - pack.size = function(x) { - return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy]; - }; - - pack.padding = function(x) { - return arguments.length ? (padding = typeof x === "function" ? x : constant$6(+x), pack) : padding; - }; - - return pack; -}; - -function radiusLeaf(radius) { - return function(node) { - if (!node.children) { - node.r = Math.max(0, +radius(node) || 0); - } - }; -} - -function packChildren(padding, k) { - return function(node) { - if (children = node.children) { - var children, - i, - n = children.length, - r = padding(node) * k || 0, - e; - - if (r) for (i = 0; i < n; ++i) children[i].r += r; - e = packEnclose(children); - if (r) for (i = 0; i < n; ++i) children[i].r -= r; - node.r = e + r; - } - }; -} - -function translateChild(k) { - return function(node) { - var parent = node.parent; - node.r *= k; - if (parent) { - node.x = parent.x + k * node.x; - node.y = parent.y + k * node.y; - } - }; -} - -var roundNode = function(node) { - node.x0 = Math.round(node.x0); - node.y0 = Math.round(node.y0); - node.x1 = Math.round(node.x1); - node.y1 = Math.round(node.y1); -}; - -var treemapDice = function(parent, x0, y0, x1, y1) { - var nodes = parent.children, - node, - i = -1, - n = nodes.length, - k = parent.value && (x1 - x0) / parent.value; - - while (++i < n) { - node = nodes[i], node.y0 = y0, node.y1 = y1; - node.x0 = x0, node.x1 = x0 += node.value * k; - } -}; - -var partition = function() { - var dx = 1, - dy = 1, - padding = 0, - round = false; - - function partition(root) { - var n = root.height + 1; - root.x0 = - root.y0 = padding; - root.x1 = dx; - root.y1 = dy / n; - root.eachBefore(positionNode(dy, n)); - if (round) root.eachBefore(roundNode); - return root; - } - - function positionNode(dy, n) { - return function(node) { - if (node.children) { - treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n); - } - var x0 = node.x0, - y0 = node.y0, - x1 = node.x1 - padding, - y1 = node.y1 - padding; - if (x1 < x0) x0 = x1 = (x0 + x1) / 2; - if (y1 < y0) y0 = y1 = (y0 + y1) / 2; - node.x0 = x0; - node.y0 = y0; - node.x1 = x1; - node.y1 = y1; - }; - } - - partition.round = function(x) { - return arguments.length ? (round = !!x, partition) : round; - }; - - partition.size = function(x) { - return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy]; - }; - - partition.padding = function(x) { - return arguments.length ? (padding = +x, partition) : padding; - }; - - return partition; -}; - -var keyPrefix$1 = "$"; -var preroot = {depth: -1}; -var ambiguous = {}; - -function defaultId(d) { - return d.id; -} - -function defaultParentId(d) { - return d.parentId; -} - -var stratify = function() { - var id = defaultId, - parentId = defaultParentId; - - function stratify(data) { - var d, - i, - n = data.length, - root, - parent, - node, - nodes = new Array(n), - nodeId, - nodeKey, - nodeByKey = {}; - - for (i = 0; i < n; ++i) { - d = data[i], node = nodes[i] = new Node(d); - if ((nodeId = id(d, i, data)) != null && (nodeId += "")) { - nodeKey = keyPrefix$1 + (node.id = nodeId); - nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node; - } - } - - for (i = 0; i < n; ++i) { - node = nodes[i], nodeId = parentId(data[i], i, data); - if (nodeId == null || !(nodeId += "")) { - if (root) throw new Error("multiple roots"); - root = node; - } else { - parent = nodeByKey[keyPrefix$1 + nodeId]; - if (!parent) throw new Error("missing: " + nodeId); - if (parent === ambiguous) throw new Error("ambiguous: " + nodeId); - if (parent.children) parent.children.push(node); - else parent.children = [node]; - node.parent = parent; - } - } - - if (!root) throw new Error("no root"); - root.parent = preroot; - root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight); - root.parent = null; - if (n > 0) throw new Error("cycle"); - - return root; - } - - stratify.id = function(x) { - return arguments.length ? (id = required(x), stratify) : id; - }; - - stratify.parentId = function(x) { - return arguments.length ? (parentId = required(x), stratify) : parentId; - }; - - return stratify; -}; - -function defaultSeparation$1(a, b) { - return a.parent === b.parent ? 1 : 2; -} - -// function radialSeparation(a, b) { -// return (a.parent === b.parent ? 1 : 2) / a.depth; -// } - -// This function is used to traverse the left contour of a subtree (or -// subforest). It returns the successor of v on this contour. This successor is -// either given by the leftmost child of v or by the thread of v. The function -// returns null if and only if v is on the highest level of its subtree. -function nextLeft(v) { - var children = v.children; - return children ? children[0] : v.t; -} - -// This function works analogously to nextLeft. -function nextRight(v) { - var children = v.children; - return children ? children[children.length - 1] : v.t; -} - -// Shifts the current subtree rooted at w+. This is done by increasing -// prelim(w+) and mod(w+) by shift. -function moveSubtree(wm, wp, shift) { - var change = shift / (wp.i - wm.i); - wp.c -= change; - wp.s += shift; - wm.c += change; - wp.z += shift; - wp.m += shift; -} - -// All other shifts, applied to the smaller subtrees between w- and w+, are -// performed by this function. To prepare the shifts, we have to adjust -// change(w+), shift(w+), and change(w-). -function executeShifts(v) { - var shift = 0, - change = 0, - children = v.children, - i = children.length, - w; - while (--i >= 0) { - w = children[i]; - w.z += shift; - w.m += shift; - shift += w.s + (change += w.c); - } -} - -// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise, -// returns the specified (default) ancestor. -function nextAncestor(vim, v, ancestor) { - return vim.a.parent === v.parent ? vim.a : ancestor; -} - -function TreeNode(node, i) { - this._ = node; - this.parent = null; - this.children = null; - this.A = null; // default ancestor - this.a = this; // ancestor - this.z = 0; // prelim - this.m = 0; // mod - this.c = 0; // change - this.s = 0; // shift - this.t = null; // thread - this.i = i; // number -} - -TreeNode.prototype = Object.create(Node.prototype); - -function treeRoot(root) { - var tree = new TreeNode(root, 0), - node, - nodes = [tree], - child, - children, - i, - n; - - while (node = nodes.pop()) { - if (children = node._.children) { - node.children = new Array(n = children.length); - for (i = n - 1; i >= 0; --i) { - nodes.push(child = node.children[i] = new TreeNode(children[i], i)); - child.parent = node; - } - } - } - - (tree.parent = new TreeNode(null, 0)).children = [tree]; - return tree; -} - -// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm -var tree = function() { - var separation = defaultSeparation$1, - dx = 1, - dy = 1, - nodeSize = null; - - function tree(root) { - var t = treeRoot(root); - - // Compute the layout using Buchheim et al.’s algorithm. - t.eachAfter(firstWalk), t.parent.m = -t.z; - t.eachBefore(secondWalk); - - // If a fixed node size is specified, scale x and y. - if (nodeSize) root.eachBefore(sizeNode); - - // If a fixed tree size is specified, scale x and y based on the extent. - // Compute the left-most, right-most, and depth-most nodes for extents. - else { - var left = root, - right = root, - bottom = root; - root.eachBefore(function(node) { - if (node.x < left.x) left = node; - if (node.x > right.x) right = node; - if (node.depth > bottom.depth) bottom = node; - }); - var s = left === right ? 1 : separation(left, right) / 2, - tx = s - left.x, - kx = dx / (right.x + s + tx), - ky = dy / (bottom.depth || 1); - root.eachBefore(function(node) { - node.x = (node.x + tx) * kx; - node.y = node.depth * ky; - }); - } - - return root; - } - - // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is - // applied recursively to the children of v, as well as the function - // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the - // node v is placed to the midpoint of its outermost children. - function firstWalk(v) { - var children = v.children, - siblings = v.parent.children, - w = v.i ? siblings[v.i - 1] : null; - if (children) { - executeShifts(v); - var midpoint = (children[0].z + children[children.length - 1].z) / 2; - if (w) { - v.z = w.z + separation(v._, w._); - v.m = v.z - midpoint; - } else { - v.z = midpoint; - } - } else if (w) { - v.z = w.z + separation(v._, w._); - } - v.parent.A = apportion(v, w, v.parent.A || siblings[0]); - } - - // Computes all real x-coordinates by summing up the modifiers recursively. - function secondWalk(v) { - v._.x = v.z + v.parent.m; - v.m += v.parent.m; - } - - // The core of the algorithm. Here, a new subtree is combined with the - // previous subtrees. Threads are used to traverse the inside and outside - // contours of the left and right subtree up to the highest common level. The - // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the - // superscript o means outside and i means inside, the subscript - means left - // subtree and + means right subtree. For summing up the modifiers along the - // contour, we use respective variables si+, si-, so-, and so+. Whenever two - // nodes of the inside contours conflict, we compute the left one of the - // greatest uncommon ancestors using the function ANCESTOR and call MOVE - // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees. - // Finally, we add a new thread (if necessary). - function apportion(v, w, ancestor) { - if (w) { - var vip = v, - vop = v, - vim = w, - vom = vip.parent.children[0], - sip = vip.m, - sop = vop.m, - sim = vim.m, - som = vom.m, - shift; - while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) { - vom = nextLeft(vom); - vop = nextRight(vop); - vop.a = v; - shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); - if (shift > 0) { - moveSubtree(nextAncestor(vim, v, ancestor), v, shift); - sip += shift; - sop += shift; - } - sim += vim.m; - sip += vip.m; - som += vom.m; - sop += vop.m; - } - if (vim && !nextRight(vop)) { - vop.t = vim; - vop.m += sim - sop; - } - if (vip && !nextLeft(vom)) { - vom.t = vip; - vom.m += sip - som; - ancestor = v; - } - } - return ancestor; - } - - function sizeNode(node) { - node.x *= dx; - node.y = node.depth * dy; - } - - tree.separation = function(x) { - return arguments.length ? (separation = x, tree) : separation; - }; - - tree.size = function(x) { - return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]); - }; - - tree.nodeSize = function(x) { - return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null); - }; - - return tree; -}; - -var treemapSlice = function(parent, x0, y0, x1, y1) { - var nodes = parent.children, - node, - i = -1, - n = nodes.length, - k = parent.value && (y1 - y0) / parent.value; - - while (++i < n) { - node = nodes[i], node.x0 = x0, node.x1 = x1; - node.y0 = y0, node.y1 = y0 += node.value * k; - } -}; - -var phi = (1 + Math.sqrt(5)) / 2; - -function squarifyRatio(ratio, parent, x0, y0, x1, y1) { - var rows = [], - nodes = parent.children, - row, - nodeValue, - i0 = 0, - i1, - n = nodes.length, - dx, dy, - value = parent.value, - sumValue, - minValue, - maxValue, - newRatio, - minRatio, - alpha, - beta; - - while (i0 < n) { - dx = x1 - x0, dy = y1 - y0; - minValue = maxValue = sumValue = nodes[i0].value; - alpha = Math.max(dy / dx, dx / dy) / (value * ratio); - beta = sumValue * sumValue * alpha; - minRatio = Math.max(maxValue / beta, beta / minValue); - - // Keep adding nodes while the aspect ratio maintains or improves. - for (i1 = i0 + 1; i1 < n; ++i1) { - sumValue += nodeValue = nodes[i1].value; - if (nodeValue < minValue) minValue = nodeValue; - if (nodeValue > maxValue) maxValue = nodeValue; - beta = sumValue * sumValue * alpha; - newRatio = Math.max(maxValue / beta, beta / minValue); - if (newRatio > minRatio) { sumValue -= nodeValue; break; } - minRatio = newRatio; - } - - // Position and record the row orientation. - rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)}); - if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1); - else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1); - value -= sumValue, i0 = i1; - } - - return rows; -} - -var squarify = (function custom(ratio) { - - function squarify(parent, x0, y0, x1, y1) { - squarifyRatio(ratio, parent, x0, y0, x1, y1); - } - - squarify.ratio = function(x) { - return custom((x = +x) > 1 ? x : 1); - }; - - return squarify; -})(phi); - -var index$1 = function() { - var tile = squarify, - round = false, - dx = 1, - dy = 1, - paddingStack = [0], - paddingInner = constantZero, - paddingTop = constantZero, - paddingRight = constantZero, - paddingBottom = constantZero, - paddingLeft = constantZero; - - function treemap(root) { - root.x0 = - root.y0 = 0; - root.x1 = dx; - root.y1 = dy; - root.eachBefore(positionNode); - paddingStack = [0]; - if (round) root.eachBefore(roundNode); - return root; - } - - function positionNode(node) { - var p = paddingStack[node.depth], - x0 = node.x0 + p, - y0 = node.y0 + p, - x1 = node.x1 - p, - y1 = node.y1 - p; - if (x1 < x0) x0 = x1 = (x0 + x1) / 2; - if (y1 < y0) y0 = y1 = (y0 + y1) / 2; - node.x0 = x0; - node.y0 = y0; - node.x1 = x1; - node.y1 = y1; - if (node.children) { - p = paddingStack[node.depth + 1] = paddingInner(node) / 2; - x0 += paddingLeft(node) - p; - y0 += paddingTop(node) - p; - x1 -= paddingRight(node) - p; - y1 -= paddingBottom(node) - p; - if (x1 < x0) x0 = x1 = (x0 + x1) / 2; - if (y1 < y0) y0 = y1 = (y0 + y1) / 2; - tile(node, x0, y0, x1, y1); - } - } - - treemap.round = function(x) { - return arguments.length ? (round = !!x, treemap) : round; - }; - - treemap.size = function(x) { - return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy]; - }; - - treemap.tile = function(x) { - return arguments.length ? (tile = required(x), treemap) : tile; - }; - - treemap.padding = function(x) { - return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner(); - }; - - treemap.paddingInner = function(x) { - return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$6(+x), treemap) : paddingInner; - }; - - treemap.paddingOuter = function(x) { - return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop(); - }; - - treemap.paddingTop = function(x) { - return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$6(+x), treemap) : paddingTop; - }; - - treemap.paddingRight = function(x) { - return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$6(+x), treemap) : paddingRight; - }; - - treemap.paddingBottom = function(x) { - return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$6(+x), treemap) : paddingBottom; - }; - - treemap.paddingLeft = function(x) { - return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$6(+x), treemap) : paddingLeft; - }; - - return treemap; -}; - -var binary = function(parent, x0, y0, x1, y1) { - var nodes = parent.children, - i, n = nodes.length, - sum, sums = new Array(n + 1); - - for (sums[0] = sum = i = 0; i < n; ++i) { - sums[i + 1] = sum += nodes[i].value; - } - - partition(0, n, parent.value, x0, y0, x1, y1); - - function partition(i, j, value, x0, y0, x1, y1) { - if (i >= j - 1) { - var node = nodes[i]; - node.x0 = x0, node.y0 = y0; - node.x1 = x1, node.y1 = y1; - return; - } - - var valueOffset = sums[i], - valueTarget = (value / 2) + valueOffset, - k = i + 1, - hi = j - 1; - - while (k < hi) { - var mid = k + hi >>> 1; - if (sums[mid] < valueTarget) k = mid + 1; - else hi = mid; - } - - var valueLeft = sums[k] - valueOffset, - valueRight = value - valueLeft; - - if ((y1 - y0) > (x1 - x0)) { - var yk = (y0 * valueRight + y1 * valueLeft) / value; - partition(i, k, valueLeft, x0, y0, x1, yk); - partition(k, j, valueRight, x0, yk, x1, y1); - } else { - var xk = (x0 * valueRight + x1 * valueLeft) / value; - partition(i, k, valueLeft, x0, y0, xk, y1); - partition(k, j, valueRight, xk, y0, x1, y1); - } - } -}; - -var sliceDice = function(parent, x0, y0, x1, y1) { - (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1); -}; - -var resquarify = (function custom(ratio) { - - function resquarify(parent, x0, y0, x1, y1) { - if ((rows = parent._squarify) && (rows.ratio === ratio)) { - var rows, - row, - nodes, - i, - j = -1, - n, - m = rows.length, - value = parent.value; - - while (++j < m) { - row = rows[j], nodes = row.children; - for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value; - if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value); - else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1); - value -= row.value; - } - } else { - parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1); - rows.ratio = ratio; - } - } - - resquarify.ratio = function(x) { - return custom((x = +x) > 1 ? x : 1); - }; - - return resquarify; -})(phi); - -var center$1 = function(x, y) { - var nodes; - - if (x == null) x = 0; - if (y == null) y = 0; - - function force() { - var i, - n = nodes.length, - node, - sx = 0, - sy = 0; - - for (i = 0; i < n; ++i) { - node = nodes[i], sx += node.x, sy += node.y; - } - - for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { - node = nodes[i], node.x -= sx, node.y -= sy; - } - } - - force.initialize = function(_) { - nodes = _; - }; - - force.x = function(_) { - return arguments.length ? (x = +_, force) : x; - }; - - force.y = function(_) { - return arguments.length ? (y = +_, force) : y; - }; - - return force; -}; - -var constant$7 = function(x) { - return function() { - return x; - }; -}; - -var jiggle = function() { - return (Math.random() - 0.5) * 1e-6; -}; - -function x$1(d) { - return d.x + d.vx; -} - -function y$1(d) { - return d.y + d.vy; -} - -var collide = function(radius) { - var nodes, - radii, - strength = 1, - iterations = 1; - - if (typeof radius !== "function") radius = constant$7(radius == null ? 1 : +radius); - - function force() { - var i, n = nodes.length, - tree, - node, - xi, - yi, - ri, - ri2; - - for (var k = 0; k < iterations; ++k) { - tree = quadtree(nodes, x$1, y$1).visitAfter(prepare); - for (i = 0; i < n; ++i) { - node = nodes[i]; - ri = radii[i], ri2 = ri * ri; - xi = node.x + node.vx; - yi = node.y + node.vy; - tree.visit(apply); - } - } - - function apply(quad, x0, y0, x1, y1) { - var data = quad.data, rj = quad.r, r = ri + rj; - if (data) { - if (data.index > i) { - var x = xi - data.x - data.vx, - y = yi - data.y - data.vy, - l = x * x + y * y; - if (l < r * r) { - if (x === 0) x = jiggle(), l += x * x; - if (y === 0) y = jiggle(), l += y * y; - l = (r - (l = Math.sqrt(l))) / l * strength; - node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); - node.vy += (y *= l) * r; - data.vx -= x * (r = 1 - r); - data.vy -= y * r; - } - } - return; - } - return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; - } - } - - function prepare(quad) { - if (quad.data) return quad.r = radii[quad.data.index]; - for (var i = quad.r = 0; i < 4; ++i) { - if (quad[i] && quad[i].r > quad.r) { - quad.r = quad[i].r; - } - } - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length; - radii = new Array(n); - for (i = 0; i < n; ++i) radii[i] = +radius(nodes[i], i, nodes); - } - - force.initialize = function(_) { - nodes = _; - initialize(); - }; - - force.iterations = function(_) { - return arguments.length ? (iterations = +_, force) : iterations; - }; - - force.strength = function(_) { - return arguments.length ? (strength = +_, force) : strength; - }; - - force.radius = function(_) { - return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius; - }; - - return force; -}; - -function index$2(d, i) { - return i; -} - -function find(nodeById, nodeId) { - var node = nodeById.get(nodeId); - if (!node) throw new Error("missing: " + nodeId); - return node; -} - -var link = function(links) { - var id = index$2, - strength = defaultStrength, - strengths, - distance = constant$7(30), - distances, - nodes, - count, - bias, - iterations = 1; - - if (links == null) links = []; - - function defaultStrength(link) { - return 1 / Math.min(count[link.source.index], count[link.target.index]); - } - - function force(alpha) { - for (var k = 0, n = links.length; k < iterations; ++k) { - for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { - link = links[i], source = link.source, target = link.target; - x = target.x + target.vx - source.x - source.vx || jiggle(); - y = target.y + target.vy - source.y - source.vy || jiggle(); - l = Math.sqrt(x * x + y * y); - l = (l - distances[i]) / l * alpha * strengths[i]; - x *= l, y *= l; - target.vx -= x * (b = bias[i]); - target.vy -= y * b; - source.vx += x * (b = 1 - b); - source.vy += y * b; - } - } - } - - function initialize() { - if (!nodes) return; - - var i, - n = nodes.length, - m = links.length, - nodeById = map$1(nodes, id), - link; - - for (i = 0, count = new Array(n); i < n; ++i) { - count[i] = 0; - } - - for (i = 0; i < m; ++i) { - link = links[i], link.index = i; - if (typeof link.source !== "object") link.source = find(nodeById, link.source); - if (typeof link.target !== "object") link.target = find(nodeById, link.target); - ++count[link.source.index], ++count[link.target.index]; - } - - for (i = 0, bias = new Array(m); i < m; ++i) { - link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]); - } - - strengths = new Array(m), initializeStrength(); - distances = new Array(m), initializeDistance(); - } - - function initializeStrength() { - if (!nodes) return; - - for (var i = 0, n = links.length; i < n; ++i) { - strengths[i] = +strength(links[i], i, links); - } - } - - function initializeDistance() { - if (!nodes) return; - - for (var i = 0, n = links.length; i < n; ++i) { - distances[i] = +distance(links[i], i, links); - } - } - - force.initialize = function(_) { - nodes = _; - initialize(); - }; - - force.links = function(_) { - return arguments.length ? (links = _, initialize(), force) : links; - }; - - force.id = function(_) { - return arguments.length ? (id = _, force) : id; - }; - - force.iterations = function(_) { - return arguments.length ? (iterations = +_, force) : iterations; - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initializeStrength(), force) : strength; - }; - - force.distance = function(_) { - return arguments.length ? (distance = typeof _ === "function" ? _ : constant$7(+_), initializeDistance(), force) : distance; - }; - - return force; -}; - -function x$2(d) { - return d.x; -} - -function y$2(d) { - return d.y; -} - -var initialRadius = 10; -var initialAngle = Math.PI * (3 - Math.sqrt(5)); - -var simulation = function(nodes) { - var simulation, - alpha = 1, - alphaMin = 0.001, - alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), - alphaTarget = 0, - velocityDecay = 0.6, - forces = map$1(), - stepper = timer(step), - event = dispatch("tick", "end"); - - if (nodes == null) nodes = []; - - function step() { - tick(); - event.call("tick", simulation); - if (alpha < alphaMin) { - stepper.stop(); - event.call("end", simulation); - } - } - - function tick() { - var i, n = nodes.length, node; - - alpha += (alphaTarget - alpha) * alphaDecay; - - forces.each(function(force) { - force(alpha); - }); - - for (i = 0; i < n; ++i) { - node = nodes[i]; - if (node.fx == null) node.x += node.vx *= velocityDecay; - else node.x = node.fx, node.vx = 0; - if (node.fy == null) node.y += node.vy *= velocityDecay; - else node.y = node.fy, node.vy = 0; - } - } - - function initializeNodes() { - for (var i = 0, n = nodes.length, node; i < n; ++i) { - node = nodes[i], node.index = i; - if (isNaN(node.x) || isNaN(node.y)) { - var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle; - node.x = radius * Math.cos(angle); - node.y = radius * Math.sin(angle); - } - if (isNaN(node.vx) || isNaN(node.vy)) { - node.vx = node.vy = 0; - } - } - } - - function initializeForce(force) { - if (force.initialize) force.initialize(nodes); - return force; - } - - initializeNodes(); - - return simulation = { - tick: tick, - - restart: function() { - return stepper.restart(step), simulation; - }, - - stop: function() { - return stepper.stop(), simulation; - }, - - nodes: function(_) { - return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; - }, - - alpha: function(_) { - return arguments.length ? (alpha = +_, simulation) : alpha; - }, - - alphaMin: function(_) { - return arguments.length ? (alphaMin = +_, simulation) : alphaMin; - }, - - alphaDecay: function(_) { - return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; - }, - - alphaTarget: function(_) { - return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; - }, - - velocityDecay: function(_) { - return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay; - }, - - force: function(name, _) { - return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name); - }, - - find: function(x, y, radius) { - var i = 0, - n = nodes.length, - dx, - dy, - d2, - node, - closest; - - if (radius == null) radius = Infinity; - else radius *= radius; - - for (i = 0; i < n; ++i) { - node = nodes[i]; - dx = x - node.x; - dy = y - node.y; - d2 = dx * dx + dy * dy; - if (d2 < radius) closest = node, radius = d2; - } - - return closest; - }, - - on: function(name, _) { - return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); - } - }; -}; - -var manyBody = function() { - var nodes, - node, - alpha, - strength = constant$7(-30), - strengths, - distanceMin2 = 1, - distanceMax2 = Infinity, - theta2 = 0.81; - - function force(_) { - var i, n = nodes.length, tree = quadtree(nodes, x$2, y$2).visitAfter(accumulate); - for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply); - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length; - strengths = new Array(n); - for (i = 0; i < n; ++i) strengths[i] = +strength(nodes[i], i, nodes); - } - - function accumulate(quad) { - var strength = 0, q, c, x$$1, y$$1, i; - - // For internal nodes, accumulate forces from child quadrants. - if (quad.length) { - for (x$$1 = y$$1 = i = 0; i < 4; ++i) { - if ((q = quad[i]) && (c = q.value)) { - strength += c, x$$1 += c * q.x, y$$1 += c * q.y; - } - } - quad.x = x$$1 / strength; - quad.y = y$$1 / strength; - } - - // For leaf nodes, accumulate forces from coincident quadrants. - else { - q = quad; - q.x = q.data.x; - q.y = q.data.y; - do strength += strengths[q.data.index]; - while (q = q.next); - } - - quad.value = strength; - } - - function apply(quad, x1, _, x2) { - if (!quad.value) return true; - - var x$$1 = quad.x - node.x, - y$$1 = quad.y - node.y, - w = x2 - x1, - l = x$$1 * x$$1 + y$$1 * y$$1; - - // Apply the Barnes-Hut approximation if possible. - // Limit forces for very close nodes; randomize direction if coincident. - if (w * w / theta2 < l) { - if (l < distanceMax2) { - if (x$$1 === 0) x$$1 = jiggle(), l += x$$1 * x$$1; - if (y$$1 === 0) y$$1 = jiggle(), l += y$$1 * y$$1; - if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); - node.vx += x$$1 * quad.value * alpha / l; - node.vy += y$$1 * quad.value * alpha / l; - } - return true; - } - - // Otherwise, process points directly. - else if (quad.length || l >= distanceMax2) return; - - // Limit forces for very close nodes; randomize direction if coincident. - if (quad.data !== node || quad.next) { - if (x$$1 === 0) x$$1 = jiggle(), l += x$$1 * x$$1; - if (y$$1 === 0) y$$1 = jiggle(), l += y$$1 * y$$1; - if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); - } - - do if (quad.data !== node) { - w = strengths[quad.data.index] * alpha / l; - node.vx += x$$1 * w; - node.vy += y$$1 * w; - } while (quad = quad.next); - } - - force.initialize = function(_) { - nodes = _; - initialize(); - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; - }; - - force.distanceMin = function(_) { - return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2); - }; - - force.distanceMax = function(_) { - return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); - }; - - force.theta = function(_) { - return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2); - }; - - return force; -}; - -var x$3 = function(x) { - var strength = constant$7(0.1), - nodes, - strengths, - xz; - - if (typeof x !== "function") x = constant$7(x == null ? 0 : +x); - - function force(alpha) { - for (var i = 0, n = nodes.length, node; i < n; ++i) { - node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha; - } - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length; - strengths = new Array(n); - xz = new Array(n); - for (i = 0; i < n; ++i) { - strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); - } - } - - force.initialize = function(_) { - nodes = _; - initialize(); - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; - }; - - force.x = function(_) { - return arguments.length ? (x = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : x; - }; - - return force; -}; - -var y$3 = function(y) { - var strength = constant$7(0.1), - nodes, - strengths, - yz; - - if (typeof y !== "function") y = constant$7(y == null ? 0 : +y); - - function force(alpha) { - for (var i = 0, n = nodes.length, node; i < n; ++i) { - node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha; - } - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length; - strengths = new Array(n); - yz = new Array(n); - for (i = 0; i < n; ++i) { - strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); - } - } - - force.initialize = function(_) { - nodes = _; - initialize(); - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; - }; - - force.y = function(_) { - return arguments.length ? (y = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : y; - }; - - return force; -}; - -function nopropagation() { - exports.event.stopImmediatePropagation(); -} - -var noevent = function() { - exports.event.preventDefault(); - exports.event.stopImmediatePropagation(); -}; - -var dragDisable = function(view) { - var root = view.document.documentElement, - selection$$1 = select(view).on("dragstart.drag", noevent, true); - if ("onselectstart" in root) { - selection$$1.on("selectstart.drag", noevent, true); - } else { - root.__noselect = root.style.MozUserSelect; - root.style.MozUserSelect = "none"; - } -}; - -function yesdrag(view, noclick) { - var root = view.document.documentElement, - selection$$1 = select(view).on("dragstart.drag", null); - if (noclick) { - selection$$1.on("click.drag", noevent, true); - setTimeout(function() { selection$$1.on("click.drag", null); }, 0); - } - if ("onselectstart" in root) { - selection$$1.on("selectstart.drag", null); - } else { - root.style.MozUserSelect = root.__noselect; - delete root.__noselect; - } -} - -var constant$8 = function(x) { - return function() { - return x; - }; -}; - -function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) { - this.target = target; - this.type = type; - this.subject = subject; - this.identifier = id; - this.active = active; - this.x = x; - this.y = y; - this.dx = dx; - this.dy = dy; - this._ = dispatch; -} - -DragEvent.prototype.on = function() { - var value = this._.on.apply(this._, arguments); - return value === this._ ? this : value; -}; - -// Ignore right-click, since that should open the context menu. -function defaultFilter() { - return !exports.event.button; -} - -function defaultContainer() { - return this.parentNode; -} - -function defaultSubject(d) { - return d == null ? {x: exports.event.x, y: exports.event.y} : d; -} - -var drag = function() { - var filter = defaultFilter, - container = defaultContainer, - subject = defaultSubject, - gestures = {}, - listeners = dispatch("start", "drag", "end"), - active = 0, - mousemoving, - touchending; - - function drag(selection$$1) { - selection$$1 - .on("mousedown.drag", mousedowned) - .on("touchstart.drag", touchstarted) - .on("touchmove.drag", touchmoved) - .on("touchend.drag touchcancel.drag", touchended) - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); - } - - function mousedowned() { - if (touchending || !filter.apply(this, arguments)) return; - var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments); - if (!gesture) return; - select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true); - dragDisable(exports.event.view); - nopropagation(); - mousemoving = false; - gesture("start"); - } - - function mousemoved() { - noevent(); - mousemoving = true; - gestures.mouse("drag"); - } - - function mouseupped() { - select(exports.event.view).on("mousemove.drag mouseup.drag", null); - yesdrag(exports.event.view, mousemoving); - noevent(); - gestures.mouse("end"); - } - - function touchstarted() { - if (!filter.apply(this, arguments)) return; - var touches$$1 = exports.event.changedTouches, - c = container.apply(this, arguments), - n = touches$$1.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = beforestart(touches$$1[i].identifier, c, touch, this, arguments)) { - nopropagation(); - gesture("start"); - } - } - } - - function touchmoved() { - var touches$$1 = exports.event.changedTouches, - n = touches$$1.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches$$1[i].identifier]) { - noevent(); - gesture("drag"); - } - } - } - - function touchended() { - var touches$$1 = exports.event.changedTouches, - n = touches$$1.length, i, gesture; - - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches$$1[i].identifier]) { - nopropagation(); - gesture("end"); - } - } - } - - function beforestart(id, container, point, that, args) { - var p = point(container, id), s, dx, dy, - sublisteners = listeners.copy(); - - if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() { - if ((exports.event.subject = s = subject.apply(that, args)) == null) return false; - dx = s.x - p[0] || 0; - dy = s.y - p[1] || 0; - return true; - })) return; - - return function gesture(type) { - var p0 = p, n; - switch (type) { - case "start": gestures[id] = gesture, n = active++; break; - case "end": delete gestures[id], --active; // nobreak - case "drag": p = point(container, id), n = active; break; - } - customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]); - }; - } - - drag.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$8(!!_), drag) : filter; - }; - - drag.container = function(_) { - return arguments.length ? (container = typeof _ === "function" ? _ : constant$8(_), drag) : container; - }; - - drag.subject = function(_) { - return arguments.length ? (subject = typeof _ === "function" ? _ : constant$8(_), drag) : subject; - }; - - drag.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? drag : value; - }; - - return drag; -}; - -var constant$9 = function(x) { - return function() { - return x; - }; -}; - -function x$4(d) { - return d[0]; -} - -function y$4(d) { - return d[1]; -} - -function RedBlackTree() { - this._ = null; // root node -} - -function RedBlackNode(node) { - node.U = // parent node - node.C = // color - true for red, false for black - node.L = // left node - node.R = // right node - node.P = // previous node - node.N = null; // next node -} - -RedBlackTree.prototype = { - constructor: RedBlackTree, - - insert: function(after, node) { - var parent, grandpa, uncle; - - if (after) { - node.P = after; - node.N = after.N; - if (after.N) after.N.P = node; - after.N = node; - if (after.R) { - after = after.R; - while (after.L) after = after.L; - after.L = node; - } else { - after.R = node; - } - parent = after; - } else if (this._) { - after = RedBlackFirst(this._); - node.P = null; - node.N = after; - after.P = after.L = node; - parent = after; - } else { - node.P = node.N = null; - this._ = node; - parent = null; - } - node.L = node.R = null; - node.U = parent; - node.C = true; - - after = node; - while (parent && parent.C) { - grandpa = parent.U; - if (parent === grandpa.L) { - uncle = grandpa.R; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.R) { - RedBlackRotateLeft(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - RedBlackRotateRight(this, grandpa); - } - } else { - uncle = grandpa.L; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.L) { - RedBlackRotateRight(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - RedBlackRotateLeft(this, grandpa); - } - } - parent = after.U; - } - this._.C = false; - }, - - remove: function(node) { - if (node.N) node.N.P = node.P; - if (node.P) node.P.N = node.N; - node.N = node.P = null; - - var parent = node.U, - sibling, - left = node.L, - right = node.R, - next, - red; - - if (!left) next = right; - else if (!right) next = left; - else next = RedBlackFirst(right); - - if (parent) { - if (parent.L === node) parent.L = next; - else parent.R = next; - } else { - this._ = next; - } - - if (left && right) { - red = next.C; - next.C = node.C; - next.L = left; - left.U = next; - if (next !== right) { - parent = next.U; - next.U = node.U; - node = next.R; - parent.L = node; - next.R = right; - right.U = next; - } else { - next.U = parent; - parent = next; - node = next.R; - } - } else { - red = node.C; - node = next; - } - - if (node) node.U = parent; - if (red) return; - if (node && node.C) { node.C = false; return; } - - do { - if (node === this._) break; - if (node === parent.L) { - sibling = parent.R; - if (sibling.C) { - sibling.C = false; - parent.C = true; - RedBlackRotateLeft(this, parent); - sibling = parent.R; - } - if ((sibling.L && sibling.L.C) - || (sibling.R && sibling.R.C)) { - if (!sibling.R || !sibling.R.C) { - sibling.L.C = false; - sibling.C = true; - RedBlackRotateRight(this, sibling); - sibling = parent.R; - } - sibling.C = parent.C; - parent.C = sibling.R.C = false; - RedBlackRotateLeft(this, parent); - node = this._; - break; - } - } else { - sibling = parent.L; - if (sibling.C) { - sibling.C = false; - parent.C = true; - RedBlackRotateRight(this, parent); - sibling = parent.L; - } - if ((sibling.L && sibling.L.C) - || (sibling.R && sibling.R.C)) { - if (!sibling.L || !sibling.L.C) { - sibling.R.C = false; - sibling.C = true; - RedBlackRotateLeft(this, sibling); - sibling = parent.L; - } - sibling.C = parent.C; - parent.C = sibling.L.C = false; - RedBlackRotateRight(this, parent); - node = this._; - break; - } - } - sibling.C = true; - node = parent; - parent = parent.U; - } while (!node.C); - - if (node) node.C = false; - } -}; - -function RedBlackRotateLeft(tree, node) { - var p = node, - q = node.R, - parent = p.U; - - if (parent) { - if (parent.L === p) parent.L = q; - else parent.R = q; - } else { - tree._ = q; - } - - q.U = parent; - p.U = q; - p.R = q.L; - if (p.R) p.R.U = p; - q.L = p; -} - -function RedBlackRotateRight(tree, node) { - var p = node, - q = node.L, - parent = p.U; - - if (parent) { - if (parent.L === p) parent.L = q; - else parent.R = q; - } else { - tree._ = q; - } - - q.U = parent; - p.U = q; - p.L = q.R; - if (p.L) p.L.U = p; - q.R = p; -} - -function RedBlackFirst(node) { - while (node.L) node = node.L; - return node; -} - -function createEdge(left, right, v0, v1) { - var edge = [null, null], - index = edges.push(edge) - 1; - edge.left = left; - edge.right = right; - if (v0) setEdgeEnd(edge, left, right, v0); - if (v1) setEdgeEnd(edge, right, left, v1); - cells[left.index].halfedges.push(index); - cells[right.index].halfedges.push(index); - return edge; -} - -function createBorderEdge(left, v0, v1) { - var edge = [v0, v1]; - edge.left = left; - return edge; -} - -function setEdgeEnd(edge, left, right, vertex) { - if (!edge[0] && !edge[1]) { - edge[0] = vertex; - edge.left = left; - edge.right = right; - } else if (edge.left === right) { - edge[1] = vertex; - } else { - edge[0] = vertex; - } -} - -// Liang–Barsky line clipping. -function clipEdge(edge, x0, y0, x1, y1) { - var a = edge[0], - b = edge[1], - ax = a[0], - ay = a[1], - bx = b[0], - by = b[1], - t0 = 0, - t1 = 1, - dx = bx - ax, - dy = by - ay, - r; - - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check? - - if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy]; - if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy]; - return true; -} - -function connectEdge(edge, x0, y0, x1, y1) { - var v1 = edge[1]; - if (v1) return true; - - var v0 = edge[0], - left = edge.left, - right = edge.right, - lx = left[0], - ly = left[1], - rx = right[0], - ry = right[1], - fx = (lx + rx) / 2, - fy = (ly + ry) / 2, - fm, - fb; - - if (ry === ly) { - if (fx < x0 || fx >= x1) return; - if (lx > rx) { - if (!v0) v0 = [fx, y0]; - else if (v0[1] >= y1) return; - v1 = [fx, y1]; - } else { - if (!v0) v0 = [fx, y1]; - else if (v0[1] < y0) return; - v1 = [fx, y0]; - } - } else { - fm = (lx - rx) / (ry - ly); - fb = fy - fm * fx; - if (fm < -1 || fm > 1) { - if (lx > rx) { - if (!v0) v0 = [(y0 - fb) / fm, y0]; - else if (v0[1] >= y1) return; - v1 = [(y1 - fb) / fm, y1]; - } else { - if (!v0) v0 = [(y1 - fb) / fm, y1]; - else if (v0[1] < y0) return; - v1 = [(y0 - fb) / fm, y0]; - } - } else { - if (ly < ry) { - if (!v0) v0 = [x0, fm * x0 + fb]; - else if (v0[0] >= x1) return; - v1 = [x1, fm * x1 + fb]; - } else { - if (!v0) v0 = [x1, fm * x1 + fb]; - else if (v0[0] < x0) return; - v1 = [x0, fm * x0 + fb]; - } - } - } - - edge[0] = v0; - edge[1] = v1; - return true; -} - -function clipEdges(x0, y0, x1, y1) { - var i = edges.length, - edge; - - while (i--) { - if (!connectEdge(edge = edges[i], x0, y0, x1, y1) - || !clipEdge(edge, x0, y0, x1, y1) - || !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$3 - || Math.abs(edge[0][1] - edge[1][1]) > epsilon$3)) { - delete edges[i]; - } - } -} - -function createCell(site) { - return cells[site.index] = { - site: site, - halfedges: [] - }; -} - -function cellHalfedgeAngle(cell, edge) { - var site = cell.site, - va = edge.left, - vb = edge.right; - if (site === vb) vb = va, va = site; - if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]); - if (site === va) va = edge[1], vb = edge[0]; - else va = edge[0], vb = edge[1]; - return Math.atan2(va[0] - vb[0], vb[1] - va[1]); -} - -function cellHalfedgeStart(cell, edge) { - return edge[+(edge.left !== cell.site)]; -} - -function cellHalfedgeEnd(cell, edge) { - return edge[+(edge.left === cell.site)]; -} - -function sortCellHalfedges() { - for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) { - if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) { - var index = new Array(m), - array = new Array(m); - for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]); - index.sort(function(i, j) { return array[j] - array[i]; }); - for (j = 0; j < m; ++j) array[j] = halfedges[index[j]]; - for (j = 0; j < m; ++j) halfedges[j] = array[j]; - } - } -} - -function clipCells(x0, y0, x1, y1) { - var nCells = cells.length, - iCell, - cell, - site, - iHalfedge, - halfedges, - nHalfedges, - start, - startX, - startY, - end, - endX, - endY, - cover = true; - - for (iCell = 0; iCell < nCells; ++iCell) { - if (cell = cells[iCell]) { - site = cell.site; - halfedges = cell.halfedges; - iHalfedge = halfedges.length; - - // Remove any dangling clipped edges. - while (iHalfedge--) { - if (!edges[halfedges[iHalfedge]]) { - halfedges.splice(iHalfedge, 1); - } - } - - // Insert any border edges as necessary. - iHalfedge = 0, nHalfedges = halfedges.length; - while (iHalfedge < nHalfedges) { - end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1]; - start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1]; - if (Math.abs(endX - startX) > epsilon$3 || Math.abs(endY - startY) > epsilon$3) { - halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end, - Math.abs(endX - x0) < epsilon$3 && y1 - endY > epsilon$3 ? [x0, Math.abs(startX - x0) < epsilon$3 ? startY : y1] - : Math.abs(endY - y1) < epsilon$3 && x1 - endX > epsilon$3 ? [Math.abs(startY - y1) < epsilon$3 ? startX : x1, y1] - : Math.abs(endX - x1) < epsilon$3 && endY - y0 > epsilon$3 ? [x1, Math.abs(startX - x1) < epsilon$3 ? startY : y0] - : Math.abs(endY - y0) < epsilon$3 && endX - x0 > epsilon$3 ? [Math.abs(startY - y0) < epsilon$3 ? startX : x0, y0] - : null)) - 1); - ++nHalfedges; - } - } - - if (nHalfedges) cover = false; - } - } - - // If there weren’t any edges, have the closest site cover the extent. - // It doesn’t matter which corner of the extent we measure! - if (cover) { - var dx, dy, d2, dc = Infinity; - - for (iCell = 0, cover = null; iCell < nCells; ++iCell) { - if (cell = cells[iCell]) { - site = cell.site; - dx = site[0] - x0; - dy = site[1] - y0; - d2 = dx * dx + dy * dy; - if (d2 < dc) dc = d2, cover = cell; - } - } - - if (cover) { - var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0]; - cover.halfedges.push( - edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1, - edges.push(createBorderEdge(site, v01, v11)) - 1, - edges.push(createBorderEdge(site, v11, v10)) - 1, - edges.push(createBorderEdge(site, v10, v00)) - 1 - ); - } - } - - // Lastly delete any cells with no edges; these were entirely clipped. - for (iCell = 0; iCell < nCells; ++iCell) { - if (cell = cells[iCell]) { - if (!cell.halfedges.length) { - delete cells[iCell]; - } - } - } -} - -var circlePool = []; - -var firstCircle; - -function Circle() { - RedBlackNode(this); - this.x = - this.y = - this.arc = - this.site = - this.cy = null; -} - -function attachCircle(arc) { - var lArc = arc.P, - rArc = arc.N; - - if (!lArc || !rArc) return; - - var lSite = lArc.site, - cSite = arc.site, - rSite = rArc.site; - - if (lSite === rSite) return; - - var bx = cSite[0], - by = cSite[1], - ax = lSite[0] - bx, - ay = lSite[1] - by, - cx = rSite[0] - bx, - cy = rSite[1] - by; - - var d = 2 * (ax * cy - ay * cx); - if (d >= -epsilon2$1) return; - - var ha = ax * ax + ay * ay, - hc = cx * cx + cy * cy, - x = (cy * ha - ay * hc) / d, - y = (ax * hc - cx * ha) / d; - - var circle = circlePool.pop() || new Circle; - circle.arc = arc; - circle.site = cSite; - circle.x = x + bx; - circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom - - arc.circle = circle; - - var before = null, - node = circles._; - - while (node) { - if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) { - if (node.L) node = node.L; - else { before = node.P; break; } - } else { - if (node.R) node = node.R; - else { before = node; break; } - } - } - - circles.insert(before, circle); - if (!before) firstCircle = circle; -} - -function detachCircle(arc) { - var circle = arc.circle; - if (circle) { - if (!circle.P) firstCircle = circle.N; - circles.remove(circle); - circlePool.push(circle); - RedBlackNode(circle); - arc.circle = null; - } -} - -var beachPool = []; - -function Beach() { - RedBlackNode(this); - this.edge = - this.site = - this.circle = null; -} - -function createBeach(site) { - var beach = beachPool.pop() || new Beach; - beach.site = site; - return beach; -} - -function detachBeach(beach) { - detachCircle(beach); - beaches.remove(beach); - beachPool.push(beach); - RedBlackNode(beach); -} - -function removeBeach(beach) { - var circle = beach.circle, - x = circle.x, - y = circle.cy, - vertex = [x, y], - previous = beach.P, - next = beach.N, - disappearing = [beach]; - - detachBeach(beach); - - var lArc = previous; - while (lArc.circle - && Math.abs(x - lArc.circle.x) < epsilon$3 - && Math.abs(y - lArc.circle.cy) < epsilon$3) { - previous = lArc.P; - disappearing.unshift(lArc); - detachBeach(lArc); - lArc = previous; - } - - disappearing.unshift(lArc); - detachCircle(lArc); - - var rArc = next; - while (rArc.circle - && Math.abs(x - rArc.circle.x) < epsilon$3 - && Math.abs(y - rArc.circle.cy) < epsilon$3) { - next = rArc.N; - disappearing.push(rArc); - detachBeach(rArc); - rArc = next; - } - - disappearing.push(rArc); - detachCircle(rArc); - - var nArcs = disappearing.length, - iArc; - for (iArc = 1; iArc < nArcs; ++iArc) { - rArc = disappearing[iArc]; - lArc = disappearing[iArc - 1]; - setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); - } - - lArc = disappearing[0]; - rArc = disappearing[nArcs - 1]; - rArc.edge = createEdge(lArc.site, rArc.site, null, vertex); - - attachCircle(lArc); - attachCircle(rArc); -} - -function addBeach(site) { - var x = site[0], - directrix = site[1], - lArc, - rArc, - dxl, - dxr, - node = beaches._; - - while (node) { - dxl = leftBreakPoint(node, directrix) - x; - if (dxl > epsilon$3) node = node.L; else { - dxr = x - rightBreakPoint(node, directrix); - if (dxr > epsilon$3) { - if (!node.R) { - lArc = node; - break; - } - node = node.R; - } else { - if (dxl > -epsilon$3) { - lArc = node.P; - rArc = node; - } else if (dxr > -epsilon$3) { - lArc = node; - rArc = node.N; - } else { - lArc = rArc = node; - } - break; - } - } - } - - createCell(site); - var newArc = createBeach(site); - beaches.insert(lArc, newArc); - - if (!lArc && !rArc) return; - - if (lArc === rArc) { - detachCircle(lArc); - rArc = createBeach(lArc.site); - beaches.insert(newArc, rArc); - newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site); - attachCircle(lArc); - attachCircle(rArc); - return; - } - - if (!rArc) { // && lArc - newArc.edge = createEdge(lArc.site, newArc.site); - return; - } - - // else lArc !== rArc - detachCircle(lArc); - detachCircle(rArc); - - var lSite = lArc.site, - ax = lSite[0], - ay = lSite[1], - bx = site[0] - ax, - by = site[1] - ay, - rSite = rArc.site, - cx = rSite[0] - ax, - cy = rSite[1] - ay, - d = 2 * (bx * cy - by * cx), - hb = bx * bx + by * by, - hc = cx * cx + cy * cy, - vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay]; - - setEdgeEnd(rArc.edge, lSite, rSite, vertex); - newArc.edge = createEdge(lSite, site, null, vertex); - rArc.edge = createEdge(site, rSite, null, vertex); - attachCircle(lArc); - attachCircle(rArc); -} - -function leftBreakPoint(arc, directrix) { - var site = arc.site, - rfocx = site[0], - rfocy = site[1], - pby2 = rfocy - directrix; - - if (!pby2) return rfocx; - - var lArc = arc.P; - if (!lArc) return -Infinity; - - site = lArc.site; - var lfocx = site[0], - lfocy = site[1], - plby2 = lfocy - directrix; - - if (!plby2) return lfocx; - - var hl = lfocx - rfocx, - aby2 = 1 / pby2 - 1 / plby2, - b = hl / plby2; - - if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; - - return (rfocx + lfocx) / 2; -} - -function rightBreakPoint(arc, directrix) { - var rArc = arc.N; - if (rArc) return leftBreakPoint(rArc, directrix); - var site = arc.site; - return site[1] === directrix ? site[0] : Infinity; -} - -var epsilon$3 = 1e-6; -var epsilon2$1 = 1e-12; -var beaches; -var cells; -var circles; -var edges; - -function triangleArea(a, b, c) { - return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]); -} - -function lexicographic(a, b) { - return b[1] - a[1] - || b[0] - a[0]; -} - -function Diagram(sites, extent) { - var site = sites.sort(lexicographic).pop(), - x, - y, - circle; - - edges = []; - cells = new Array(sites.length); - beaches = new RedBlackTree; - circles = new RedBlackTree; - - while (true) { - circle = firstCircle; - if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) { - if (site[0] !== x || site[1] !== y) { - addBeach(site); - x = site[0], y = site[1]; - } - site = sites.pop(); - } else if (circle) { - removeBeach(circle.arc); - } else { - break; - } - } - - sortCellHalfedges(); - - if (extent) { - var x0 = +extent[0][0], - y0 = +extent[0][1], - x1 = +extent[1][0], - y1 = +extent[1][1]; - clipEdges(x0, y0, x1, y1); - clipCells(x0, y0, x1, y1); - } - - this.edges = edges; - this.cells = cells; - - beaches = - circles = - edges = - cells = null; -} - -Diagram.prototype = { - constructor: Diagram, - - polygons: function() { - var edges = this.edges; - - return this.cells.map(function(cell) { - var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); }); - polygon.data = cell.site.data; - return polygon; - }); - }, - - triangles: function() { - var triangles = [], - edges = this.edges; - - this.cells.forEach(function(cell, i) { - var site = cell.site, - halfedges = cell.halfedges, - j = -1, - m = halfedges.length, - s0, - e1 = edges[halfedges[m - 1]], - s1 = e1.left === site ? e1.right : e1.left; - - while (++j < m) { - s0 = s1; - e1 = edges[halfedges[j]]; - s1 = e1.left === site ? e1.right : e1.left; - if (i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) { - triangles.push([site.data, s0.data, s1.data]); - } - } - }); - - return triangles; - }, - - links: function() { - return this.edges.filter(function(edge) { - return edge.right; - }).map(function(edge) { - return { - source: edge.left.data, - target: edge.right.data - }; - }); - } -}; - -var voronoi = function() { - var x$$1 = x$4, - y$$1 = y$4, - extent = null; - - function voronoi(data) { - return new Diagram(data.map(function(d, i) { - var s = [Math.round(x$$1(d, i, data) / epsilon$3) * epsilon$3, Math.round(y$$1(d, i, data) / epsilon$3) * epsilon$3]; - s.index = i; - s.data = d; - return s; - }), extent); - } - - voronoi.polygons = function(data) { - return voronoi(data).polygons(); - }; - - voronoi.links = function(data) { - return voronoi(data).links(); - }; - - voronoi.triangles = function(data) { - return voronoi(data).triangles(); - }; - - voronoi.x = function(_) { - return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$9(+_), voronoi) : x$$1; - }; - - voronoi.y = function(_) { - return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$9(+_), voronoi) : y$$1; - }; - - voronoi.extent = function(_) { - return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]]; - }; - - voronoi.size = function(_) { - return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]]; - }; - - return voronoi; -}; - -var constant$10 = function(x) { - return function() { - return x; - }; -}; - -function ZoomEvent(target, type, transform) { - this.target = target; - this.type = type; - this.transform = transform; -} - -function Transform(k, x, y) { - this.k = k; - this.x = x; - this.y = y; -} - -Transform.prototype = { - constructor: Transform, - scale: function(k) { - return k === 1 ? this : new Transform(this.k * k, this.x, this.y); - }, - translate: function(x, y) { - return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); - }, - apply: function(point) { - return [point[0] * this.k + this.x, point[1] * this.k + this.y]; - }, - applyX: function(x) { - return x * this.k + this.x; - }, - applyY: function(y) { - return y * this.k + this.y; - }, - invert: function(location) { - return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; - }, - invertX: function(x) { - return (x - this.x) / this.k; - }, - invertY: function(y) { - return (y - this.y) / this.k; - }, - rescaleX: function(x) { - return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); - }, - rescaleY: function(y) { - return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); - }, - toString: function() { - return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; - } -}; - -var identity$6 = new Transform(1, 0, 0); - -transform.prototype = Transform.prototype; - -function transform(node) { - return node.__zoom || identity$6; -} - -function nopropagation$1() { - exports.event.stopImmediatePropagation(); -} - -var noevent$1 = function() { - exports.event.preventDefault(); - exports.event.stopImmediatePropagation(); -}; - -// Ignore right-click, since that should open the context menu. -function defaultFilter$1() { - return !exports.event.button; -} - -function defaultExtent() { - var e = this, w, h; - if (e instanceof SVGElement) { - e = e.ownerSVGElement || e; - w = e.width.baseVal.value; - h = e.height.baseVal.value; - } else { - w = e.clientWidth; - h = e.clientHeight; - } - return [[0, 0], [w, h]]; -} - -function defaultTransform() { - return this.__zoom || identity$6; -} - -var zoom = function() { - var filter = defaultFilter$1, - extent = defaultExtent, - k0 = 0, - k1 = Infinity, - x0 = -k1, - x1 = k1, - y0 = x0, - y1 = x1, - duration = 250, - gestures = [], - listeners = dispatch("start", "zoom", "end"), - touchstarting, - touchending, - touchDelay = 500, - wheelDelay = 150; - - function zoom(selection$$1) { - selection$$1 - .on("wheel.zoom", wheeled) - .on("mousedown.zoom", mousedowned) - .on("dblclick.zoom", dblclicked) - .on("touchstart.zoom", touchstarted) - .on("touchmove.zoom", touchmoved) - .on("touchend.zoom touchcancel.zoom", touchended) - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") - .property("__zoom", defaultTransform); - } - - zoom.transform = function(collection, transform) { - var selection$$1 = collection.selection ? collection.selection() : collection; - selection$$1.property("__zoom", defaultTransform); - if (collection !== selection$$1) { - schedule(collection, transform); - } else { - selection$$1.interrupt().each(function() { - gesture(this, arguments) - .start() - .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform) - .end(); - }); - } - }; - - zoom.scaleBy = function(selection$$1, k) { - zoom.scaleTo(selection$$1, function() { - var k0 = this.__zoom.k, - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return k0 * k1; - }); - }; - - zoom.scaleTo = function(selection$$1, k) { - zoom.transform(selection$$1, function() { - var e = extent.apply(this, arguments), - t0 = this.__zoom, - p0 = centroid(e), - p1 = t0.invert(p0), - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return constrain(translate(scale(t0, k1), p0, p1), e); - }); - }; - - zoom.translateBy = function(selection$$1, x, y) { - zoom.transform(selection$$1, function() { - return constrain(this.__zoom.translate( - typeof x === "function" ? x.apply(this, arguments) : x, - typeof y === "function" ? y.apply(this, arguments) : y - ), extent.apply(this, arguments)); - }); - }; - - function scale(transform, k) { - k = Math.max(k0, Math.min(k1, k)); - return k === transform.k ? transform : new Transform(k, transform.x, transform.y); - } - - function translate(transform, p0, p1) { - var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k; - return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); - } - - function constrain(transform, extent) { - var dx = Math.min(0, transform.invertX(extent[0][0]) - x0) || Math.max(0, transform.invertX(extent[1][0]) - x1), - dy = Math.min(0, transform.invertY(extent[0][1]) - y0) || Math.max(0, transform.invertY(extent[1][1]) - y1); - return dx || dy ? transform.translate(dx, dy) : transform; - } - - function centroid(extent) { - return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; - } - - function schedule(transition$$1, transform, center) { - transition$$1 - .on("start.zoom", function() { gesture(this, arguments).start(); }) - .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); }) - .tween("zoom", function() { - var that = this, - args = arguments, - g = gesture(that, args), - e = extent.apply(that, args), - p = center || centroid(e), - w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), - a = that.__zoom, - b = typeof transform === "function" ? transform.apply(that, args) : transform, - i = interpolateZoom(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); - return function(t) { - if (t === 1) t = b; // Avoid rounding error on end. - else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } - g.zoom(null, t); - }; - }); - } - - function gesture(that, args) { - for (var i = 0, n = gestures.length, g; i < n; ++i) { - if ((g = gestures[i]).that === that) { - return g; - } - } - return new Gesture(that, args); - } - - function Gesture(that, args) { - this.that = that; - this.args = args; - this.index = -1; - this.active = 0; - this.extent = extent.apply(that, args); - } - - Gesture.prototype = { - start: function() { - if (++this.active === 1) { - this.index = gestures.push(this) - 1; - this.emit("start"); - } - return this; - }, - zoom: function(key, transform) { - if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]); - if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]); - if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]); - this.that.__zoom = transform; - this.emit("zoom"); - return this; - }, - end: function() { - if (--this.active === 0) { - gestures.splice(this.index, 1); - this.index = -1; - this.emit("end"); - } - return this; - }, - emit: function(type) { - customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]); - } - }; - - function wheeled() { - if (!filter.apply(this, arguments)) return; - var g = gesture(this, arguments), - t = this.__zoom, - k = Math.max(k0, Math.min(k1, t.k * Math.pow(2, -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500))), - p = mouse(this); - - // If the mouse is in the same location as before, reuse it. - // If there were recent wheel events, reset the wheel idle timeout. - if (g.wheel) { - if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { - g.mouse[1] = t.invert(g.mouse[0] = p); - } - clearTimeout(g.wheel); - } - - // If this wheel event won’t trigger a transform change, ignore it. - else if (t.k === k) return; - - // Otherwise, capture the mouse point and location at the start. - else { - g.mouse = [p, t.invert(p)]; - interrupt(this); - g.start(); - } - - noevent$1(); - g.wheel = setTimeout(wheelidled, wheelDelay); - g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent)); - - function wheelidled() { - g.wheel = null; - g.end(); - } - } - - function mousedowned() { - if (touchending || !filter.apply(this, arguments)) return; - var g = gesture(this, arguments), - v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), - p = mouse(this); - - dragDisable(exports.event.view); - nopropagation$1(); - g.mouse = [p, this.__zoom.invert(p)]; - interrupt(this); - g.start(); - - function mousemoved() { - noevent$1(); - g.moved = true; - g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent)); - } - - function mouseupped() { - v.on("mousemove.zoom mouseup.zoom", null); - yesdrag(exports.event.view, g.moved); - noevent$1(); - g.end(); - } - } - - function dblclicked() { - if (!filter.apply(this, arguments)) return; - var t0 = this.__zoom, - p0 = mouse(this), - p1 = t0.invert(p0), - k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2), - t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments)); - - noevent$1(); - if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0); - else select(this).call(zoom.transform, t1); - } - - function touchstarted() { - if (!filter.apply(this, arguments)) return; - var g = gesture(this, arguments), - touches$$1 = exports.event.changedTouches, - n = touches$$1.length, i, t, p; - - nopropagation$1(); - for (i = 0; i < n; ++i) { - t = touches$$1[i], p = touch(this, touches$$1, t.identifier); - p = [p, this.__zoom.invert(p), t.identifier]; - if (!g.touch0) g.touch0 = p; - else if (!g.touch1) g.touch1 = p; - } - if (touchstarting) { - touchstarting = clearTimeout(touchstarting); - if (!g.touch1) return g.end(), dblclicked.apply(this, arguments); - } - if (exports.event.touches.length === n) { - touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); - interrupt(this); - g.start(); - } - } - - function touchmoved() { - var g = gesture(this, arguments), - touches$$1 = exports.event.changedTouches, - n = touches$$1.length, i, t, p, l; - - noevent$1(); - if (touchstarting) touchstarting = clearTimeout(touchstarting); - for (i = 0; i < n; ++i) { - t = touches$$1[i], p = touch(this, touches$$1, t.identifier); - if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; - else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; - } - t = g.that.__zoom; - if (g.touch1) { - var p0 = g.touch0[0], l0 = g.touch0[1], - p1 = g.touch1[0], l1 = g.touch1[1], - dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, - dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; - t = scale(t, Math.sqrt(dp / dl)); - p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - } - else if (g.touch0) p = g.touch0[0], l = g.touch0[1]; - else return; - g.zoom("touch", constrain(translate(t, p, l), g.extent)); - } - - function touchended() { - var g = gesture(this, arguments), - touches$$1 = exports.event.changedTouches, - n = touches$$1.length, i, t; - - nopropagation$1(); - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, touchDelay); - for (i = 0; i < n; ++i) { - t = touches$$1[i]; - if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0; - else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; - } - if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; - if (!g.touch0) g.end(); - } - - zoom.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$10(!!_), zoom) : filter; - }; - - zoom.extent = function(_) { - return arguments.length ? (extent = typeof _ === "function" ? _ : constant$10([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; - }; - - zoom.scaleExtent = function(_) { - return arguments.length ? (k0 = +_[0], k1 = +_[1], zoom) : [k0, k1]; - }; - - zoom.translateExtent = function(_) { - return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], zoom) : [[x0, y0], [x1, y1]]; - }; - - zoom.duration = function(_) { - return arguments.length ? (duration = +_, zoom) : duration; - }; - - zoom.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? zoom : value; - }; - - return zoom; -}; - -var constant$11 = function(x) { - return function() { - return x; - }; -}; - -var BrushEvent = function(target, type, selection) { - this.target = target; - this.type = type; - this.selection = selection; -}; - -function nopropagation$2() { - exports.event.stopImmediatePropagation(); -} - -var noevent$2 = function() { - exports.event.preventDefault(); - exports.event.stopImmediatePropagation(); -}; - -var MODE_DRAG = {name: "drag"}; -var MODE_SPACE = {name: "space"}; -var MODE_HANDLE = {name: "handle"}; -var MODE_CENTER = {name: "center"}; - -var X = { - name: "x", - handles: ["e", "w"].map(type$1), - input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, - output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } -}; - -var Y = { - name: "y", - handles: ["n", "s"].map(type$1), - input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, - output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } -}; - -var XY = { - name: "xy", - handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type$1), - input: function(xy) { return xy; }, - output: function(xy) { return xy; } -}; - -var cursors = { - overlay: "crosshair", - selection: "move", - n: "ns-resize", - e: "ew-resize", - s: "ns-resize", - w: "ew-resize", - nw: "nwse-resize", - ne: "nesw-resize", - se: "nwse-resize", - sw: "nesw-resize" -}; - -var flipX = { - e: "w", - w: "e", - nw: "ne", - ne: "nw", - se: "sw", - sw: "se" -}; - -var flipY = { - n: "s", - s: "n", - nw: "sw", - ne: "se", - se: "ne", - sw: "nw" -}; - -var signsX = { - overlay: +1, - selection: +1, - n: null, - e: +1, - s: null, - w: -1, - nw: -1, - ne: +1, - se: +1, - sw: -1 -}; - -var signsY = { - overlay: +1, - selection: +1, - n: -1, - e: null, - s: +1, - w: null, - nw: -1, - ne: -1, - se: +1, - sw: +1 -}; - -function type$1(t) { - return {type: t}; -} - -// Ignore right-click, since that should open the context menu. -function defaultFilter$2() { - return !exports.event.button; -} - -function defaultExtent$1() { - var svg = this.ownerSVGElement || this; - return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; -} - -// Like d3.local, but with the name “__brush” rather than auto-generated. -function local$1(node) { - while (!node.__brush) if (!(node = node.parentNode)) return; - return node.__brush; -} - -function empty$1(extent) { - return extent[0][0] === extent[1][0] - || extent[0][1] === extent[1][1]; -} - -function brushSelection(node) { - var state = node.__brush; - return state ? state.dim.output(state.selection) : null; -} - -function brushX() { - return brush$1(X); -} - -function brushY() { - return brush$1(Y); -} - -var brush = function() { - return brush$1(XY); -}; - -function brush$1(dim) { - var extent = defaultExtent$1, - filter = defaultFilter$2, - listeners = dispatch(brush, "start", "brush", "end"), - handleSize = 6, - touchending; - - function brush(group) { - var overlay = group - .property("__brush", initialize) - .selectAll(".overlay") - .data([type$1("overlay")]); - - overlay.enter().append("rect") - .attr("class", "overlay") - .attr("pointer-events", "all") - .attr("cursor", cursors.overlay) - .merge(overlay) - .each(function() { - var extent = local$1(this).extent; - select(this) - .attr("x", extent[0][0]) - .attr("y", extent[0][1]) - .attr("width", extent[1][0] - extent[0][0]) - .attr("height", extent[1][1] - extent[0][1]); - }); - - group.selectAll(".selection") - .data([type$1("selection")]) - .enter().append("rect") - .attr("class", "selection") - .attr("cursor", cursors.selection) - .attr("fill", "#777") - .attr("fill-opacity", 0.3) - .attr("stroke", "#fff") - .attr("shape-rendering", "crispEdges"); - - var handle = group.selectAll(".handle") - .data(dim.handles, function(d) { return d.type; }); - - handle.exit().remove(); - - handle.enter().append("rect") - .attr("class", function(d) { return "handle handle--" + d.type; }) - .attr("cursor", function(d) { return cursors[d.type]; }); - - group - .each(redraw) - .attr("fill", "none") - .attr("pointer-events", "all") - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") - .on("mousedown.brush touchstart.brush", started); - } - - brush.move = function(group, selection$$1) { - if (group.selection) { - group - .on("start.brush", function() { emitter(this, arguments).beforestart().start(); }) - .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); }) - .tween("brush", function() { - var that = this, - state = that.__brush, - emit = emitter(that, arguments), - selection0 = state.selection, - selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(this, arguments) : selection$$1, state.extent), - i = interpolate(selection0, selection1); - - function tween(t) { - state.selection = t === 1 && empty$1(selection1) ? null : i(t); - redraw.call(that); - emit.brush(); - } - - return selection0 && selection1 ? tween : tween(1); - }); - } else { - group - .each(function() { - var that = this, - args = arguments, - state = that.__brush, - selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(that, args) : selection$$1, state.extent), - emit = emitter(that, args).beforestart(); - - interrupt(that); - state.selection = selection1 == null || empty$1(selection1) ? null : selection1; - redraw.call(that); - emit.start().brush().end(); - }); - } - }; - - function redraw() { - var group = select(this), - selection$$1 = local$1(this).selection; - - if (selection$$1) { - group.selectAll(".selection") - .style("display", null) - .attr("x", selection$$1[0][0]) - .attr("y", selection$$1[0][1]) - .attr("width", selection$$1[1][0] - selection$$1[0][0]) - .attr("height", selection$$1[1][1] - selection$$1[0][1]); - - group.selectAll(".handle") - .style("display", null) - .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection$$1[1][0] - handleSize / 2 : selection$$1[0][0] - handleSize / 2; }) - .attr("y", function(d) { return d.type[0] === "s" ? selection$$1[1][1] - handleSize / 2 : selection$$1[0][1] - handleSize / 2; }) - .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection$$1[1][0] - selection$$1[0][0] + handleSize : handleSize; }) - .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection$$1[1][1] - selection$$1[0][1] + handleSize : handleSize; }); - } - - else { - group.selectAll(".selection,.handle") - .style("display", "none") - .attr("x", null) - .attr("y", null) - .attr("width", null) - .attr("height", null); - } - } - - function emitter(that, args) { - return that.__brush.emitter || new Emitter(that, args); - } - - function Emitter(that, args) { - this.that = that; - this.args = args; - this.state = that.__brush; - this.active = 0; - } - - Emitter.prototype = { - beforestart: function() { - if (++this.active === 1) this.state.emitter = this, this.starting = true; - return this; - }, - start: function() { - if (this.starting) this.starting = false, this.emit("start"); - return this; - }, - brush: function() { - this.emit("brush"); - return this; - }, - end: function() { - if (--this.active === 0) delete this.state.emitter, this.emit("end"); - return this; - }, - emit: function(type) { - customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]); - } - }; - - function started() { - if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$2(); } - else if (touchending) return; - if (!filter.apply(this, arguments)) return; - - var that = this, - type = exports.event.target.__data__.type, - mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE), - signX = dim === Y ? null : signsX[type], - signY = dim === X ? null : signsY[type], - state = local$1(that), - extent = state.extent, - selection$$1 = state.selection, - W = extent[0][0], w0, w1, - N = extent[0][1], n0, n1, - E = extent[1][0], e0, e1, - S = extent[1][1], s0, s1, - dx, - dy, - moving, - shifting = signX && signY && exports.event.shiftKey, - lockX, - lockY, - point0 = mouse(that), - point = point0, - emit = emitter(that, arguments).beforestart(); - - if (type === "overlay") { - state.selection = selection$$1 = [ - [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], - [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] - ]; - } else { - w0 = selection$$1[0][0]; - n0 = selection$$1[0][1]; - e0 = selection$$1[1][0]; - s0 = selection$$1[1][1]; - } - - w1 = w0; - n1 = n0; - e1 = e0; - s1 = s0; - - var group = select(that) - .attr("pointer-events", "none"); - - var overlay = group.selectAll(".overlay") - .attr("cursor", cursors[type]); - - if (exports.event.touches) { - group - .on("touchmove.brush", moved, true) - .on("touchend.brush touchcancel.brush", ended, true); - } else { - var view = select(exports.event.view) - .on("keydown.brush", keydowned, true) - .on("keyup.brush", keyupped, true) - .on("mousemove.brush", moved, true) - .on("mouseup.brush", ended, true); - - dragDisable(exports.event.view); - } - - nopropagation$2(); - interrupt(that); - redraw.call(that); - emit.start(); - - function moved() { - var point1 = mouse(that); - if (shifting && !lockX && !lockY) { - if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true; - else lockX = true; - } - point = point1; - moving = true; - noevent$2(); - move(); - } - - function move() { - var t; - - dx = point[0] - point0[0]; - dy = point[1] - point0[1]; - - switch (mode) { - case MODE_SPACE: - case MODE_DRAG: { - if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; - if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; - break; - } - case MODE_HANDLE: { - if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; - else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; - if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0; - else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy; - break; - } - case MODE_CENTER: { - if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX)); - if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY)); - break; - } - } - - if (e1 < w1) { - signX *= -1; - t = w0, w0 = e0, e0 = t; - t = w1, w1 = e1, e1 = t; - if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); - } - - if (s1 < n1) { - signY *= -1; - t = n0, n0 = s0, s0 = t; - t = n1, n1 = s1, s1 = t; - if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); - } - - if (state.selection) selection$$1 = state.selection; // May be set by brush.move! - if (lockX) w1 = selection$$1[0][0], e1 = selection$$1[1][0]; - if (lockY) n1 = selection$$1[0][1], s1 = selection$$1[1][1]; - - if (selection$$1[0][0] !== w1 - || selection$$1[0][1] !== n1 - || selection$$1[1][0] !== e1 - || selection$$1[1][1] !== s1) { - state.selection = [[w1, n1], [e1, s1]]; - redraw.call(that); - emit.brush(); - } - } - - function ended() { - nopropagation$2(); - if (exports.event.touches) { - if (exports.event.touches.length) return; - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! - group.on("touchmove.brush touchend.brush touchcancel.brush", null); - } else { - yesdrag(exports.event.view, moving); - view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); - } - group.attr("pointer-events", "all"); - overlay.attr("cursor", cursors.overlay); - if (state.selection) selection$$1 = state.selection; // May be set by brush.move (on start)! - if (empty$1(selection$$1)) state.selection = null, redraw.call(that); - emit.end(); - } - - function keydowned() { - switch (exports.event.keyCode) { - case 16: { // SHIFT - shifting = signX && signY; - break; - } - case 18: { // ALT - if (mode === MODE_HANDLE) { - if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; - if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; - mode = MODE_CENTER; - move(); - } - break; - } - case 32: { // SPACE; takes priority over ALT - if (mode === MODE_HANDLE || mode === MODE_CENTER) { - if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; - if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; - mode = MODE_SPACE; - overlay.attr("cursor", cursors.selection); - move(); - } - break; - } - default: return; - } - noevent$2(); - } - - function keyupped() { - switch (exports.event.keyCode) { - case 16: { // SHIFT - if (shifting) { - lockX = lockY = shifting = false; - move(); - } - break; - } - case 18: { // ALT - if (mode === MODE_CENTER) { - if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; - if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; - mode = MODE_HANDLE; - move(); - } - break; - } - case 32: { // SPACE - if (mode === MODE_SPACE) { - if (exports.event.altKey) { - if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; - if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; - mode = MODE_CENTER; - } else { - if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; - if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; - mode = MODE_HANDLE; - } - overlay.attr("cursor", cursors[type]); - move(); - } - break; - } - default: return; - } - noevent$2(); - } - } - - function initialize() { - var state = this.__brush || {selection: null}; - state.extent = extent.apply(this, arguments); - state.dim = dim; - return state; - } - - brush.extent = function(_) { - return arguments.length ? (extent = typeof _ === "function" ? _ : constant$11([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent; - }; - - brush.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$11(!!_), brush) : filter; - }; - - brush.handleSize = function(_) { - return arguments.length ? (handleSize = +_, brush) : handleSize; - }; - - brush.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? brush : value; - }; - - return brush; -} - -var cos = Math.cos; -var sin = Math.sin; -var pi$3 = Math.PI; -var halfPi$2 = pi$3 / 2; -var tau$3 = pi$3 * 2; -var max$1 = Math.max; - -function compareValue(compare) { - return function(a, b) { - return compare( - a.source.value + a.target.value, - b.source.value + b.target.value - ); - }; -} - -var chord = function() { - var padAngle = 0, - sortGroups = null, - sortSubgroups = null, - sortChords = null; - - function chord(matrix) { - var n = matrix.length, - groupSums = [], - groupIndex = range(n), - subgroupIndex = [], - chords = [], - groups = chords.groups = new Array(n), - subgroups = new Array(n * n), - k, - x, - x0, - dx, - i, - j; - - // Compute the sum. - k = 0, i = -1; while (++i < n) { - x = 0, j = -1; while (++j < n) { - x += matrix[i][j]; - } - groupSums.push(x); - subgroupIndex.push(range(n)); - k += x; - } - - // Sort groups… - if (sortGroups) groupIndex.sort(function(a, b) { - return sortGroups(groupSums[a], groupSums[b]); - }); - - // Sort subgroups… - if (sortSubgroups) subgroupIndex.forEach(function(d, i) { - d.sort(function(a, b) { - return sortSubgroups(matrix[i][a], matrix[i][b]); - }); - }); - - // Convert the sum to scaling factor for [0, 2pi]. - // TODO Allow start and end angle to be specified? - // TODO Allow padding to be specified as percentage? - k = max$1(0, tau$3 - padAngle * n) / k; - dx = k ? padAngle : tau$3 / n; - - // Compute the start and end angle for each group and subgroup. - // Note: Opera has a bug reordering object literal properties! - x = 0, i = -1; while (++i < n) { - x0 = x, j = -1; while (++j < n) { - var di = groupIndex[i], - dj = subgroupIndex[di][j], - v = matrix[di][dj], - a0 = x, - a1 = x += v * k; - subgroups[dj * n + di] = { - index: di, - subindex: dj, - startAngle: a0, - endAngle: a1, - value: v - }; - } - groups[di] = { - index: di, - startAngle: x0, - endAngle: x, - value: groupSums[di] - }; - x += dx; - } - - // Generate chords for each (non-empty) subgroup-subgroup link. - i = -1; while (++i < n) { - j = i - 1; while (++j < n) { - var source = subgroups[j * n + i], - target = subgroups[i * n + j]; - if (source.value || target.value) { - chords.push(source.value < target.value - ? {source: target, target: source} - : {source: source, target: target}); - } - } - } - - return sortChords ? chords.sort(sortChords) : chords; - } - - chord.padAngle = function(_) { - return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle; - }; - - chord.sortGroups = function(_) { - return arguments.length ? (sortGroups = _, chord) : sortGroups; - }; - - chord.sortSubgroups = function(_) { - return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups; - }; - - chord.sortChords = function(_) { - return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._; - }; - - return chord; -}; - -var slice$5 = Array.prototype.slice; - -var constant$12 = function(x) { - return function() { - return x; - }; -}; - -function defaultSource(d) { - return d.source; -} - -function defaultTarget(d) { - return d.target; -} - -function defaultRadius$1(d) { - return d.radius; -} - -function defaultStartAngle(d) { - return d.startAngle; -} - -function defaultEndAngle(d) { - return d.endAngle; -} - -var ribbon = function() { - var source = defaultSource, - target = defaultTarget, - radius = defaultRadius$1, - startAngle = defaultStartAngle, - endAngle = defaultEndAngle, - context = null; - - function ribbon() { - var buffer, - argv = slice$5.call(arguments), - s = source.apply(this, argv), - t = target.apply(this, argv), - sr = +radius.apply(this, (argv[0] = s, argv)), - sa0 = startAngle.apply(this, argv) - halfPi$2, - sa1 = endAngle.apply(this, argv) - halfPi$2, - sx0 = sr * cos(sa0), - sy0 = sr * sin(sa0), - tr = +radius.apply(this, (argv[0] = t, argv)), - ta0 = startAngle.apply(this, argv) - halfPi$2, - ta1 = endAngle.apply(this, argv) - halfPi$2; - - if (!context) context = buffer = path(); - - context.moveTo(sx0, sy0); - context.arc(0, 0, sr, sa0, sa1); - if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr? - context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0)); - context.arc(0, 0, tr, ta0, ta1); - } - context.quadraticCurveTo(0, 0, sx0, sy0); - context.closePath(); - - if (buffer) return context = null, buffer + "" || null; - } - - ribbon.radius = function(_) { - return arguments.length ? (radius = typeof _ === "function" ? _ : constant$12(+_), ribbon) : radius; - }; - - ribbon.startAngle = function(_) { - return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$12(+_), ribbon) : startAngle; - }; - - ribbon.endAngle = function(_) { - return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$12(+_), ribbon) : endAngle; - }; - - ribbon.source = function(_) { - return arguments.length ? (source = _, ribbon) : source; - }; - - ribbon.target = function(_) { - return arguments.length ? (target = _, ribbon) : target; - }; - - ribbon.context = function(_) { - return arguments.length ? ((context = _ == null ? null : _), ribbon) : context; - }; - - return ribbon; -}; - -// Adds floating point numbers with twice the normal precision. -// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and -// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) -// 305–363 (1997). -// Code adapted from GeographicLib by Charles F. F. Karney, -// http://geographiclib.sourceforge.net/ - -var adder = function() { - return new Adder; -}; - -function Adder() { - this.reset(); -} - -Adder.prototype = { - constructor: Adder, - reset: function() { - this.s = // rounded value - this.t = 0; // exact error - }, - add: function(y) { - add$1(temp, y, this.t); - add$1(this, temp.s, this.s); - if (this.s) this.t += temp.t; - else this.s = temp.t; - }, - valueOf: function() { - return this.s; - } -}; - -var temp = new Adder; - -function add$1(adder, a, b) { - var x = adder.s = a + b, - bv = x - a, - av = x - bv; - adder.t = (a - av) + (b - bv); -} - -var epsilon$4 = 1e-6; -var epsilon2$2 = 1e-12; -var pi$4 = Math.PI; -var halfPi$3 = pi$4 / 2; -var quarterPi = pi$4 / 4; -var tau$4 = pi$4 * 2; - -var degrees$1 = 180 / pi$4; -var radians = pi$4 / 180; - -var abs = Math.abs; -var atan = Math.atan; -var atan2 = Math.atan2; -var cos$1 = Math.cos; -var ceil = Math.ceil; -var exp = Math.exp; - -var log$1 = Math.log; -var pow$1 = Math.pow; -var sin$1 = Math.sin; -var sign$1 = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }; -var sqrt$1 = Math.sqrt; -var tan = Math.tan; - -function acos(x) { - return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x); -} - -function asin$1(x) { - return x > 1 ? halfPi$3 : x < -1 ? -halfPi$3 : Math.asin(x); -} - -function haversin(x) { - return (x = sin$1(x / 2)) * x; -} - -function noop$2() {} - -function streamGeometry(geometry, stream) { - if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { - streamGeometryType[geometry.type](geometry, stream); - } -} - -var streamObjectType = { - Feature: function(feature, stream) { - streamGeometry(feature.geometry, stream); - }, - FeatureCollection: function(object, stream) { - var features = object.features, i = -1, n = features.length; - while (++i < n) streamGeometry(features[i].geometry, stream); - } -}; - -var streamGeometryType = { - Sphere: function(object, stream) { - stream.sphere(); - }, - Point: function(object, stream) { - object = object.coordinates; - stream.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]); - }, - LineString: function(object, stream) { - streamLine(object.coordinates, stream, 0); - }, - MultiLineString: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) streamLine(coordinates[i], stream, 0); - }, - Polygon: function(object, stream) { - streamPolygon(object.coordinates, stream); - }, - MultiPolygon: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) streamPolygon(coordinates[i], stream); - }, - GeometryCollection: function(object, stream) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) streamGeometry(geometries[i], stream); - } -}; - -function streamLine(coordinates, stream, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - stream.lineStart(); - while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); - stream.lineEnd(); -} - -function streamPolygon(coordinates, stream) { - var i = -1, n = coordinates.length; - stream.polygonStart(); - while (++i < n) streamLine(coordinates[i], stream, 1); - stream.polygonEnd(); -} - -var geoStream = function(object, stream) { - if (object && streamObjectType.hasOwnProperty(object.type)) { - streamObjectType[object.type](object, stream); - } else { - streamGeometry(object, stream); - } -}; - -var areaRingSum = adder(); - -var areaSum = adder(); -var lambda00; -var phi00; -var lambda0; -var cosPhi0; -var sinPhi0; - -var areaStream = { - point: noop$2, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: function() { - areaRingSum.reset(); - areaStream.lineStart = areaRingStart; - areaStream.lineEnd = areaRingEnd; - }, - polygonEnd: function() { - var areaRing = +areaRingSum; - areaSum.add(areaRing < 0 ? tau$4 + areaRing : areaRing); - this.lineStart = this.lineEnd = this.point = noop$2; - }, - sphere: function() { - areaSum.add(tau$4); - } -}; - -function areaRingStart() { - areaStream.point = areaPointFirst; -} - -function areaRingEnd() { - areaPoint(lambda00, phi00); -} - -function areaPointFirst(lambda, phi) { - areaStream.point = areaPoint; - lambda00 = lambda, phi00 = phi; - lambda *= radians, phi *= radians; - lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi); -} - -function areaPoint(lambda, phi) { - lambda *= radians, phi *= radians; - phi = phi / 2 + quarterPi; // half the angular distance from south pole - - // Spherical excess E for a spherical triangle with vertices: south pole, - // previous point, current point. Uses a formula derived from Cagnoli’s - // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). - var dLambda = lambda - lambda0, - sdLambda = dLambda >= 0 ? 1 : -1, - adLambda = sdLambda * dLambda, - cosPhi = cos$1(phi), - sinPhi = sin$1(phi), - k = sinPhi0 * sinPhi, - u = cosPhi0 * cosPhi + k * cos$1(adLambda), - v = k * sdLambda * sin$1(adLambda); - areaRingSum.add(atan2(v, u)); - - // Advance the previous points. - lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi; -} - -var area$2 = function(object) { - areaSum.reset(); - geoStream(object, areaStream); - return areaSum * 2; -}; - -function spherical(cartesian) { - return [atan2(cartesian[1], cartesian[0]), asin$1(cartesian[2])]; -} - -function cartesian(spherical) { - var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi); - return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)]; -} - -function cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -function cartesianCross(a, b) { - return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; -} - -// TODO return a -function cartesianAddInPlace(a, b) { - a[0] += b[0], a[1] += b[1], a[2] += b[2]; -} - -function cartesianScale(vector, k) { - return [vector[0] * k, vector[1] * k, vector[2] * k]; -} - -// TODO return d -function cartesianNormalizeInPlace(d) { - var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l, d[1] /= l, d[2] /= l; -} - -var lambda0$1; -var phi0; -var lambda1; -var phi1; -var lambda2; -var lambda00$1; -var phi00$1; -var p0; -var deltaSum = adder(); -var ranges; -var range$1; - -var boundsStream = { - point: boundsPoint, - lineStart: boundsLineStart, - lineEnd: boundsLineEnd, - polygonStart: function() { - boundsStream.point = boundsRingPoint; - boundsStream.lineStart = boundsRingStart; - boundsStream.lineEnd = boundsRingEnd; - deltaSum.reset(); - areaStream.polygonStart(); - }, - polygonEnd: function() { - areaStream.polygonEnd(); - boundsStream.point = boundsPoint; - boundsStream.lineStart = boundsLineStart; - boundsStream.lineEnd = boundsLineEnd; - if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); - else if (deltaSum > epsilon$4) phi1 = 90; - else if (deltaSum < -epsilon$4) phi0 = -90; - range$1[0] = lambda0$1, range$1[1] = lambda1; - } -}; - -function boundsPoint(lambda, phi) { - ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]); - if (phi < phi0) phi0 = phi; - if (phi > phi1) phi1 = phi; -} - -function linePoint(lambda, phi) { - var p = cartesian([lambda * radians, phi * radians]); - if (p0) { - var normal = cartesianCross(p0, p), - equatorial = [normal[1], -normal[0], 0], - inflection = cartesianCross(equatorial, normal); - cartesianNormalizeInPlace(inflection); - inflection = spherical(inflection); - var delta = lambda - lambda2, - sign$$1 = delta > 0 ? 1 : -1, - lambdai = inflection[0] * degrees$1 * sign$$1, - phii, - antimeridian = abs(delta) > 180; - if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { - phii = inflection[1] * degrees$1; - if (phii > phi1) phi1 = phii; - } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { - phii = -inflection[1] * degrees$1; - if (phii < phi0) phi0 = phii; - } else { - if (phi < phi0) phi0 = phi; - if (phi > phi1) phi1 = phi; - } - if (antimeridian) { - if (lambda < lambda2) { - if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; - } else { - if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; - } - } else { - if (lambda1 >= lambda0$1) { - if (lambda < lambda0$1) lambda0$1 = lambda; - if (lambda > lambda1) lambda1 = lambda; - } else { - if (lambda > lambda2) { - if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; - } else { - if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; - } - } - } - } else { - boundsPoint(lambda, phi); - } - p0 = p, lambda2 = lambda; -} - -function boundsLineStart() { - boundsStream.point = linePoint; -} - -function boundsLineEnd() { - range$1[0] = lambda0$1, range$1[1] = lambda1; - boundsStream.point = boundsPoint; - p0 = null; -} - -function boundsRingPoint(lambda, phi) { - if (p0) { - var delta = lambda - lambda2; - deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta); - } else { - lambda00$1 = lambda, phi00$1 = phi; - } - areaStream.point(lambda, phi); - linePoint(lambda, phi); -} - -function boundsRingStart() { - areaStream.lineStart(); -} - -function boundsRingEnd() { - boundsRingPoint(lambda00$1, phi00$1); - areaStream.lineEnd(); - if (abs(deltaSum) > epsilon$4) lambda0$1 = -(lambda1 = 180); - range$1[0] = lambda0$1, range$1[1] = lambda1; - p0 = null; -} - -// Finds the left-right distance between two longitudes. -// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want -// the distance between ±180° to be 360°. -function angle(lambda0, lambda1) { - return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1; -} - -function rangeCompare(a, b) { - return a[0] - b[0]; -} - -function rangeContains(range, x) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; -} - -var bounds = function(feature) { - var i, n, a, b, merged, deltaMax, delta; - - phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity); - ranges = []; - geoStream(feature, boundsStream); - - // First, sort ranges by their minimum longitudes. - if (n = ranges.length) { - ranges.sort(rangeCompare); - - // Then, merge any ranges that overlap. - for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) { - b = ranges[i]; - if (rangeContains(a, b[0]) || rangeContains(a, b[1])) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - - // Finally, find the largest gap between the merged ranges. - // The final bounding box will be the inverse of this gap. - for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) { - b = merged[i]; - if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; - } - } - - ranges = range$1 = null; - - return lambda0$1 === Infinity || phi0 === Infinity - ? [[NaN, NaN], [NaN, NaN]] - : [[lambda0$1, phi0], [lambda1, phi1]]; -}; - -var W0; -var W1; -var X0; -var Y0; -var Z0; -var X1; -var Y1; -var Z1; -var X2; -var Y2; -var Z2; -var lambda00$2; -var phi00$2; -var x0; -var y0; -var z0; // previous point - -var centroidStream = { - sphere: noop$2, - point: centroidPoint, - lineStart: centroidLineStart, - lineEnd: centroidLineEnd, - polygonStart: function() { - centroidStream.lineStart = centroidRingStart; - centroidStream.lineEnd = centroidRingEnd; - }, - polygonEnd: function() { - centroidStream.lineStart = centroidLineStart; - centroidStream.lineEnd = centroidLineEnd; - } -}; - -// Arithmetic mean of Cartesian vectors. -function centroidPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos$1(phi); - centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)); -} - -function centroidPointCartesian(x, y, z) { - ++W0; - X0 += (x - X0) / W0; - Y0 += (y - Y0) / W0; - Z0 += (z - Z0) / W0; -} - -function centroidLineStart() { - centroidStream.point = centroidLinePointFirst; -} - -function centroidLinePointFirst(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos$1(phi); - x0 = cosPhi * cos$1(lambda); - y0 = cosPhi * sin$1(lambda); - z0 = sin$1(phi); - centroidStream.point = centroidLinePoint; - centroidPointCartesian(x0, y0, z0); -} - -function centroidLinePoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos$1(phi), - x = cosPhi * cos$1(lambda), - y = cosPhi * sin$1(lambda), - z = sin$1(phi), - w = atan2(sqrt$1((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); - W1 += w; - X1 += w * (x0 + (x0 = x)); - Y1 += w * (y0 + (y0 = y)); - Z1 += w * (z0 + (z0 = z)); - centroidPointCartesian(x0, y0, z0); -} - -function centroidLineEnd() { - centroidStream.point = centroidPoint; -} - -// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, -// J. Applied Mechanics 42, 239 (1975). -function centroidRingStart() { - centroidStream.point = centroidRingPointFirst; -} - -function centroidRingEnd() { - centroidRingPoint(lambda00$2, phi00$2); - centroidStream.point = centroidPoint; -} - -function centroidRingPointFirst(lambda, phi) { - lambda00$2 = lambda, phi00$2 = phi; - lambda *= radians, phi *= radians; - centroidStream.point = centroidRingPoint; - var cosPhi = cos$1(phi); - x0 = cosPhi * cos$1(lambda); - y0 = cosPhi * sin$1(lambda); - z0 = sin$1(phi); - centroidPointCartesian(x0, y0, z0); -} - -function centroidRingPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos$1(phi), - x = cosPhi * cos$1(lambda), - y = cosPhi * sin$1(lambda), - z = sin$1(phi), - cx = y0 * z - z0 * y, - cy = z0 * x - x0 * z, - cz = x0 * y - y0 * x, - m = sqrt$1(cx * cx + cy * cy + cz * cz), - u = x0 * x + y0 * y + z0 * z, - v = m && -acos(u) / m, // area weight - w = atan2(m, u); // line weight - X2 += v * cx; - Y2 += v * cy; - Z2 += v * cz; - W1 += w; - X1 += w * (x0 + (x0 = x)); - Y1 += w * (y0 + (y0 = y)); - Z1 += w * (z0 + (z0 = z)); - centroidPointCartesian(x0, y0, z0); -} - -var centroid$1 = function(object) { - W0 = W1 = - X0 = Y0 = Z0 = - X1 = Y1 = Z1 = - X2 = Y2 = Z2 = 0; - geoStream(object, centroidStream); - - var x = X2, - y = Y2, - z = Z2, - m = x * x + y * y + z * z; - - // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid. - if (m < epsilon2$2) { - x = X1, y = Y1, z = Z1; - // If the feature has zero length, fall back to arithmetic mean of point vectors. - if (W1 < epsilon$4) x = X0, y = Y0, z = Z0; - m = x * x + y * y + z * z; - // If the feature still has an undefined ccentroid, then return. - if (m < epsilon2$2) return [NaN, NaN]; - } - - return [atan2(y, x) * degrees$1, asin$1(z / sqrt$1(m)) * degrees$1]; -}; - -var constant$13 = function(x) { - return function() { - return x; - }; -}; - -var compose = function(a, b) { - - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - - return compose; -}; - -function rotationIdentity(lambda, phi) { - return [lambda > pi$4 ? lambda - tau$4 : lambda < -pi$4 ? lambda + tau$4 : lambda, phi]; -} - -rotationIdentity.invert = rotationIdentity; - -function rotateRadians(deltaLambda, deltaPhi, deltaGamma) { - return (deltaLambda %= tau$4) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) - : rotationLambda(deltaLambda)) - : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) - : rotationIdentity); -} - -function forwardRotationLambda(deltaLambda) { - return function(lambda, phi) { - return lambda += deltaLambda, [lambda > pi$4 ? lambda - tau$4 : lambda < -pi$4 ? lambda + tau$4 : lambda, phi]; - }; -} - -function rotationLambda(deltaLambda) { - var rotation = forwardRotationLambda(deltaLambda); - rotation.invert = forwardRotationLambda(-deltaLambda); - return rotation; -} - -function rotationPhiGamma(deltaPhi, deltaGamma) { - var cosDeltaPhi = cos$1(deltaPhi), - sinDeltaPhi = sin$1(deltaPhi), - cosDeltaGamma = cos$1(deltaGamma), - sinDeltaGamma = sin$1(deltaGamma); - - function rotation(lambda, phi) { - var cosPhi = cos$1(phi), - x = cos$1(lambda) * cosPhi, - y = sin$1(lambda) * cosPhi, - z = sin$1(phi), - k = z * cosDeltaPhi + x * sinDeltaPhi; - return [ - atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), - asin$1(k * cosDeltaGamma + y * sinDeltaGamma) - ]; - } - - rotation.invert = function(lambda, phi) { - var cosPhi = cos$1(phi), - x = cos$1(lambda) * cosPhi, - y = sin$1(lambda) * cosPhi, - z = sin$1(phi), - k = z * cosDeltaGamma - y * sinDeltaGamma; - return [ - atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), - asin$1(k * cosDeltaPhi - x * sinDeltaPhi) - ]; - }; - - return rotation; -} - -var rotation = function(rotate) { - rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0); - - function forward(coordinates) { - coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians); - return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; - } - - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians); - return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; - }; - - return forward; -}; - -// Generates a circle centered at [0°, 0°], with a given radius and precision. -function circleStream(stream, radius, delta, direction, t0, t1) { - if (!delta) return; - var cosRadius = cos$1(radius), - sinRadius = sin$1(radius), - step = direction * delta; - if (t0 == null) { - t0 = radius + direction * tau$4; - t1 = radius - step / 2; - } else { - t0 = circleRadius(cosRadius, t0); - t1 = circleRadius(cosRadius, t1); - if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$4; - } - for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) { - point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]); - stream.point(point[0], point[1]); - } -} - -// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0]. -function circleRadius(cosRadius, point) { - point = cartesian(point), point[0] -= cosRadius; - cartesianNormalizeInPlace(point); - var radius = acos(-point[1]); - return ((-point[2] < 0 ? -radius : radius) + tau$4 - epsilon$4) % tau$4; -} - -var circle$1 = function() { - var center = constant$13([0, 0]), - radius = constant$13(90), - precision = constant$13(6), - ring, - rotate, - stream = {point: point}; - - function point(x, y) { - ring.push(x = rotate(x, y)); - x[0] *= degrees$1, x[1] *= degrees$1; - } - - function circle() { - var c = center.apply(this, arguments), - r = radius.apply(this, arguments) * radians, - p = precision.apply(this, arguments) * radians; - ring = []; - rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert; - circleStream(stream, r, p, 1); - c = {type: "Polygon", coordinates: [ring]}; - ring = rotate = null; - return c; - } - - circle.center = function(_) { - return arguments.length ? (center = typeof _ === "function" ? _ : constant$13([+_[0], +_[1]]), circle) : center; - }; - - circle.radius = function(_) { - return arguments.length ? (radius = typeof _ === "function" ? _ : constant$13(+_), circle) : radius; - }; - - circle.precision = function(_) { - return arguments.length ? (precision = typeof _ === "function" ? _ : constant$13(+_), circle) : precision; - }; - - return circle; -}; - -var clipBuffer = function() { - var lines = [], - line; - return { - point: function(x, y) { - line.push([x, y]); - }, - lineStart: function() { - lines.push(line = []); - }, - lineEnd: noop$2, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - }, - result: function() { - var result = lines; - lines = []; - line = null; - return result; - } - }; -}; - -var clipLine = function(a, b, x0, y0, x1, y1) { - var ax = a[0], - ay = a[1], - bx = b[0], - by = b[1], - t0 = 0, - t1 = 1, - dx = bx - ax, - dy = by - ay, - r; - - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; - if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; - return true; -}; - -var pointEqual = function(a, b) { - return abs(a[0] - b[0]) < epsilon$4 && abs(a[1] - b[1]) < epsilon$4; -}; - -function Intersection(point, points, other, entry) { - this.x = point; - this.z = points; - this.o = other; // another intersection - this.e = entry; // is an entry? - this.v = false; // visited - this.n = this.p = null; // next & previous -} - -// A generalized polygon clipping algorithm: given a polygon that has been cut -// into its visible line segments, and rejoins the segments by interpolating -// along the clip edge. -var clipPolygon = function(segments, compareIntersection, startInside, interpolate, stream) { - var subject = [], - clip = [], - i, - n; - - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n], x; - - // If the first and last points of a segment are coincident, then treat as a - // closed ring. TODO if all rings are closed, then the winding order of the - // exterior ring should be checked. - if (pointEqual(p0, p1)) { - stream.lineStart(); - for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]); - stream.lineEnd(); - return; - } - - subject.push(x = new Intersection(p0, segment, null, true)); - clip.push(x.o = new Intersection(p0, null, x, false)); - subject.push(x = new Intersection(p1, segment, null, false)); - clip.push(x.o = new Intersection(p1, null, x, true)); - }); - - if (!subject.length) return; - - clip.sort(compareIntersection); - link$1(subject); - link$1(clip); - - for (i = 0, n = clip.length; i < n; ++i) { - clip[i].e = startInside = !startInside; - } - - var start = subject[0], - points, - point; - - while (1) { - // Find first unvisited intersection. - var current = start, - isSubject = true; - while (current.v) if ((current = current.n) === start) return; - points = current.z; - stream.lineStart(); - do { - current.v = current.o.v = true; - if (current.e) { - if (isSubject) { - for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.n.x, 1, stream); - } - current = current.n; - } else { - if (isSubject) { - points = current.p.z; - for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.p.x, -1, stream); - } - current = current.p; - } - current = current.o; - points = current.z; - isSubject = !isSubject; - } while (!current.v); - stream.lineEnd(); - } -}; - -function link$1(array) { - if (!(n = array.length)) return; - var n, - i = 0, - a = array[0], - b; - while (++i < n) { - a.n = b = array[i]; - b.p = a; - a = b; - } - a.n = b = array[0]; - b.p = a; -} - -var clipMax = 1e9; -var clipMin = -clipMax; - -// TODO Use d3-polygon’s polygonContains here for the ring check? -// TODO Eliminate duplicate buffering in clipBuffer and polygon.push? - -function clipExtent(x0, y0, x1, y1) { - - function visible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - - function interpolate(from, to, direction, stream) { - var a = 0, a1 = 0; - if (from == null - || (a = corner(from, direction)) !== (a1 = corner(to, direction)) - || comparePoint(from, to) < 0 ^ direction > 0) { - do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - while ((a = (a + direction + 4) % 4) !== a1); - } else { - stream.point(to[0], to[1]); - } - } - - function corner(p, direction) { - return abs(p[0] - x0) < epsilon$4 ? direction > 0 ? 0 : 3 - : abs(p[0] - x1) < epsilon$4 ? direction > 0 ? 2 : 1 - : abs(p[1] - y0) < epsilon$4 ? direction > 0 ? 1 : 0 - : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon - } - - function compareIntersection(a, b) { - return comparePoint(a.x, b.x); - } - - function comparePoint(a, b) { - var ca = corner(a, 1), - cb = corner(b, 1); - return ca !== cb ? ca - cb - : ca === 0 ? b[1] - a[1] - : ca === 1 ? a[0] - b[0] - : ca === 2 ? a[1] - b[1] - : b[0] - a[0]; - } - - return function(stream) { - var activeStream = stream, - bufferStream = clipBuffer(), - segments, - polygon, - ring, - x__, y__, v__, // first point - x_, y_, v_, // previous point - first, - clean; - - var clipStream = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: polygonStart, - polygonEnd: polygonEnd - }; - - function point(x, y) { - if (visible(x, y)) activeStream.point(x, y); - } - - function polygonInside() { - var winding = 0; - - for (var i = 0, n = polygon.length; i < n; ++i) { - for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) { - a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1]; - if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; } - else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; } - } - } - - return winding; - } - - // Buffer geometry within a polygon and then clip it en masse. - function polygonStart() { - activeStream = bufferStream, segments = [], polygon = [], clean = true; - } - - function polygonEnd() { - var startInside = polygonInside(), - cleanInside = clean && startInside, - visible = (segments = merge(segments)).length; - if (cleanInside || visible) { - stream.polygonStart(); - if (cleanInside) { - stream.lineStart(); - interpolate(null, null, 1, stream); - stream.lineEnd(); - } - if (visible) { - clipPolygon(segments, compareIntersection, startInside, interpolate, stream); - } - stream.polygonEnd(); - } - activeStream = stream, segments = polygon = ring = null; - } - - function lineStart() { - clipStream.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - - // TODO rather than special-case polygons, simply handle them separately. - // Ideally, coincident intersection points should be jittered to avoid - // clipping issues. - function lineEnd() { - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferStream.rejoin(); - segments.push(bufferStream.result()); - } - clipStream.point = point; - if (v_) activeStream.lineEnd(); - } - - function linePoint(x, y) { - var v = visible(x, y); - if (polygon) ring.push([x, y]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - activeStream.lineStart(); - activeStream.point(x, y); - } - } else { - if (v && v_) activeStream.point(x, y); - else { - var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))], - b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))]; - if (clipLine(a, b, x0, y0, x1, y1)) { - if (!v_) { - activeStream.lineStart(); - activeStream.point(a[0], a[1]); - } - activeStream.point(b[0], b[1]); - if (!v) activeStream.lineEnd(); - clean = false; - } else if (v) { - activeStream.lineStart(); - activeStream.point(x, y); - clean = false; - } - } - } - x_ = x, y_ = y, v_ = v; - } - - return clipStream; - }; -} - -var extent$1 = function() { - var x0 = 0, - y0 = 0, - x1 = 960, - y1 = 500, - cache, - cacheStream, - clip; - - return clip = { - stream: function(stream) { - return cache && cacheStream === stream ? cache : cache = clipExtent(x0, y0, x1, y1)(cacheStream = stream); - }, - extent: function(_) { - return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]]; - } - }; -}; - -var lengthSum = adder(); -var lambda0$2; -var sinPhi0$1; -var cosPhi0$1; - -var lengthStream = { - sphere: noop$2, - point: noop$2, - lineStart: lengthLineStart, - lineEnd: noop$2, - polygonStart: noop$2, - polygonEnd: noop$2 -}; - -function lengthLineStart() { - lengthStream.point = lengthPointFirst; - lengthStream.lineEnd = lengthLineEnd; -} - -function lengthLineEnd() { - lengthStream.point = lengthStream.lineEnd = noop$2; -} - -function lengthPointFirst(lambda, phi) { - lambda *= radians, phi *= radians; - lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi); - lengthStream.point = lengthPoint; -} - -function lengthPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var sinPhi = sin$1(phi), - cosPhi = cos$1(phi), - delta = abs(lambda - lambda0$2), - cosDelta = cos$1(delta), - sinDelta = sin$1(delta), - x = cosPhi * sinDelta, - y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta, - z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta; - lengthSum.add(atan2(sqrt$1(x * x + y * y), z)); - lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi; -} - -var length$2 = function(object) { - lengthSum.reset(); - geoStream(object, lengthStream); - return +lengthSum; -}; - -var coordinates = [null, null]; -var object$1 = {type: "LineString", coordinates: coordinates}; - -var distance = function(a, b) { - coordinates[0] = a; - coordinates[1] = b; - return length$2(object$1); -}; - -function graticuleX(y0, y1, dy) { - var y = range(y0, y1 - epsilon$4, dy).concat(y1); - return function(x) { return y.map(function(y) { return [x, y]; }); }; -} - -function graticuleY(x0, x1, dx) { - var x = range(x0, x1 - epsilon$4, dx).concat(x1); - return function(y) { return x.map(function(x) { return [x, y]; }); }; -} - -var graticule = function() { - var x1, x0, X1, X0, - y1, y0, Y1, Y0, - dx = 10, dy = dx, DX = 90, DY = 360, - x, y, X, Y, - precision = 2.5; - - function graticule() { - return {type: "MultiLineString", coordinates: lines()}; - } - - function lines() { - return range(ceil(X0 / DX) * DX, X1, DX).map(X) - .concat(range(ceil(Y0 / DY) * DY, Y1, DY).map(Y)) - .concat(range(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$4; }).map(x)) - .concat(range(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$4; }).map(y)); - } - - graticule.lines = function() { - return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; }); - }; - - graticule.outline = function() { - return { - type: "Polygon", - coordinates: [ - X(X0).concat( - Y(Y1).slice(1), - X(X1).reverse().slice(1), - Y(Y0).reverse().slice(1)) - ] - }; - }; - - graticule.extent = function(_) { - if (!arguments.length) return graticule.extentMinor(); - return graticule.extentMajor(_).extentMinor(_); - }; - - graticule.extentMajor = function(_) { - if (!arguments.length) return [[X0, Y0], [X1, Y1]]; - X0 = +_[0][0], X1 = +_[1][0]; - Y0 = +_[0][1], Y1 = +_[1][1]; - if (X0 > X1) _ = X0, X0 = X1, X1 = _; - if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; - return graticule.precision(precision); - }; - - graticule.extentMinor = function(_) { - if (!arguments.length) return [[x0, y0], [x1, y1]]; - x0 = +_[0][0], x1 = +_[1][0]; - y0 = +_[0][1], y1 = +_[1][1]; - if (x0 > x1) _ = x0, x0 = x1, x1 = _; - if (y0 > y1) _ = y0, y0 = y1, y1 = _; - return graticule.precision(precision); - }; - - graticule.step = function(_) { - if (!arguments.length) return graticule.stepMinor(); - return graticule.stepMajor(_).stepMinor(_); - }; - - graticule.stepMajor = function(_) { - if (!arguments.length) return [DX, DY]; - DX = +_[0], DY = +_[1]; - return graticule; - }; - - graticule.stepMinor = function(_) { - if (!arguments.length) return [dx, dy]; - dx = +_[0], dy = +_[1]; - return graticule; - }; - - graticule.precision = function(_) { - if (!arguments.length) return precision; - precision = +_; - x = graticuleX(y0, y1, 90); - y = graticuleY(x0, x1, precision); - X = graticuleX(Y0, Y1, 90); - Y = graticuleY(X0, X1, precision); - return graticule; - }; - - return graticule - .extentMajor([[-180, -90 + epsilon$4], [180, 90 - epsilon$4]]) - .extentMinor([[-180, -80 - epsilon$4], [180, 80 + epsilon$4]]); -}; - -var interpolate$2 = function(a, b) { - var x0 = a[0] * radians, - y0 = a[1] * radians, - x1 = b[0] * radians, - y1 = b[1] * radians, - cy0 = cos$1(y0), - sy0 = sin$1(y0), - cy1 = cos$1(y1), - sy1 = sin$1(y1), - kx0 = cy0 * cos$1(x0), - ky0 = cy0 * sin$1(x0), - kx1 = cy1 * cos$1(x1), - ky1 = cy1 * sin$1(x1), - d = 2 * asin$1(sqrt$1(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))), - k = sin$1(d); - - var interpolate = d ? function(t) { - var B = sin$1(t *= d) / k, - A = sin$1(d - t) / k, - x = A * kx0 + B * kx1, - y = A * ky0 + B * ky1, - z = A * sy0 + B * sy1; - return [ - atan2(y, x) * degrees$1, - atan2(z, sqrt$1(x * x + y * y)) * degrees$1 - ]; - } : function() { - return [x0 * degrees$1, y0 * degrees$1]; - }; - - interpolate.distance = d; - - return interpolate; -}; - -var identity$7 = function(x) { - return x; -}; - -var areaSum$1 = adder(); -var areaRingSum$1 = adder(); -var x00; -var y00; -var x0$1; -var y0$1; - -var areaStream$1 = { - point: noop$2, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: function() { - areaStream$1.lineStart = areaRingStart$1; - areaStream$1.lineEnd = areaRingEnd$1; - }, - polygonEnd: function() { - areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2; - areaSum$1.add(abs(areaRingSum$1)); - areaRingSum$1.reset(); - }, - result: function() { - var area = areaSum$1 / 2; - areaSum$1.reset(); - return area; - } -}; - -function areaRingStart$1() { - areaStream$1.point = areaPointFirst$1; -} - -function areaPointFirst$1(x, y) { - areaStream$1.point = areaPoint$1; - x00 = x0$1 = x, y00 = y0$1 = y; -} - -function areaPoint$1(x, y) { - areaRingSum$1.add(y0$1 * x - x0$1 * y); - x0$1 = x, y0$1 = y; -} - -function areaRingEnd$1() { - areaPoint$1(x00, y00); -} - -var x0$2 = Infinity; -var y0$2 = x0$2; -var x1 = -x0$2; -var y1 = x1; - -var boundsStream$1 = { - point: boundsPoint$1, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: noop$2, - polygonEnd: noop$2, - result: function() { - var bounds = [[x0$2, y0$2], [x1, y1]]; - x1 = y1 = -(y0$2 = x0$2 = Infinity); - return bounds; - } -}; - -function boundsPoint$1(x, y) { - if (x < x0$2) x0$2 = x; - if (x > x1) x1 = x; - if (y < y0$2) y0$2 = y; - if (y > y1) y1 = y; -} - -// TODO Enforce positive area for exterior, negative area for interior? - -var X0$1 = 0; -var Y0$1 = 0; -var Z0$1 = 0; -var X1$1 = 0; -var Y1$1 = 0; -var Z1$1 = 0; -var X2$1 = 0; -var Y2$1 = 0; -var Z2$1 = 0; -var x00$1; -var y00$1; -var x0$3; -var y0$3; - -var centroidStream$1 = { - point: centroidPoint$1, - lineStart: centroidLineStart$1, - lineEnd: centroidLineEnd$1, - polygonStart: function() { - centroidStream$1.lineStart = centroidRingStart$1; - centroidStream$1.lineEnd = centroidRingEnd$1; - }, - polygonEnd: function() { - centroidStream$1.point = centroidPoint$1; - centroidStream$1.lineStart = centroidLineStart$1; - centroidStream$1.lineEnd = centroidLineEnd$1; - }, - result: function() { - var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1] - : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1] - : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1] - : [NaN, NaN]; - X0$1 = Y0$1 = Z0$1 = - X1$1 = Y1$1 = Z1$1 = - X2$1 = Y2$1 = Z2$1 = 0; - return centroid; - } -}; - -function centroidPoint$1(x, y) { - X0$1 += x; - Y0$1 += y; - ++Z0$1; -} - -function centroidLineStart$1() { - centroidStream$1.point = centroidPointFirstLine; -} - -function centroidPointFirstLine(x, y) { - centroidStream$1.point = centroidPointLine; - centroidPoint$1(x0$3 = x, y0$3 = y); -} - -function centroidPointLine(x, y) { - var dx = x - x0$3, dy = y - y0$3, z = sqrt$1(dx * dx + dy * dy); - X1$1 += z * (x0$3 + x) / 2; - Y1$1 += z * (y0$3 + y) / 2; - Z1$1 += z; - centroidPoint$1(x0$3 = x, y0$3 = y); -} - -function centroidLineEnd$1() { - centroidStream$1.point = centroidPoint$1; -} - -function centroidRingStart$1() { - centroidStream$1.point = centroidPointFirstRing; -} - -function centroidRingEnd$1() { - centroidPointRing(x00$1, y00$1); -} - -function centroidPointFirstRing(x, y) { - centroidStream$1.point = centroidPointRing; - centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y); -} - -function centroidPointRing(x, y) { - var dx = x - x0$3, - dy = y - y0$3, - z = sqrt$1(dx * dx + dy * dy); - - X1$1 += z * (x0$3 + x) / 2; - Y1$1 += z * (y0$3 + y) / 2; - Z1$1 += z; - - z = y0$3 * x - x0$3 * y; - X2$1 += z * (x0$3 + x); - Y2$1 += z * (y0$3 + y); - Z2$1 += z * 3; - centroidPoint$1(x0$3 = x, y0$3 = y); -} - -function PathContext(context) { - this._context = context; -} - -PathContext.prototype = { - _radius: 4.5, - pointRadius: function(_) { - return this._radius = _, this; - }, - polygonStart: function() { - this._line = 0; - }, - polygonEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._line === 0) this._context.closePath(); - this._point = NaN; - }, - point: function(x, y) { - switch (this._point) { - case 0: { - this._context.moveTo(x, y); - this._point = 1; - break; - } - case 1: { - this._context.lineTo(x, y); - break; - } - default: { - this._context.moveTo(x + this._radius, y); - this._context.arc(x, y, this._radius, 0, tau$4); - break; - } - } - }, - result: noop$2 -}; - -function PathString() { - this._string = []; -} - -PathString.prototype = { - _circle: circle$2(4.5), - pointRadius: function(_) { - return this._circle = circle$2(_), this; - }, - polygonStart: function() { - this._line = 0; - }, - polygonEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._line === 0) this._string.push("Z"); - this._point = NaN; - }, - point: function(x, y) { - switch (this._point) { - case 0: { - this._string.push("M", x, ",", y); - this._point = 1; - break; - } - case 1: { - this._string.push("L", x, ",", y); - break; - } - default: { - this._string.push("M", x, ",", y, this._circle); - break; - } - } - }, - result: function() { - if (this._string.length) { - var result = this._string.join(""); - this._string = []; - return result; - } - } -}; - -function circle$2(radius) { - return "m0," + radius - + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius - + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius - + "z"; -} - -var index$3 = function() { - var pointRadius = 4.5, - projection, - projectionStream, - context, - contextStream; - - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - geoStream(object, projectionStream(contextStream)); - } - return contextStream.result(); - } - - path.area = function(object) { - geoStream(object, projectionStream(areaStream$1)); - return areaStream$1.result(); - }; - - path.bounds = function(object) { - geoStream(object, projectionStream(boundsStream$1)); - return boundsStream$1.result(); - }; - - path.centroid = function(object) { - geoStream(object, projectionStream(centroidStream$1)); - return centroidStream$1.result(); - }; - - path.projection = function(_) { - return arguments.length ? (projectionStream = (projection = _) == null ? identity$7 : _.stream, path) : projection; - }; - - path.context = function(_) { - if (!arguments.length) return context; - contextStream = (context = _) == null ? new PathString : new PathContext(_); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return path; - }; - - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - - return path.projection(null).context(null); -}; - -var sum$2 = adder(); - -var polygonContains = function(polygon, point) { - var lambda = point[0], - phi = point[1], - normal = [sin$1(lambda), -cos$1(lambda), 0], - angle = 0, - winding = 0; - - sum$2.reset(); - - for (var i = 0, n = polygon.length; i < n; ++i) { - if (!(m = (ring = polygon[i]).length)) continue; - var ring, - m, - point0 = ring[m - 1], - lambda0 = point0[0], - phi0 = point0[1] / 2 + quarterPi, - sinPhi0 = sin$1(phi0), - cosPhi0 = cos$1(phi0); - - for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) { - var point1 = ring[j], - lambda1 = point1[0], - phi1 = point1[1] / 2 + quarterPi, - sinPhi1 = sin$1(phi1), - cosPhi1 = cos$1(phi1), - delta = lambda1 - lambda0, - sign$$1 = delta >= 0 ? 1 : -1, - absDelta = sign$$1 * delta, - antimeridian = absDelta > pi$4, - k = sinPhi0 * sinPhi1; - - sum$2.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta))); - angle += antimeridian ? delta + sign$$1 * tau$4 : delta; - - // Are the longitudes either side of the point’s meridian (lambda), - // and are the latitudes smaller than the parallel (phi)? - if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) { - var arc = cartesianCross(cartesian(point0), cartesian(point1)); - cartesianNormalizeInPlace(arc); - var intersection = cartesianCross(normal, arc); - cartesianNormalizeInPlace(intersection); - var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin$1(intersection[2]); - if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) { - winding += antimeridian ^ delta >= 0 ? 1 : -1; - } - } - } - } - - // First, determine whether the South pole is inside or outside: - // - // It is inside if: - // * the polygon winds around it in a clockwise direction. - // * the polygon does not (cumulatively) wind around it, but has a negative - // (counter-clockwise) area. - // - // Second, count the (signed) number of times a segment crosses a lambda - // from the point to the South pole. If it is zero, then the point is the - // same side as the South pole. - - return (angle < -epsilon$4 || angle < epsilon$4 && sum$2 < -epsilon$4) ^ (winding & 1); -}; - -var clip = function(pointVisible, clipLine, interpolate, start) { - return function(rotate, sink) { - var line = clipLine(sink), - rotatedStart = rotate.invert(start[0], start[1]), - ringBuffer = clipBuffer(), - ringSink = clipLine(ringBuffer), - polygonStarted = false, - polygon, - segments, - ring; - - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - segments = merge(segments); - var startInside = polygonContains(polygon, rotatedStart); - if (segments.length) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - clipPolygon(segments, compareIntersection, startInside, interpolate, sink); - } else if (startInside) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - sink.lineStart(); - interpolate(null, null, 1, sink); - sink.lineEnd(); - } - if (polygonStarted) sink.polygonEnd(), polygonStarted = false; - segments = polygon = null; - }, - sphere: function() { - sink.polygonStart(); - sink.lineStart(); - interpolate(null, null, 1, sink); - sink.lineEnd(); - sink.polygonEnd(); - } - }; - - function point(lambda, phi) { - var point = rotate(lambda, phi); - if (pointVisible(lambda = point[0], phi = point[1])) sink.point(lambda, phi); - } - - function pointLine(lambda, phi) { - var point = rotate(lambda, phi); - line.point(point[0], point[1]); - } - - function lineStart() { - clip.point = pointLine; - line.lineStart(); - } - - function lineEnd() { - clip.point = point; - line.lineEnd(); - } - - function pointRing(lambda, phi) { - ring.push([lambda, phi]); - var point = rotate(lambda, phi); - ringSink.point(point[0], point[1]); - } - - function ringStart() { - ringSink.lineStart(); - ring = []; - } - - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringSink.lineEnd(); - - var clean = ringSink.clean(), - ringSegments = ringBuffer.result(), - i, n = ringSegments.length, m, - segment, - point; - - ring.pop(); - polygon.push(ring); - ring = null; - - if (!n) return; - - // No intersections. - if (clean & 1) { - segment = ringSegments[0]; - if ((m = segment.length - 1) > 0) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - sink.lineStart(); - for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]); - sink.lineEnd(); - } - return; - } - - // Rejoin connected segments. - // TODO reuse ringBuffer.rejoin()? - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - - segments.push(ringSegments.filter(validSegment)); - } - - return clip; - }; -}; - -function validSegment(segment) { - return segment.length > 1; -} - -// Intersections are sorted along the clip edge. For both antimeridian cutting -// and circle clipping, the same comparison is used. -function compareIntersection(a, b) { - return ((a = a.x)[0] < 0 ? a[1] - halfPi$3 - epsilon$4 : halfPi$3 - a[1]) - - ((b = b.x)[0] < 0 ? b[1] - halfPi$3 - epsilon$4 : halfPi$3 - b[1]); -} - -var clipAntimeridian = clip( - function() { return true; }, - clipAntimeridianLine, - clipAntimeridianInterpolate, - [-pi$4, -halfPi$3] -); - -// Takes a line and cuts into visible segments. Return values: 0 - there were -// intersections or the line was empty; 1 - no intersections; 2 - there were -// intersections, and the first and last segments should be rejoined. -function clipAntimeridianLine(stream) { - var lambda0 = NaN, - phi0 = NaN, - sign0 = NaN, - clean; // no intersections - - return { - lineStart: function() { - stream.lineStart(); - clean = 1; - }, - point: function(lambda1, phi1) { - var sign1 = lambda1 > 0 ? pi$4 : -pi$4, - delta = abs(lambda1 - lambda0); - if (abs(delta - pi$4) < epsilon$4) { // line crosses a pole - stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$3 : -halfPi$3); - stream.point(sign0, phi0); - stream.lineEnd(); - stream.lineStart(); - stream.point(sign1, phi0); - stream.point(lambda1, phi0); - clean = 0; - } else if (sign0 !== sign1 && delta >= pi$4) { // line crosses antimeridian - if (abs(lambda0 - sign0) < epsilon$4) lambda0 -= sign0 * epsilon$4; // handle degeneracies - if (abs(lambda1 - sign1) < epsilon$4) lambda1 -= sign1 * epsilon$4; - phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1); - stream.point(sign0, phi0); - stream.lineEnd(); - stream.lineStart(); - stream.point(sign1, phi0); - clean = 0; - } - stream.point(lambda0 = lambda1, phi0 = phi1); - sign0 = sign1; - }, - lineEnd: function() { - stream.lineEnd(); - lambda0 = phi0 = NaN; - }, - clean: function() { - return 2 - clean; // if intersections, rejoin first and last segments - } - }; -} - -function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) { - var cosPhi0, - cosPhi1, - sinLambda0Lambda1 = sin$1(lambda0 - lambda1); - return abs(sinLambda0Lambda1) > epsilon$4 - ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1) - - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0)) - / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) - : (phi0 + phi1) / 2; -} - -function clipAntimeridianInterpolate(from, to, direction, stream) { - var phi; - if (from == null) { - phi = direction * halfPi$3; - stream.point(-pi$4, phi); - stream.point(0, phi); - stream.point(pi$4, phi); - stream.point(pi$4, 0); - stream.point(pi$4, -phi); - stream.point(0, -phi); - stream.point(-pi$4, -phi); - stream.point(-pi$4, 0); - stream.point(-pi$4, phi); - } else if (abs(from[0] - to[0]) > epsilon$4) { - var lambda = from[0] < to[0] ? pi$4 : -pi$4; - phi = direction * lambda / 2; - stream.point(-lambda, phi); - stream.point(0, phi); - stream.point(lambda, phi); - } else { - stream.point(to[0], to[1]); - } -} - -var clipCircle = function(radius, delta) { - var cr = cos$1(radius), - smallRadius = cr > 0, - notHemisphere = abs(cr) > epsilon$4; // TODO optimise for this common case - - function interpolate(from, to, direction, stream) { - circleStream(stream, radius, delta, direction, from, to); - } - - function visible(lambda, phi) { - return cos$1(lambda) * cos$1(phi) > cr; - } - - // Takes a line and cuts into visible segments. Return values used for polygon - // clipping: 0 - there were intersections or the line was empty; 1 - no - // intersections 2 - there were intersections, and the first and last segments - // should be rejoined. - function clipLine(stream) { - var point0, // previous point - c0, // code for previous point - v0, // visibility of previous point - v00, // visibility of first point - clean; // no intersections - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(lambda, phi) { - var point1 = [lambda, phi], - point2, - v = visible(lambda, phi), - c = smallRadius - ? v ? 0 : code(lambda, phi) - : v ? code(lambda + (lambda < 0 ? pi$4 : -pi$4), phi) : 0; - if (!point0 && (v00 = v0 = v)) stream.lineStart(); - // Handle degeneracies. - // TODO ignore if not clipping polygons. - if (v !== v0) { - point2 = intersect(point0, point1); - if (pointEqual(point0, point2) || pointEqual(point1, point2)) { - point1[0] += epsilon$4; - point1[1] += epsilon$4; - v = visible(point1[0], point1[1]); - } - } - if (v !== v0) { - clean = 0; - if (v) { - // outside going in - stream.lineStart(); - point2 = intersect(point1, point0); - stream.point(point2[0], point2[1]); - } else { - // inside going out - point2 = intersect(point0, point1); - stream.point(point2[0], point2[1]); - stream.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - // If the codes for two points are different, or are both zero, - // and there this segment intersects with the small circle. - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - stream.lineStart(); - stream.point(t[0][0], t[0][1]); - stream.point(t[1][0], t[1][1]); - stream.lineEnd(); - } else { - stream.point(t[1][0], t[1][1]); - stream.lineEnd(); - stream.lineStart(); - stream.point(t[0][0], t[0][1]); - } - } - } - if (v && (!point0 || !pointEqual(point0, point1))) { - stream.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) stream.lineEnd(); - point0 = null; - }, - // Rejoin first and last segments if there were intersections and the first - // and last points were visible. - clean: function() { - return clean | ((v00 && v0) << 1); - } - }; - } - - // Intersects the great circle between a and b with the clip circle. - function intersect(a, b, two) { - var pa = cartesian(a), - pb = cartesian(b); - - // We have two planes, n1.p = d1 and n2.p = d2. - // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). - var n1 = [1, 0, 0], // normal - n2 = cartesianCross(pa, pb), - n2n2 = cartesianDot(n2, n2), - n1n2 = n2[0], // cartesianDot(n1, n2), - determinant = n2n2 - n1n2 * n1n2; - - // Two polar points. - if (!determinant) return !two && a; - - var c1 = cr * n2n2 / determinant, - c2 = -cr * n1n2 / determinant, - n1xn2 = cartesianCross(n1, n2), - A = cartesianScale(n1, c1), - B = cartesianScale(n2, c2); - cartesianAddInPlace(A, B); - - // Solve |p(t)|^2 = 1. - var u = n1xn2, - w = cartesianDot(A, u), - uu = cartesianDot(u, u), - t2 = w * w - uu * (cartesianDot(A, A) - 1); - - if (t2 < 0) return; - - var t = sqrt$1(t2), - q = cartesianScale(u, (-w - t) / uu); - cartesianAddInPlace(q, A); - q = spherical(q); - - if (!two) return q; - - // Two intersection points. - var lambda0 = a[0], - lambda1 = b[0], - phi0 = a[1], - phi1 = b[1], - z; - - if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z; - - var delta = lambda1 - lambda0, - polar = abs(delta - pi$4) < epsilon$4, - meridian = polar || delta < epsilon$4; - - if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; - - // Check that the first point is between a and b. - if (meridian - ? polar - ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$4 ? phi0 : phi1) - : phi0 <= q[1] && q[1] <= phi1 - : delta > pi$4 ^ (lambda0 <= q[0] && q[0] <= lambda1)) { - var q1 = cartesianScale(u, (-w + t) / uu); - cartesianAddInPlace(q1, A); - return [q, spherical(q1)]; - } - } - - // Generates a 4-bit vector representing the location of a point relative to - // the small circle's bounding box. - function code(lambda, phi) { - var r = smallRadius ? radius : pi$4 - radius, - code = 0; - if (lambda < -r) code |= 1; // left - else if (lambda > r) code |= 2; // right - if (phi < -r) code |= 4; // below - else if (phi > r) code |= 8; // above - return code; - } - - return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$4, radius - pi$4]); -}; - -var transform$1 = function(prototype) { - return { - stream: transform$2(prototype) - }; -}; - -function transform$2(prototype) { - function T() {} - var p = T.prototype = Object.create(Transform$1.prototype); - for (var k in prototype) p[k] = prototype[k]; - return function(stream) { - var t = new T; - t.stream = stream; - return t; - }; -} - -function Transform$1() {} - -Transform$1.prototype = { - point: function(x, y) { this.stream.point(x, y); }, - sphere: function() { this.stream.sphere(); }, - lineStart: function() { this.stream.lineStart(); }, - lineEnd: function() { this.stream.lineEnd(); }, - polygonStart: function() { this.stream.polygonStart(); }, - polygonEnd: function() { this.stream.polygonEnd(); } -}; - -function fit(project, extent, object) { - var w = extent[1][0] - extent[0][0], - h = extent[1][1] - extent[0][1], - clip = project.clipExtent && project.clipExtent(); - - project - .scale(150) - .translate([0, 0]); - - if (clip != null) project.clipExtent(null); - - geoStream(object, project.stream(boundsStream$1)); - - var b = boundsStream$1.result(), - k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])), - x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2, - y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2; - - if (clip != null) project.clipExtent(clip); - - return project - .scale(k * 150) - .translate([x, y]); -} - -function fitSize(project) { - return function(size, object) { - return fit(project, [[0, 0], size], object); - }; -} - -function fitExtent(project) { - return function(extent, object) { - return fit(project, extent, object); - }; -} - -var maxDepth = 16; -var cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance) - -var resample = function(project, delta2) { - return +delta2 ? resample$1(project, delta2) : resampleNone(project); -}; - -function resampleNone(project) { - return transform$2({ - point: function(x, y) { - x = project(x, y); - this.stream.point(x[0], x[1]); - } - }); -} - -function resample$1(project, delta2) { - - function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, - dy = y1 - y0, - d2 = dx * dx + dy * dy; - if (d2 > 4 * delta2 && depth--) { - var a = a0 + a1, - b = b0 + b1, - c = c0 + c1, - m = sqrt$1(a * a + b * b + c * c), - phi2 = asin$1(c /= m), - lambda2 = abs(abs(c) - 1) < epsilon$4 || abs(lambda0 - lambda1) < epsilon$4 ? (lambda0 + lambda1) / 2 : atan2(b, a), - p = project(lambda2, phi2), - x2 = p[0], - y2 = p[1], - dx2 = x2 - x0, - dy2 = y2 - y0, - dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > delta2 // perpendicular projected distance - || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end - || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream); - } - } - } - return function(stream) { - var lambda00, x00, y00, a00, b00, c00, // first point - lambda0, x0, y0, a0, b0, c0; // previous point - - var resampleStream = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; }, - polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; } - }; - - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - - function lineStart() { - x0 = NaN; - resampleStream.point = linePoint; - stream.lineStart(); - } - - function linePoint(lambda, phi) { - var c = cartesian([lambda, phi]), p = project(lambda, phi); - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - - function lineEnd() { - resampleStream.point = point; - stream.lineEnd(); - } - - function ringStart() { - lineStart(); - resampleStream.point = ringPoint; - resampleStream.lineEnd = ringEnd; - } - - function ringPoint(lambda, phi) { - linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resampleStream.point = linePoint; - } - - function ringEnd() { - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream); - resampleStream.lineEnd = lineEnd; - lineEnd(); - } - - return resampleStream; - }; -} - -var transformRadians = transform$2({ - point: function(x, y) { - this.stream.point(x * radians, y * radians); - } -}); - -function projection(project) { - return projectionMutator(function() { return project; })(); -} - -function projectionMutator(projectAt) { - var project, - k = 150, // scale - x = 480, y = 250, // translate - dx, dy, lambda = 0, phi = 0, // center - deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, projectRotate, // rotate - theta = null, preclip = clipAntimeridian, // clip angle - x0 = null, y0, x1, y1, postclip = identity$7, // clip extent - delta2 = 0.5, projectResample = resample(projectTransform, delta2), // precision - cache, - cacheStream; - - function projection(point) { - point = projectRotate(point[0] * radians, point[1] * radians); - return [point[0] * k + dx, dy - point[1] * k]; - } - - function invert(point) { - point = projectRotate.invert((point[0] - dx) / k, (dy - point[1]) / k); - return point && [point[0] * degrees$1, point[1] * degrees$1]; - } - - function projectTransform(x, y) { - return x = project(x, y), [x[0] * k + dx, dy - x[1] * k]; - } - - projection.stream = function(stream) { - return cache && cacheStream === stream ? cache : cache = transformRadians(preclip(rotate, projectResample(postclip(cacheStream = stream)))); - }; - - projection.clipAngle = function(_) { - return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians, 6 * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1; - }; - - projection.clipExtent = function(_) { - return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$7) : clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; - }; - - projection.scale = function(_) { - return arguments.length ? (k = +_, recenter()) : k; - }; - - projection.translate = function(_) { - return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y]; - }; - - projection.center = function(_) { - return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1]; - }; - - projection.rotate = function(_) { - return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1]; - }; - - projection.precision = function(_) { - return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2); - }; - - projection.fitExtent = fitExtent(projection); - - projection.fitSize = fitSize(projection); - - function recenter() { - projectRotate = compose(rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma), project); - var center = project(lambda, phi); - dx = x - center[0] * k; - dy = y + center[1] * k; - return reset(); - } - - function reset() { - cache = cacheStream = null; - return projection; - } - - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return recenter(); - }; -} - -function conicProjection(projectAt) { - var phi0 = 0, - phi1 = pi$4 / 3, - m = projectionMutator(projectAt), - p = m(phi0, phi1); - - p.parallels = function(_) { - return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1]; - }; - - return p; -} - -function cylindricalEqualAreaRaw(phi0) { - var cosPhi0 = cos$1(phi0); - - function forward(lambda, phi) { - return [lambda * cosPhi0, sin$1(phi) / cosPhi0]; - } - - forward.invert = function(x, y) { - return [x / cosPhi0, asin$1(y * cosPhi0)]; - }; - - return forward; -} - -function conicEqualAreaRaw(y0, y1) { - var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2; - - // Are the parallels symmetrical around the Equator? - if (abs(n) < epsilon$4) return cylindricalEqualAreaRaw(y0); - - var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt$1(c) / n; - - function project(x, y) { - var r = sqrt$1(c - 2 * n * sin$1(y)) / n; - return [r * sin$1(x *= n), r0 - r * cos$1(x)]; - } - - project.invert = function(x, y) { - var r0y = r0 - y; - return [atan2(x, abs(r0y)) / n * sign$1(r0y), asin$1((c - (x * x + r0y * r0y) * n * n) / (2 * n))]; - }; - - return project; -} - -var conicEqualArea = function() { - return conicProjection(conicEqualAreaRaw) - .scale(155.424) - .center([0, 33.6442]); -}; - -var albers = function() { - return conicEqualArea() - .parallels([29.5, 45.5]) - .scale(1070) - .translate([480, 250]) - .rotate([96, 0]) - .center([-0.6, 38.7]); -}; - -// The projections must have mutually exclusive clip regions on the sphere, -// as this will avoid emitting interleaving lines and polygons. -function multiplex(streams) { - var n = streams.length; - return { - point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); }, - sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); }, - lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); }, - lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); }, - polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); }, - polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); } - }; -} - -// A composite projection for the United States, configured by default for -// 960×500. The projection also works quite well at 960×600 if you change the -// scale to 1285 and adjust the translate accordingly. The set of standard -// parallels for each region comes from USGS, which is published here: -// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers -var albersUsa = function() { - var cache, - cacheStream, - lower48 = albers(), lower48Point, - alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338 - hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007 - point, pointStream = {point: function(x, y) { point = [x, y]; }}; - - function albersUsa(coordinates) { - var x = coordinates[0], y = coordinates[1]; - return point = null, - (lower48Point.point(x, y), point) - || (alaskaPoint.point(x, y), point) - || (hawaiiPoint.point(x, y), point); - } - - albersUsa.invert = function(coordinates) { - var k = lower48.scale(), - t = lower48.translate(), - x = (coordinates[0] - t[0]) / k, - y = (coordinates[1] - t[1]) / k; - return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska - : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii - : lower48).invert(coordinates); - }; - - albersUsa.stream = function(stream) { - return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]); - }; - - albersUsa.precision = function(_) { - if (!arguments.length) return lower48.precision(); - lower48.precision(_), alaska.precision(_), hawaii.precision(_); - return reset(); - }; - - albersUsa.scale = function(_) { - if (!arguments.length) return lower48.scale(); - lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_); - return albersUsa.translate(lower48.translate()); - }; - - albersUsa.translate = function(_) { - if (!arguments.length) return lower48.translate(); - var k = lower48.scale(), x = +_[0], y = +_[1]; - - lower48Point = lower48 - .translate(_) - .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]]) - .stream(pointStream); - - alaskaPoint = alaska - .translate([x - 0.307 * k, y + 0.201 * k]) - .clipExtent([[x - 0.425 * k + epsilon$4, y + 0.120 * k + epsilon$4], [x - 0.214 * k - epsilon$4, y + 0.234 * k - epsilon$4]]) - .stream(pointStream); - - hawaiiPoint = hawaii - .translate([x - 0.205 * k, y + 0.212 * k]) - .clipExtent([[x - 0.214 * k + epsilon$4, y + 0.166 * k + epsilon$4], [x - 0.115 * k - epsilon$4, y + 0.234 * k - epsilon$4]]) - .stream(pointStream); - - return reset(); - }; - - albersUsa.fitExtent = fitExtent(albersUsa); - - albersUsa.fitSize = fitSize(albersUsa); - - function reset() { - cache = cacheStream = null; - return albersUsa; - } - - return albersUsa.scale(1070); -}; - -function azimuthalRaw(scale) { - return function(x, y) { - var cx = cos$1(x), - cy = cos$1(y), - k = scale(cx * cy); - return [ - k * cy * sin$1(x), - k * sin$1(y) - ]; - } -} - -function azimuthalInvert(angle) { - return function(x, y) { - var z = sqrt$1(x * x + y * y), - c = angle(z), - sc = sin$1(c), - cc = cos$1(c); - return [ - atan2(x * sc, z * cc), - asin$1(z && y * sc / z) - ]; - } -} - -var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) { - return sqrt$1(2 / (1 + cxcy)); -}); - -azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) { - return 2 * asin$1(z / 2); -}); - -var azimuthalEqualArea = function() { - return projection(azimuthalEqualAreaRaw) - .scale(124.75) - .clipAngle(180 - 1e-3); -}; - -var azimuthalEquidistantRaw = azimuthalRaw(function(c) { - return (c = acos(c)) && c / sin$1(c); -}); - -azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) { - return z; -}); - -var azimuthalEquidistant = function() { - return projection(azimuthalEquidistantRaw) - .scale(79.4188) - .clipAngle(180 - 1e-3); -}; - -function mercatorRaw(lambda, phi) { - return [lambda, log$1(tan((halfPi$3 + phi) / 2))]; -} - -mercatorRaw.invert = function(x, y) { - return [x, 2 * atan(exp(y)) - halfPi$3]; -}; - -var mercator = function() { - return mercatorProjection(mercatorRaw) - .scale(961 / tau$4); -}; - -function mercatorProjection(project) { - var m = projection(project), - scale = m.scale, - translate = m.translate, - clipExtent = m.clipExtent, - clipAuto; - - m.scale = function(_) { - return arguments.length ? (scale(_), clipAuto && m.clipExtent(null), m) : scale(); - }; - - m.translate = function(_) { - return arguments.length ? (translate(_), clipAuto && m.clipExtent(null), m) : translate(); - }; - - m.clipExtent = function(_) { - if (!arguments.length) return clipAuto ? null : clipExtent(); - if (clipAuto = _ == null) { - var k = pi$4 * scale(), - t = translate(); - _ = [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]; - } - clipExtent(_); - return m; - }; - - return m.clipExtent(null); -} - -function tany(y) { - return tan((halfPi$3 + y) / 2); -} - -function conicConformalRaw(y0, y1) { - var cy0 = cos$1(y0), - n = y0 === y1 ? sin$1(y0) : log$1(cy0 / cos$1(y1)) / log$1(tany(y1) / tany(y0)), - f = cy0 * pow$1(tany(y0), n) / n; - - if (!n) return mercatorRaw; - - function project(x, y) { - if (f > 0) { if (y < -halfPi$3 + epsilon$4) y = -halfPi$3 + epsilon$4; } - else { if (y > halfPi$3 - epsilon$4) y = halfPi$3 - epsilon$4; } - var r = f / pow$1(tany(y), n); - return [r * sin$1(n * x), f - r * cos$1(n * x)]; - } - - project.invert = function(x, y) { - var fy = f - y, r = sign$1(n) * sqrt$1(x * x + fy * fy); - return [atan2(x, abs(fy)) / n * sign$1(fy), 2 * atan(pow$1(f / r, 1 / n)) - halfPi$3]; - }; - - return project; -} - -var conicConformal = function() { - return conicProjection(conicConformalRaw) - .scale(109.5) - .parallels([30, 30]); -}; - -function equirectangularRaw(lambda, phi) { - return [lambda, phi]; -} - -equirectangularRaw.invert = equirectangularRaw; - -var equirectangular = function() { - return projection(equirectangularRaw) - .scale(152.63); -}; - -function conicEquidistantRaw(y0, y1) { - var cy0 = cos$1(y0), - n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0), - g = cy0 / n + y0; - - if (abs(n) < epsilon$4) return equirectangularRaw; - - function project(x, y) { - var gy = g - y, nx = n * x; - return [gy * sin$1(nx), g - gy * cos$1(nx)]; - } - - project.invert = function(x, y) { - var gy = g - y; - return [atan2(x, abs(gy)) / n * sign$1(gy), g - sign$1(n) * sqrt$1(x * x + gy * gy)]; - }; - - return project; -} - -var conicEquidistant = function() { - return conicProjection(conicEquidistantRaw) - .scale(131.154) - .center([0, 13.9389]); -}; - -function gnomonicRaw(x, y) { - var cy = cos$1(y), k = cos$1(x) * cy; - return [cy * sin$1(x) / k, sin$1(y) / k]; -} - -gnomonicRaw.invert = azimuthalInvert(atan); - -var gnomonic = function() { - return projection(gnomonicRaw) - .scale(144.049) - .clipAngle(60); -}; - -function orthographicRaw(x, y) { - return [cos$1(y) * sin$1(x), sin$1(y)]; -} - -orthographicRaw.invert = azimuthalInvert(asin$1); - -var orthographic = function() { - return projection(orthographicRaw) - .scale(249.5) - .clipAngle(90 + epsilon$4); -}; - -function stereographicRaw(x, y) { - var cy = cos$1(y), k = 1 + cos$1(x) * cy; - return [cy * sin$1(x) / k, sin$1(y) / k]; -} - -stereographicRaw.invert = azimuthalInvert(function(z) { - return 2 * atan(z); -}); - -var stereographic = function() { - return projection(stereographicRaw) - .scale(250) - .clipAngle(142); -}; - -function transverseMercatorRaw(lambda, phi) { - return [log$1(tan((halfPi$3 + phi) / 2)), -lambda]; -} - -transverseMercatorRaw.invert = function(x, y) { - return [-y, 2 * atan(exp(x)) - halfPi$3]; -}; - -var transverseMercator = function() { - var m = mercatorProjection(transverseMercatorRaw), - center = m.center, - rotate = m.rotate; - - m.center = function(_) { - return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]); - }; - - m.rotate = function(_) { - return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]); - }; - - return rotate([0, 0, 90]) - .scale(159.155); -}; - -exports.version = version; -exports.bisect = bisectRight; -exports.bisectRight = bisectRight; -exports.bisectLeft = bisectLeft; -exports.ascending = ascending; -exports.bisector = bisector; -exports.descending = descending; -exports.deviation = deviation; -exports.extent = extent; -exports.histogram = histogram; -exports.thresholdFreedmanDiaconis = freedmanDiaconis; -exports.thresholdScott = scott; -exports.thresholdSturges = sturges; -exports.max = max; -exports.mean = mean; -exports.median = median; -exports.merge = merge; -exports.min = min; -exports.pairs = pairs; -exports.permute = permute; -exports.quantile = threshold; -exports.range = range; -exports.scan = scan; -exports.shuffle = shuffle; -exports.sum = sum; -exports.ticks = ticks; -exports.tickStep = tickStep; -exports.transpose = transpose; -exports.variance = variance; -exports.zip = zip; -exports.entries = entries; -exports.keys = keys; -exports.values = values; -exports.map = map$1; -exports.set = set; -exports.nest = nest; -exports.randomUniform = uniform; -exports.randomNormal = normal; -exports.randomLogNormal = logNormal; -exports.randomBates = bates; -exports.randomIrwinHall = irwinHall; -exports.randomExponential = exponential; -exports.easeLinear = linear; -exports.easeQuad = quadInOut; -exports.easeQuadIn = quadIn; -exports.easeQuadOut = quadOut; -exports.easeQuadInOut = quadInOut; -exports.easeCubic = cubicInOut; -exports.easeCubicIn = cubicIn; -exports.easeCubicOut = cubicOut; -exports.easeCubicInOut = cubicInOut; -exports.easePoly = polyInOut; -exports.easePolyIn = polyIn; -exports.easePolyOut = polyOut; -exports.easePolyInOut = polyInOut; -exports.easeSin = sinInOut; -exports.easeSinIn = sinIn; -exports.easeSinOut = sinOut; -exports.easeSinInOut = sinInOut; -exports.easeExp = expInOut; -exports.easeExpIn = expIn; -exports.easeExpOut = expOut; -exports.easeExpInOut = expInOut; -exports.easeCircle = circleInOut; -exports.easeCircleIn = circleIn; -exports.easeCircleOut = circleOut; -exports.easeCircleInOut = circleInOut; -exports.easeBounce = bounceOut; -exports.easeBounceIn = bounceIn; -exports.easeBounceOut = bounceOut; -exports.easeBounceInOut = bounceInOut; -exports.easeBack = backInOut; -exports.easeBackIn = backIn; -exports.easeBackOut = backOut; -exports.easeBackInOut = backInOut; -exports.easeElastic = elasticOut; -exports.easeElasticIn = elasticIn; -exports.easeElasticOut = elasticOut; -exports.easeElasticInOut = elasticInOut; -exports.polygonArea = area; -exports.polygonCentroid = centroid; -exports.polygonHull = hull; -exports.polygonContains = contains; -exports.polygonLength = length$1; -exports.path = path; -exports.quadtree = quadtree; -exports.queue = queue; -exports.arc = arc; -exports.area = area$1; -exports.line = line; -exports.pie = pie; -exports.radialArea = radialArea; -exports.radialLine = radialLine$1; -exports.symbol = symbol; -exports.symbols = symbols; -exports.symbolCircle = circle; -exports.symbolCross = cross$1; -exports.symbolDiamond = diamond; -exports.symbolSquare = square; -exports.symbolStar = star; -exports.symbolTriangle = triangle; -exports.symbolWye = wye; -exports.curveBasisClosed = basisClosed; -exports.curveBasisOpen = basisOpen; -exports.curveBasis = basis; -exports.curveBundle = bundle; -exports.curveCardinalClosed = cardinalClosed; -exports.curveCardinalOpen = cardinalOpen; -exports.curveCardinal = cardinal; -exports.curveCatmullRomClosed = catmullRomClosed; -exports.curveCatmullRomOpen = catmullRomOpen; -exports.curveCatmullRom = catmullRom; -exports.curveLinearClosed = linearClosed; -exports.curveLinear = curveLinear; -exports.curveMonotoneX = monotoneX; -exports.curveMonotoneY = monotoneY; -exports.curveNatural = natural; -exports.curveStep = step; -exports.curveStepAfter = stepAfter; -exports.curveStepBefore = stepBefore; -exports.stack = stack; -exports.stackOffsetExpand = expand; -exports.stackOffsetNone = none; -exports.stackOffsetSilhouette = silhouette; -exports.stackOffsetWiggle = wiggle; -exports.stackOrderAscending = ascending$1; -exports.stackOrderDescending = descending$2; -exports.stackOrderInsideOut = insideOut; -exports.stackOrderNone = none$1; -exports.stackOrderReverse = reverse; -exports.color = color; -exports.rgb = rgb; -exports.hsl = hsl; -exports.lab = lab; -exports.hcl = hcl; -exports.cubehelix = cubehelix; -exports.interpolate = interpolate; -exports.interpolateArray = array$1; -exports.interpolateDate = date; -exports.interpolateNumber = interpolateNumber; -exports.interpolateObject = object; -exports.interpolateRound = interpolateRound; -exports.interpolateString = interpolateString; -exports.interpolateTransformCss = interpolateTransformCss; -exports.interpolateTransformSvg = interpolateTransformSvg; -exports.interpolateZoom = interpolateZoom; -exports.interpolateRgb = interpolateRgb; -exports.interpolateRgbBasis = rgbBasis; -exports.interpolateRgbBasisClosed = rgbBasisClosed; -exports.interpolateHsl = hsl$2; -exports.interpolateHslLong = hslLong; -exports.interpolateLab = lab$1; -exports.interpolateHcl = hcl$2; -exports.interpolateHclLong = hclLong; -exports.interpolateCubehelix = cubehelix$2; -exports.interpolateCubehelixLong = cubehelixLong; -exports.interpolateBasis = basis$2; -exports.interpolateBasisClosed = basisClosed$1; -exports.quantize = quantize; -exports.dispatch = dispatch; -exports.dsvFormat = dsv; -exports.csvParse = csvParse; -exports.csvParseRows = csvParseRows; -exports.csvFormat = csvFormat; -exports.csvFormatRows = csvFormatRows; -exports.tsvParse = tsvParse; -exports.tsvParseRows = tsvParseRows; -exports.tsvFormat = tsvFormat; -exports.tsvFormatRows = tsvFormatRows; -exports.request = request; -exports.html = html; -exports.json = json; -exports.text = text; -exports.xml = xml; -exports.csv = csv$1; -exports.tsv = tsv$1; -exports.now = now; -exports.timer = timer; -exports.timerFlush = timerFlush; -exports.timeout = timeout$1; -exports.interval = interval$1; -exports.timeInterval = newInterval; -exports.timeMillisecond = millisecond; -exports.timeMilliseconds = milliseconds; -exports.timeSecond = second; -exports.timeSeconds = seconds; -exports.timeMinute = minute; -exports.timeMinutes = minutes; -exports.timeHour = hour; -exports.timeHours = hours; -exports.timeDay = day; -exports.timeDays = days; -exports.timeWeek = sunday; -exports.timeWeeks = sundays; -exports.timeSunday = sunday; -exports.timeSundays = sundays; -exports.timeMonday = monday; -exports.timeMondays = mondays; -exports.timeTuesday = tuesday; -exports.timeTuesdays = tuesdays; -exports.timeWednesday = wednesday; -exports.timeWednesdays = wednesdays; -exports.timeThursday = thursday; -exports.timeThursdays = thursdays; -exports.timeFriday = friday; -exports.timeFridays = fridays; -exports.timeSaturday = saturday; -exports.timeSaturdays = saturdays; -exports.timeMonth = month; -exports.timeMonths = months; -exports.timeYear = year; -exports.timeYears = years; -exports.utcMillisecond = millisecond; -exports.utcMilliseconds = milliseconds; -exports.utcSecond = second; -exports.utcSeconds = seconds; -exports.utcMinute = utcMinute; -exports.utcMinutes = utcMinutes; -exports.utcHour = utcHour; -exports.utcHours = utcHours; -exports.utcDay = utcDay; -exports.utcDays = utcDays; -exports.utcWeek = utcSunday; -exports.utcWeeks = utcSundays; -exports.utcSunday = utcSunday; -exports.utcSundays = utcSundays; -exports.utcMonday = utcMonday; -exports.utcMondays = utcMondays; -exports.utcTuesday = utcTuesday; -exports.utcTuesdays = utcTuesdays; -exports.utcWednesday = utcWednesday; -exports.utcWednesdays = utcWednesdays; -exports.utcThursday = utcThursday; -exports.utcThursdays = utcThursdays; -exports.utcFriday = utcFriday; -exports.utcFridays = utcFridays; -exports.utcSaturday = utcSaturday; -exports.utcSaturdays = utcSaturdays; -exports.utcMonth = utcMonth; -exports.utcMonths = utcMonths; -exports.utcYear = utcYear; -exports.utcYears = utcYears; -exports.formatLocale = formatLocale; -exports.formatDefaultLocale = defaultLocale; -exports.formatSpecifier = formatSpecifier; -exports.precisionFixed = precisionFixed; -exports.precisionPrefix = precisionPrefix; -exports.precisionRound = precisionRound; -exports.isoFormat = formatIso; -exports.isoParse = parseIso; -exports.timeFormatLocale = formatLocale$1; -exports.timeFormatDefaultLocale = defaultLocale$1; -exports.scaleBand = band; -exports.scalePoint = point$4; -exports.scaleIdentity = identity$4; -exports.scaleLinear = linear$2; -exports.scaleLog = log; -exports.scaleOrdinal = ordinal; -exports.scaleImplicit = implicit; -exports.scalePow = pow; -exports.scaleSqrt = sqrt; -exports.scaleQuantile = quantile$$1; -exports.scaleQuantize = quantize$1; -exports.scaleThreshold = threshold$1; -exports.scaleTime = time; -exports.scaleUtc = utcTime; -exports.schemeCategory10 = category10; -exports.schemeCategory20b = category20b; -exports.schemeCategory20c = category20c; -exports.schemeCategory20 = category20; -exports.scaleSequential = sequential; -exports.interpolateCubehelixDefault = cubehelix$3; -exports.interpolateRainbow = rainbow$1; -exports.interpolateWarm = warm; -exports.interpolateCool = cool; -exports.interpolateViridis = viridis; -exports.interpolateMagma = magma; -exports.interpolateInferno = inferno; -exports.interpolatePlasma = plasma; -exports.creator = creator; -exports.customEvent = customEvent; -exports.local = local; -exports.matcher = matcher$1; -exports.mouse = mouse; -exports.namespace = namespace; -exports.namespaces = namespaces; -exports.select = select; -exports.selectAll = selectAll; -exports.selection = selection; -exports.selector = selector; -exports.selectorAll = selectorAll; -exports.touch = touch; -exports.touches = touches; -exports.window = window; -exports.active = active; -exports.interrupt = interrupt; -exports.transition = transition; -exports.axisTop = axisTop; -exports.axisRight = axisRight; -exports.axisBottom = axisBottom; -exports.axisLeft = axisLeft; -exports.cluster = cluster; -exports.hierarchy = hierarchy; -exports.pack = index; -exports.packSiblings = siblings; -exports.packEnclose = enclose; -exports.partition = partition; -exports.stratify = stratify; -exports.tree = tree; -exports.treemap = index$1; -exports.treemapBinary = binary; -exports.treemapDice = treemapDice; -exports.treemapSlice = treemapSlice; -exports.treemapSliceDice = sliceDice; -exports.treemapSquarify = squarify; -exports.treemapResquarify = resquarify; -exports.forceCenter = center$1; -exports.forceCollide = collide; -exports.forceLink = link; -exports.forceManyBody = manyBody; -exports.forceSimulation = simulation; -exports.forceX = x$3; -exports.forceY = y$3; -exports.drag = drag; -exports.dragDisable = dragDisable; -exports.dragEnable = yesdrag; -exports.voronoi = voronoi; -exports.zoom = zoom; -exports.zoomIdentity = identity$6; -exports.zoomTransform = transform; -exports.brush = brush; -exports.brushX = brushX; -exports.brushY = brushY; -exports.brushSelection = brushSelection; -exports.chord = chord; -exports.ribbon = ribbon; -exports.geoAlbers = albers; -exports.geoAlbersUsa = albersUsa; -exports.geoArea = area$2; -exports.geoAzimuthalEqualArea = azimuthalEqualArea; -exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw; -exports.geoAzimuthalEquidistant = azimuthalEquidistant; -exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw; -exports.geoBounds = bounds; -exports.geoCentroid = centroid$1; -exports.geoCircle = circle$1; -exports.geoClipExtent = extent$1; -exports.geoConicConformal = conicConformal; -exports.geoConicConformalRaw = conicConformalRaw; -exports.geoConicEqualArea = conicEqualArea; -exports.geoConicEqualAreaRaw = conicEqualAreaRaw; -exports.geoConicEquidistant = conicEquidistant; -exports.geoConicEquidistantRaw = conicEquidistantRaw; -exports.geoDistance = distance; -exports.geoEquirectangular = equirectangular; -exports.geoEquirectangularRaw = equirectangularRaw; -exports.geoGnomonic = gnomonic; -exports.geoGnomonicRaw = gnomonicRaw; -exports.geoGraticule = graticule; -exports.geoInterpolate = interpolate$2; -exports.geoLength = length$2; -exports.geoMercator = mercator; -exports.geoMercatorRaw = mercatorRaw; -exports.geoOrthographic = orthographic; -exports.geoOrthographicRaw = orthographicRaw; -exports.geoPath = index$3; -exports.geoProjection = projection; -exports.geoProjectionMutator = projectionMutator; -exports.geoRotation = rotation; -exports.geoStereographic = stereographic; -exports.geoStereographicRaw = stereographicRaw; -exports.geoStream = geoStream; -exports.geoTransform = transform$1; -exports.geoTransverseMercator = transverseMercator; -exports.geoTransverseMercatorRaw = transverseMercatorRaw; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); diff --git a/v3/js/libs/eventemitter.min.js b/v3/js/libs/eventemitter.min.js deleted file mode 100644 index 3f6ae86..0000000 --- a/v3/js/libs/eventemitter.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * EventEmitter v4.0.3 - git.io/ee - * Oliver Caldwell - * MIT license - */(function(e){"use strict";function t(){}function i(e,t){if(r)return t.indexOf(e);var n=t.length;while(n--)if(t[n]===e)return n;return-1}var n=t.prototype,r=Array.prototype.indexOf?!0:!1;n.getListeners=function(e){var t=this._events||(this._events={});return t[e]||(t[e]=[])},n.addListener=function(e,t){var n=this.getListeners(e);return i(t,n)===-1&&n.push(t),this},n.on=n.addListener,n.removeListener=function(e,t){var n=this.getListeners(e),r=i(t,n);return r!==-1&&(n.splice(r,1),n.length===0&&(this._events[e]=null)),this},n.off=n.removeListener,n.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},n.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},n.manipulateListeners=function(e,t,n){var r,i,s=e?this.removeListener:this.addListener,o=e?this.removeListeners:this.addListeners;if(typeof t=="object")for(r in t)t.hasOwnProperty(r)&&(i=t[r])&&(typeof i=="function"?s.call(this,r,i):o.call(this,r,i));else{r=n.length;while(r--)s.call(this,t,n[r])}return this},n.removeEvent=function(e){return e?this._events[e]=null:this._events=null,this},n.emitEvent=function(e,t){var n=this.getListeners(e),r=n.length,i;while(r--)i=t?n[r].apply(null,t):n[r](),i===!0&&this.removeListener(e,n[r]);return this},n.trigger=n.emitEvent,typeof define=="function"&&define.amd?define(function(){return t}):e.EventEmitter=t})(this); diff --git a/v3/js/libs/float16.js b/v3/js/libs/float16.js deleted file mode 100644 index fcd03f5..0000000 --- a/v3/js/libs/float16.js +++ /dev/null @@ -1,1251 +0,0 @@ -/*! @petamoriken/float16 v3.6.5 | MIT License - https://github.com/petamoriken/float16 */ - -const float16 = (function (exports) { - 'use strict'; - - const THIS_IS_NOT_AN_OBJECT = "This is not an object"; - const THIS_IS_NOT_A_FLOAT16ARRAY_OBJECT = "This is not a Float16Array object"; - const THIS_CONSTRUCTOR_IS_NOT_A_SUBCLASS_OF_FLOAT16ARRAY = - "This constructor is not a subclass of Float16Array"; - const THE_CONSTRUCTOR_PROPERTY_VALUE_IS_NOT_AN_OBJECT = - "The constructor property value is not an object"; - const SPECIES_CONSTRUCTOR_DIDNT_RETURN_TYPEDARRAY_OBJECT = - "Species constructor didn't return TypedArray object"; - const DERIVED_CONSTRUCTOR_CREATED_TYPEDARRAY_OBJECT_WHICH_WAS_TOO_SMALL_LENGTH = - "Derived constructor created TypedArray object which was too small length"; - const ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER = - "Attempting to access detached ArrayBuffer"; - const CANNOT_CONVERT_UNDEFINED_OR_NULL_TO_OBJECT = - "Cannot convert undefined or null to object"; - const CANNOT_CONVERT_A_BIGINT_VALUE_TO_A_NUMBER = - "Cannot convert a BigInt value to a number"; - const CANNOT_MIX_BIGINT_AND_OTHER_TYPES = - "Cannot mix BigInt and other types, use explicit conversions"; - const ITERATOR_PROPERTY_IS_NOT_CALLABLE = "@@iterator property is not callable"; - const REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE = - "Reduce of empty array with no initial value"; - const OFFSET_IS_OUT_OF_BOUNDS = "Offset is out of bounds"; - - function uncurryThis(target) { - return (thisArg, ...args) => { - return ReflectApply(target, thisArg, args); - }; - } - function uncurryThisGetter(target, key) { - return uncurryThis( - ReflectGetOwnPropertyDescriptor( - target, - key - ).get - ); - } - const { - apply: ReflectApply, - construct: ReflectConstruct, - defineProperty: ReflectDefineProperty, - get: ReflectGet, - getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor, - getPrototypeOf: ReflectGetPrototypeOf, - has: ReflectHas, - ownKeys: ReflectOwnKeys, - set: ReflectSet, - setPrototypeOf: ReflectSetPrototypeOf, - } = Reflect; - const NativeProxy = Proxy; - const NativeNumber = Number; - const { - isFinite: NumberIsFinite, - isNaN: NumberIsNaN, - } = NativeNumber; - const { - iterator: SymbolIterator, - species: SymbolSpecies, - toStringTag: SymbolToStringTag, - for: SymbolFor, - } = Symbol; - const NativeObject = Object; - const { - create: ObjectCreate, - defineProperty: ObjectDefineProperty, - freeze: ObjectFreeze, - is: ObjectIs, - } = NativeObject; - const ObjectPrototype = NativeObject.prototype; - const ObjectPrototype__lookupGetter__ = (ObjectPrototype).__lookupGetter__ - ? uncurryThis( (ObjectPrototype).__lookupGetter__) - : (object, key) => { - if (object == null) { - throw NativeTypeError( - CANNOT_CONVERT_UNDEFINED_OR_NULL_TO_OBJECT - ); - } - let target = NativeObject(object); - do { - const descriptor = ReflectGetOwnPropertyDescriptor(target, key); - if (descriptor !== undefined) { - if (ObjectHasOwn(descriptor, "get")) { - return descriptor.get; - } - return; - } - } while ((target = ReflectGetPrototypeOf(target)) !== null); - }; - const ObjectHasOwn = (NativeObject).hasOwn || - uncurryThis(ObjectPrototype.hasOwnProperty); - const NativeArray = Array; - const ArrayIsArray = NativeArray.isArray; - const ArrayPrototype = NativeArray.prototype; - const ArrayPrototypeJoin = uncurryThis(ArrayPrototype.join); - const ArrayPrototypePush = uncurryThis(ArrayPrototype.push); - const ArrayPrototypeToLocaleString = uncurryThis( - ArrayPrototype.toLocaleString - ); - const NativeArrayPrototypeSymbolIterator = ArrayPrototype[SymbolIterator]; - const ArrayPrototypeSymbolIterator = uncurryThis(NativeArrayPrototypeSymbolIterator); - const MathTrunc = Math.trunc; - const NativeArrayBuffer = ArrayBuffer; - const ArrayBufferIsView = NativeArrayBuffer.isView; - const ArrayBufferPrototype = NativeArrayBuffer.prototype; - const ArrayBufferPrototypeSlice = uncurryThis(ArrayBufferPrototype.slice); - const ArrayBufferPrototypeGetByteLength = uncurryThisGetter(ArrayBufferPrototype, "byteLength"); - const NativeSharedArrayBuffer = typeof SharedArrayBuffer !== "undefined" ? SharedArrayBuffer : null; - const SharedArrayBufferPrototypeGetByteLength = NativeSharedArrayBuffer - && uncurryThisGetter(NativeSharedArrayBuffer.prototype, "byteLength"); - const TypedArray = ReflectGetPrototypeOf(Uint8Array); - const TypedArrayFrom = TypedArray.from; - const TypedArrayPrototype = TypedArray.prototype; - const NativeTypedArrayPrototypeSymbolIterator = TypedArrayPrototype[SymbolIterator]; - const TypedArrayPrototypeKeys = uncurryThis(TypedArrayPrototype.keys); - const TypedArrayPrototypeValues = uncurryThis( - TypedArrayPrototype.values - ); - const TypedArrayPrototypeEntries = uncurryThis( - TypedArrayPrototype.entries - ); - const TypedArrayPrototypeSet = uncurryThis(TypedArrayPrototype.set); - const TypedArrayPrototypeReverse = uncurryThis( - TypedArrayPrototype.reverse - ); - const TypedArrayPrototypeFill = uncurryThis(TypedArrayPrototype.fill); - const TypedArrayPrototypeCopyWithin = uncurryThis( - TypedArrayPrototype.copyWithin - ); - const TypedArrayPrototypeSort = uncurryThis(TypedArrayPrototype.sort); - const TypedArrayPrototypeSlice = uncurryThis(TypedArrayPrototype.slice); - const TypedArrayPrototypeSubarray = uncurryThis( - TypedArrayPrototype.subarray - ); - const TypedArrayPrototypeGetBuffer = uncurryThisGetter( - TypedArrayPrototype, - "buffer" - ); - const TypedArrayPrototypeGetByteOffset = uncurryThisGetter( - TypedArrayPrototype, - "byteOffset" - ); - const TypedArrayPrototypeGetLength = uncurryThisGetter( - TypedArrayPrototype, - "length" - ); - const TypedArrayPrototypeGetSymbolToStringTag = uncurryThisGetter( - TypedArrayPrototype, - SymbolToStringTag - ); - const NativeUint16Array = Uint16Array; - const Uint16ArrayFrom = (...args) => { - return ReflectApply(TypedArrayFrom, NativeUint16Array, args); - }; - const NativeUint32Array = Uint32Array; - const NativeFloat32Array = Float32Array; - const ArrayIteratorPrototype = ReflectGetPrototypeOf([][SymbolIterator]()); - const ArrayIteratorPrototypeNext = uncurryThis(ArrayIteratorPrototype.next); - const GeneratorPrototypeNext = uncurryThis((function* () {})().next); - const IteratorPrototype = ReflectGetPrototypeOf(ArrayIteratorPrototype); - const DataViewPrototype = DataView.prototype; - const DataViewPrototypeGetUint16 = uncurryThis( - DataViewPrototype.getUint16 - ); - const DataViewPrototypeSetUint16 = uncurryThis( - DataViewPrototype.setUint16 - ); - const NativeTypeError = TypeError; - const NativeRangeError = RangeError; - const NativeWeakSet = WeakSet; - const WeakSetPrototype = NativeWeakSet.prototype; - const WeakSetPrototypeAdd = uncurryThis(WeakSetPrototype.add); - const WeakSetPrototypeHas = uncurryThis(WeakSetPrototype.has); - const NativeWeakMap = WeakMap; - const WeakMapPrototype = NativeWeakMap.prototype; - const WeakMapPrototypeGet = uncurryThis(WeakMapPrototype.get); - const WeakMapPrototypeHas = uncurryThis(WeakMapPrototype.has); - const WeakMapPrototypeSet = uncurryThis(WeakMapPrototype.set); - - const arrayIterators = new NativeWeakMap(); - const SafeIteratorPrototype = ObjectCreate(null, { - next: { - value: function next() { - const arrayIterator = WeakMapPrototypeGet(arrayIterators, this); - return ArrayIteratorPrototypeNext(arrayIterator); - }, - }, - [SymbolIterator]: { - value: function values() { - return this; - }, - }, - }); - function safeIfNeeded(array) { - if (array[SymbolIterator] === NativeArrayPrototypeSymbolIterator) { - return array; - } - const safe = ObjectCreate(SafeIteratorPrototype); - WeakMapPrototypeSet(arrayIterators, safe, ArrayPrototypeSymbolIterator(array)); - return safe; - } - const generators = new NativeWeakMap(); - const DummyArrayIteratorPrototype = ObjectCreate(IteratorPrototype, { - next: { - value: function next() { - const generator = WeakMapPrototypeGet(generators, this); - return GeneratorPrototypeNext(generator); - }, - writable: true, - configurable: true, - }, - }); - for (const key of ReflectOwnKeys(ArrayIteratorPrototype)) { - if (key === "next") { - continue; - } - ObjectDefineProperty(DummyArrayIteratorPrototype, key, ReflectGetOwnPropertyDescriptor(ArrayIteratorPrototype, key)); - } - function wrap(generator) { - const dummy = ObjectCreate(DummyArrayIteratorPrototype); - WeakMapPrototypeSet(generators, dummy, generator); - return dummy; - } - - function isObject(value) { - return (value !== null && typeof value === "object") || - typeof value === "function"; - } - function isObjectLike(value) { - return value !== null && typeof value === "object"; - } - function isNativeTypedArray(value) { - return TypedArrayPrototypeGetSymbolToStringTag(value) !== undefined; - } - function isNativeBigIntTypedArray(value) { - const typedArrayName = TypedArrayPrototypeGetSymbolToStringTag(value); - return typedArrayName === "BigInt64Array" || - typedArrayName === "BigUint64Array"; - } - function isArrayBuffer(value) { - try { - ArrayBufferPrototypeGetByteLength( (value)); - return true; - } catch (e) { - return false; - } - } - function isSharedArrayBuffer(value) { - if (NativeSharedArrayBuffer === null) { - return false; - } - try { - SharedArrayBufferPrototypeGetByteLength( (value)); - return true; - } catch (e) { - return false; - } - } - function isOrdinaryArray(value) { - if (!ArrayIsArray(value)) { - return false; - } - if (value[SymbolIterator] === NativeArrayPrototypeSymbolIterator) { - return true; - } - const iterator = value[SymbolIterator](); - return iterator[SymbolToStringTag] === "Array Iterator"; - } - function isOrdinaryNativeTypedArray(value) { - if (!isNativeTypedArray(value)) { - return false; - } - if (value[SymbolIterator] === NativeTypedArrayPrototypeSymbolIterator) { - return true; - } - const iterator = value[SymbolIterator](); - return iterator[SymbolToStringTag] === "Array Iterator"; - } - function isCanonicalIntegerIndexString(value) { - if (typeof value !== "string") { - return false; - } - const number = NativeNumber(value); - if (value !== number + "") { - return false; - } - if (!NumberIsFinite(number)) { - return false; - } - return number === MathTrunc(number); - } - - const brand = SymbolFor("__Float16Array__"); - function hasFloat16ArrayBrand(target) { - if (!isObjectLike(target)) { - return false; - } - const prototype = ReflectGetPrototypeOf(target); - if (!isObjectLike(prototype)) { - return false; - } - const constructor = prototype.constructor; - if (constructor === undefined) { - return false; - } - if (!isObject(constructor)) { - throw NativeTypeError(THE_CONSTRUCTOR_PROPERTY_VALUE_IS_NOT_AN_OBJECT); - } - return ReflectHas(constructor, brand); - } - - const buffer = new NativeArrayBuffer(4); - const floatView = new NativeFloat32Array(buffer); - const uint32View = new NativeUint32Array(buffer); - const baseTable = new NativeUint32Array(512); - const shiftTable = new NativeUint32Array(512); - for (let i = 0; i < 256; ++i) { - const e = i - 127; - if (e < -27) { - baseTable[i] = 0x0000; - baseTable[i | 0x100] = 0x8000; - shiftTable[i] = 24; - shiftTable[i | 0x100] = 24; - } else if (e < -14) { - baseTable[i] = 0x0400 >> (-e - 14); - baseTable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000; - shiftTable[i] = -e - 1; - shiftTable[i | 0x100] = -e - 1; - } else if (e <= 15) { - baseTable[i] = (e + 15) << 10; - baseTable[i | 0x100] = ((e + 15) << 10) | 0x8000; - shiftTable[i] = 13; - shiftTable[i | 0x100] = 13; - } else if (e < 128) { - baseTable[i] = 0x7c00; - baseTable[i | 0x100] = 0xfc00; - shiftTable[i] = 24; - shiftTable[i | 0x100] = 24; - } else { - baseTable[i] = 0x7c00; - baseTable[i | 0x100] = 0xfc00; - shiftTable[i] = 13; - shiftTable[i | 0x100] = 13; - } - } - function roundToFloat16Bits(num) { - floatView[0] = (num); - const f = uint32View[0]; - const e = (f >> 23) & 0x1ff; - return baseTable[e] + ((f & 0x007fffff) >> shiftTable[e]); - } - const mantissaTable = new NativeUint32Array(2048); - const exponentTable = new NativeUint32Array(64); - const offsetTable = new NativeUint32Array(64); - for (let i = 1; i < 1024; ++i) { - let m = i << 13; - let e = 0; - while((m & 0x00800000) === 0) { - m <<= 1; - e -= 0x00800000; - } - m &= ~0x00800000; - e += 0x38800000; - mantissaTable[i] = m | e; - } - for (let i = 1024; i < 2048; ++i) { - mantissaTable[i] = 0x38000000 + ((i - 1024) << 13); - } - for (let i = 1; i < 31; ++i) { - exponentTable[i] = i << 23; - } - exponentTable[31] = 0x47800000; - exponentTable[32] = 0x80000000; - for (let i = 33; i < 63; ++i) { - exponentTable[i] = 0x80000000 + ((i - 32) << 23); - } - exponentTable[63] = 0xc7800000; - for (let i = 1; i < 64; ++i) { - if (i !== 32) { - offsetTable[i] = 1024; - } - } - function convertToNumber(float16bits) { - const m = float16bits >> 10; - uint32View[0] = mantissaTable[offsetTable[m] + (float16bits & 0x3ff)] + exponentTable[m]; - return floatView[0]; - } - - const MAX_SAFE_INTEGER = NativeNumber.MAX_SAFE_INTEGER; - function ToIntegerOrInfinity(target) { - if (typeof target === "bigint") { - throw NativeTypeError(CANNOT_CONVERT_A_BIGINT_VALUE_TO_A_NUMBER); - } - const number = NativeNumber(target); - if (NumberIsNaN(number) || number === 0) { - return 0; - } - return MathTrunc(number); - } - function ToLength(target) { - const length = ToIntegerOrInfinity(target); - if (length < 0) { - return 0; - } - return length < MAX_SAFE_INTEGER - ? length - : MAX_SAFE_INTEGER; - } - function SpeciesConstructor(target, defaultConstructor) { - if (!isObject(target)) { - throw NativeTypeError(THIS_IS_NOT_AN_OBJECT); - } - const constructor = target.constructor; - if (constructor === undefined) { - return defaultConstructor; - } - if (!isObject(constructor)) { - throw NativeTypeError(THE_CONSTRUCTOR_PROPERTY_VALUE_IS_NOT_AN_OBJECT); - } - const species = constructor[SymbolSpecies]; - if (species == null) { - return defaultConstructor; - } - return species; - } - function IsDetachedBuffer(buffer) { - if (isSharedArrayBuffer(buffer)) { - return false; - } - try { - ArrayBufferPrototypeSlice(buffer, 0, 0); - return false; - } catch (e) {} - return true; - } - function defaultCompare(x, y) { - const isXNaN = NumberIsNaN(x); - const isYNaN = NumberIsNaN(y); - if (isXNaN && isYNaN) { - return 0; - } - if (isXNaN) { - return 1; - } - if (isYNaN) { - return -1; - } - if (x < y) { - return -1; - } - if (x > y) { - return 1; - } - if (x === 0 && y === 0) { - const isXPlusZero = ObjectIs(x, 0); - const isYPlusZero = ObjectIs(y, 0); - if (!isXPlusZero && isYPlusZero) { - return -1; - } - if (isXPlusZero && !isYPlusZero) { - return 1; - } - } - return 0; - } - - const BYTES_PER_ELEMENT = 2; - const float16bitsArrays = new NativeWeakMap(); - function isFloat16Array(target) { - return WeakMapPrototypeHas(float16bitsArrays, target) || - (!ArrayBufferIsView(target) && hasFloat16ArrayBrand(target)); - } - function assertFloat16Array(target) { - if (!isFloat16Array(target)) { - throw NativeTypeError(THIS_IS_NOT_A_FLOAT16ARRAY_OBJECT); - } - } - function assertSpeciesTypedArray(target, count) { - const isTargetFloat16Array = isFloat16Array(target); - const isTargetTypedArray = isNativeTypedArray(target); - if (!isTargetFloat16Array && !isTargetTypedArray) { - throw NativeTypeError(SPECIES_CONSTRUCTOR_DIDNT_RETURN_TYPEDARRAY_OBJECT); - } - if (typeof count === "number") { - let length; - if (isTargetFloat16Array) { - const float16bitsArray = getFloat16BitsArray(target); - length = TypedArrayPrototypeGetLength(float16bitsArray); - } else { - length = TypedArrayPrototypeGetLength(target); - } - if (length < count) { - throw NativeTypeError( - DERIVED_CONSTRUCTOR_CREATED_TYPEDARRAY_OBJECT_WHICH_WAS_TOO_SMALL_LENGTH - ); - } - } - if (isNativeBigIntTypedArray(target)) { - throw NativeTypeError(CANNOT_MIX_BIGINT_AND_OTHER_TYPES); - } - } - function getFloat16BitsArray(float16) { - const float16bitsArray = WeakMapPrototypeGet(float16bitsArrays, float16); - if (float16bitsArray !== undefined) { - const buffer = TypedArrayPrototypeGetBuffer(float16bitsArray); - if (IsDetachedBuffer(buffer)) { - throw NativeTypeError(ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER); - } - return float16bitsArray; - } - const buffer = (float16).buffer; - if (IsDetachedBuffer(buffer)) { - throw NativeTypeError(ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER); - } - const cloned = ReflectConstruct(Float16Array, [ - buffer, - (float16).byteOffset, - (float16).length, - ], float16.constructor); - return WeakMapPrototypeGet(float16bitsArrays, cloned); - } - function copyToArray(float16bitsArray) { - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const array = []; - for (let i = 0; i < length; ++i) { - array[i] = convertToNumber(float16bitsArray[i]); - } - return array; - } - const TypedArrayPrototypeGetters = new NativeWeakSet(); - for (const key of ReflectOwnKeys(TypedArrayPrototype)) { - if (key === SymbolToStringTag) { - continue; - } - const descriptor = ReflectGetOwnPropertyDescriptor(TypedArrayPrototype, key); - if (ObjectHasOwn(descriptor, "get") && typeof descriptor.get === "function") { - WeakSetPrototypeAdd(TypedArrayPrototypeGetters, descriptor.get); - } - } - const handler = ObjectFreeze( ({ - get(target, key, receiver) { - if (isCanonicalIntegerIndexString(key) && ObjectHasOwn(target, key)) { - return convertToNumber(ReflectGet(target, key)); - } - if (WeakSetPrototypeHas(TypedArrayPrototypeGetters, ObjectPrototype__lookupGetter__(target, key))) { - return ReflectGet(target, key); - } - return ReflectGet(target, key, receiver); - }, - set(target, key, value, receiver) { - if (isCanonicalIntegerIndexString(key) && ObjectHasOwn(target, key)) { - return ReflectSet(target, key, roundToFloat16Bits(value)); - } - return ReflectSet(target, key, value, receiver); - }, - getOwnPropertyDescriptor(target, key) { - if (isCanonicalIntegerIndexString(key) && ObjectHasOwn(target, key)) { - const descriptor = ReflectGetOwnPropertyDescriptor(target, key); - descriptor.value = convertToNumber(descriptor.value); - return descriptor; - } - return ReflectGetOwnPropertyDescriptor(target, key); - }, - defineProperty(target, key, descriptor) { - if ( - isCanonicalIntegerIndexString(key) && - ObjectHasOwn(target, key) && - ObjectHasOwn(descriptor, "value") - ) { - descriptor.value = roundToFloat16Bits(descriptor.value); - return ReflectDefineProperty(target, key, descriptor); - } - return ReflectDefineProperty(target, key, descriptor); - }, - })); - class Float16Array { - constructor(input, _byteOffset, _length) { - let float16bitsArray; - if (isFloat16Array(input)) { - float16bitsArray = ReflectConstruct(NativeUint16Array, [getFloat16BitsArray(input)], new.target); - } else if (isObject(input) && !isArrayBuffer(input)) { - let list; - let length; - if (isNativeTypedArray(input)) { - list = input; - length = TypedArrayPrototypeGetLength(input); - const buffer = TypedArrayPrototypeGetBuffer(input); - const BufferConstructor = !isSharedArrayBuffer(buffer) - ? (SpeciesConstructor( - buffer, - NativeArrayBuffer - )) - : NativeArrayBuffer; - if (IsDetachedBuffer(buffer)) { - throw NativeTypeError(ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER); - } - if (isNativeBigIntTypedArray(input)) { - throw NativeTypeError(CANNOT_MIX_BIGINT_AND_OTHER_TYPES); - } - const data = new BufferConstructor( - length * BYTES_PER_ELEMENT - ); - float16bitsArray = ReflectConstruct(NativeUint16Array, [data], new.target); - } else { - const iterator = input[SymbolIterator]; - if (iterator != null && typeof iterator !== "function") { - throw NativeTypeError(ITERATOR_PROPERTY_IS_NOT_CALLABLE); - } - if (iterator != null) { - if (isOrdinaryArray(input)) { - list = input; - length = input.length; - } else { - list = [... (input)]; - length = list.length; - } - } else { - list = (input); - length = ToLength(list.length); - } - float16bitsArray = ReflectConstruct(NativeUint16Array, [length], new.target); - } - for (let i = 0; i < length; ++i) { - float16bitsArray[i] = roundToFloat16Bits(list[i]); - } - } else { - float16bitsArray = ReflectConstruct(NativeUint16Array, arguments, new.target); - } - const proxy = (new NativeProxy(float16bitsArray, handler)); - WeakMapPrototypeSet(float16bitsArrays, proxy, float16bitsArray); - return proxy; - } - static from(src, ...opts) { - const Constructor = this; - if (!ReflectHas(Constructor, brand)) { - throw NativeTypeError( - THIS_CONSTRUCTOR_IS_NOT_A_SUBCLASS_OF_FLOAT16ARRAY - ); - } - if (Constructor === Float16Array) { - if (isFloat16Array(src) && opts.length === 0) { - const float16bitsArray = getFloat16BitsArray(src); - const uint16 = new NativeUint16Array( - TypedArrayPrototypeGetBuffer(float16bitsArray), - TypedArrayPrototypeGetByteOffset(float16bitsArray), - TypedArrayPrototypeGetLength(float16bitsArray) - ); - return new Float16Array( - TypedArrayPrototypeGetBuffer(TypedArrayPrototypeSlice(uint16)) - ); - } - if (opts.length === 0) { - return new Float16Array( - TypedArrayPrototypeGetBuffer( - Uint16ArrayFrom(src, roundToFloat16Bits) - ) - ); - } - const mapFunc = opts[0]; - const thisArg = opts[1]; - return new Float16Array( - TypedArrayPrototypeGetBuffer( - Uint16ArrayFrom(src, function (val, ...args) { - return roundToFloat16Bits( - ReflectApply(mapFunc, this, [val, ...safeIfNeeded(args)]) - ); - }, thisArg) - ) - ); - } - let list; - let length; - const iterator = src[SymbolIterator]; - if (iterator != null && typeof iterator !== "function") { - throw NativeTypeError(ITERATOR_PROPERTY_IS_NOT_CALLABLE); - } - if (iterator != null) { - if (isOrdinaryArray(src)) { - list = src; - length = src.length; - } else if (isOrdinaryNativeTypedArray(src)) { - list = src; - length = TypedArrayPrototypeGetLength(src); - } else { - list = [...src]; - length = list.length; - } - } else { - if (src == null) { - throw NativeTypeError( - CANNOT_CONVERT_UNDEFINED_OR_NULL_TO_OBJECT - ); - } - list = NativeObject(src); - length = ToLength(list.length); - } - const array = new Constructor(length); - if (opts.length === 0) { - for (let i = 0; i < length; ++i) { - array[i] = (list[i]); - } - } else { - const mapFunc = opts[0]; - const thisArg = opts[1]; - for (let i = 0; i < length; ++i) { - array[i] = ReflectApply(mapFunc, thisArg, [list[i], i]); - } - } - return array; - } - static of(...items) { - const Constructor = this; - if (!ReflectHas(Constructor, brand)) { - throw NativeTypeError( - THIS_CONSTRUCTOR_IS_NOT_A_SUBCLASS_OF_FLOAT16ARRAY - ); - } - const length = items.length; - if (Constructor === Float16Array) { - const proxy = new Float16Array(length); - const float16bitsArray = getFloat16BitsArray(proxy); - for (let i = 0; i < length; ++i) { - float16bitsArray[i] = roundToFloat16Bits(items[i]); - } - return proxy; - } - const array = new Constructor(length); - for (let i = 0; i < length; ++i) { - array[i] = items[i]; - } - return array; - } - keys() { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - return TypedArrayPrototypeKeys(float16bitsArray); - } - values() { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - return wrap((function* () { - for (const val of TypedArrayPrototypeValues(float16bitsArray)) { - yield convertToNumber(val); - } - })()); - } - entries() { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - return wrap((function* () { - for (const [i, val] of TypedArrayPrototypeEntries(float16bitsArray)) { - yield ([i, convertToNumber(val)]); - } - })()); - } - at(index) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const relativeIndex = ToIntegerOrInfinity(index); - const k = relativeIndex >= 0 ? relativeIndex : length + relativeIndex; - if (k < 0 || k >= length) { - return; - } - return convertToNumber(float16bitsArray[k]); - } - map(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - const Constructor = SpeciesConstructor(float16bitsArray, Float16Array); - if (Constructor === Float16Array) { - const proxy = new Float16Array(length); - const array = getFloat16BitsArray(proxy); - for (let i = 0; i < length; ++i) { - const val = convertToNumber(float16bitsArray[i]); - array[i] = roundToFloat16Bits( - ReflectApply(callback, thisArg, [val, i, this]) - ); - } - return proxy; - } - const array = new Constructor(length); - assertSpeciesTypedArray(array, length); - for (let i = 0; i < length; ++i) { - const val = convertToNumber(float16bitsArray[i]); - array[i] = ReflectApply(callback, thisArg, [val, i, this]); - } - return (array); - } - filter(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - const kept = []; - for (let i = 0; i < length; ++i) { - const val = convertToNumber(float16bitsArray[i]); - if (ReflectApply(callback, thisArg, [val, i, this])) { - ArrayPrototypePush(kept, val); - } - } - const Constructor = SpeciesConstructor(float16bitsArray, Float16Array); - const array = new Constructor(kept); - assertSpeciesTypedArray(array); - return (array); - } - reduce(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - if (length === 0 && opts.length === 0) { - throw NativeTypeError(REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE); - } - let accumulator, start; - if (opts.length === 0) { - accumulator = convertToNumber(float16bitsArray[0]); - start = 1; - } else { - accumulator = opts[0]; - start = 0; - } - for (let i = start; i < length; ++i) { - accumulator = callback( - accumulator, - convertToNumber(float16bitsArray[i]), - i, - this - ); - } - return accumulator; - } - reduceRight(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - if (length === 0 && opts.length === 0) { - throw NativeTypeError(REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE); - } - let accumulator, start; - if (opts.length === 0) { - accumulator = convertToNumber(float16bitsArray[length - 1]); - start = length - 2; - } else { - accumulator = opts[0]; - start = length - 1; - } - for (let i = start; i >= 0; --i) { - accumulator = callback( - accumulator, - convertToNumber(float16bitsArray[i]), - i, - this - ); - } - return accumulator; - } - forEach(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = 0; i < length; ++i) { - ReflectApply(callback, thisArg, [ - convertToNumber(float16bitsArray[i]), - i, - this, - ]); - } - } - find(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = 0; i < length; ++i) { - const value = convertToNumber(float16bitsArray[i]); - if (ReflectApply(callback, thisArg, [value, i, this])) { - return value; - } - } - } - findIndex(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = 0; i < length; ++i) { - const value = convertToNumber(float16bitsArray[i]); - if (ReflectApply(callback, thisArg, [value, i, this])) { - return i; - } - } - return -1; - } - findLast(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = length - 1; i >= 0; --i) { - const value = convertToNumber(float16bitsArray[i]); - if (ReflectApply(callback, thisArg, [value, i, this])) { - return value; - } - } - } - findLastIndex(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = length - 1; i >= 0; --i) { - const value = convertToNumber(float16bitsArray[i]); - if (ReflectApply(callback, thisArg, [value, i, this])) { - return i; - } - } - return -1; - } - every(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = 0; i < length; ++i) { - if ( - !ReflectApply(callback, thisArg, [ - convertToNumber(float16bitsArray[i]), - i, - this, - ]) - ) { - return false; - } - } - return true; - } - some(callback, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const thisArg = opts[0]; - for (let i = 0; i < length; ++i) { - if ( - ReflectApply(callback, thisArg, [ - convertToNumber(float16bitsArray[i]), - i, - this, - ]) - ) { - return true; - } - } - return false; - } - set(input, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const targetOffset = ToIntegerOrInfinity(opts[0]); - if (targetOffset < 0) { - throw NativeRangeError(OFFSET_IS_OUT_OF_BOUNDS); - } - if (input == null) { - throw NativeTypeError( - CANNOT_CONVERT_UNDEFINED_OR_NULL_TO_OBJECT - ); - } - if (isNativeBigIntTypedArray(input)) { - throw NativeTypeError( - CANNOT_MIX_BIGINT_AND_OTHER_TYPES - ); - } - if (isFloat16Array(input)) { - return TypedArrayPrototypeSet( - getFloat16BitsArray(this), - getFloat16BitsArray(input), - targetOffset - ); - } - if (isNativeTypedArray(input)) { - const buffer = TypedArrayPrototypeGetBuffer(input); - if (IsDetachedBuffer(buffer)) { - throw NativeTypeError(ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER); - } - } - const targetLength = TypedArrayPrototypeGetLength(float16bitsArray); - const src = NativeObject(input); - const srcLength = ToLength(src.length); - if (targetOffset === Infinity || srcLength + targetOffset > targetLength) { - throw NativeRangeError(OFFSET_IS_OUT_OF_BOUNDS); - } - for (let i = 0; i < srcLength; ++i) { - float16bitsArray[i + targetOffset] = roundToFloat16Bits(src[i]); - } - } - reverse() { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - TypedArrayPrototypeReverse(float16bitsArray); - return this; - } - fill(value, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - TypedArrayPrototypeFill( - float16bitsArray, - roundToFloat16Bits(value), - ...safeIfNeeded(opts) - ); - return this; - } - copyWithin(target, start, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - TypedArrayPrototypeCopyWithin(float16bitsArray, target, start, ...safeIfNeeded(opts)); - return this; - } - sort(compareFn) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const sortCompare = compareFn !== undefined ? compareFn : defaultCompare; - TypedArrayPrototypeSort(float16bitsArray, (x, y) => { - return sortCompare(convertToNumber(x), convertToNumber(y)); - }); - return this; - } - slice(start, end) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const Constructor = SpeciesConstructor(float16bitsArray, Float16Array); - if (Constructor === Float16Array) { - const uint16 = new NativeUint16Array( - TypedArrayPrototypeGetBuffer(float16bitsArray), - TypedArrayPrototypeGetByteOffset(float16bitsArray), - TypedArrayPrototypeGetLength(float16bitsArray) - ); - return new Float16Array( - TypedArrayPrototypeGetBuffer( - TypedArrayPrototypeSlice(uint16, start, end) - ) - ); - } - const length = TypedArrayPrototypeGetLength(float16bitsArray); - const relativeStart = ToIntegerOrInfinity(start); - const relativeEnd = end === undefined ? length : ToIntegerOrInfinity(end); - let k; - if (relativeStart === -Infinity) { - k = 0; - } else if (relativeStart < 0) { - k = length + relativeStart > 0 ? length + relativeStart : 0; - } else { - k = length < relativeStart ? length : relativeStart; - } - let final; - if (relativeEnd === -Infinity) { - final = 0; - } else if (relativeEnd < 0) { - final = length + relativeEnd > 0 ? length + relativeEnd : 0; - } else { - final = length < relativeEnd ? length : relativeEnd; - } - const count = final - k > 0 ? final - k : 0; - const array = new Constructor(count); - assertSpeciesTypedArray(array, count); - if (count === 0) { - return array; - } - const buffer = TypedArrayPrototypeGetBuffer(float16bitsArray); - if (IsDetachedBuffer(buffer)) { - throw NativeTypeError(ATTEMPTING_TO_ACCESS_DETACHED_ARRAYBUFFER); - } - let n = 0; - while (k < final) { - array[n] = convertToNumber(float16bitsArray[k]); - ++k; - ++n; - } - return (array); - } - subarray(begin, end) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const Constructor = SpeciesConstructor(float16bitsArray, Float16Array); - const uint16 = new NativeUint16Array( - TypedArrayPrototypeGetBuffer(float16bitsArray), - TypedArrayPrototypeGetByteOffset(float16bitsArray), - TypedArrayPrototypeGetLength(float16bitsArray) - ); - const uint16Subarray = TypedArrayPrototypeSubarray(uint16, begin, end); - const array = new Constructor( - TypedArrayPrototypeGetBuffer(uint16Subarray), - TypedArrayPrototypeGetByteOffset(uint16Subarray), - TypedArrayPrototypeGetLength(uint16Subarray) - ); - assertSpeciesTypedArray(array); - return (array); - } - indexOf(element, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - let from = ToIntegerOrInfinity(opts[0]); - if (from === Infinity) { - return -1; - } - if (from < 0) { - from += length; - if (from < 0) { - from = 0; - } - } - for (let i = from; i < length; ++i) { - if ( - ObjectHasOwn(float16bitsArray, i) && - convertToNumber(float16bitsArray[i]) === element - ) { - return i; - } - } - return -1; - } - lastIndexOf(element, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - let from = opts.length >= 1 ? ToIntegerOrInfinity(opts[0]) : length - 1; - if (from === -Infinity) { - return -1; - } - if (from >= 0) { - from = from < length - 1 ? from : length - 1; - } else { - from += length; - } - for (let i = from; i >= 0; --i) { - if ( - ObjectHasOwn(float16bitsArray, i) && - convertToNumber(float16bitsArray[i]) === element - ) { - return i; - } - } - return -1; - } - includes(element, ...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const length = TypedArrayPrototypeGetLength(float16bitsArray); - let from = ToIntegerOrInfinity(opts[0]); - if (from === Infinity) { - return false; - } - if (from < 0) { - from += length; - if (from < 0) { - from = 0; - } - } - const isNaN = NumberIsNaN(element); - for (let i = from; i < length; ++i) { - const value = convertToNumber(float16bitsArray[i]); - if (isNaN && NumberIsNaN(value)) { - return true; - } - if (value === element) { - return true; - } - } - return false; - } - join(separator) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const array = copyToArray(float16bitsArray); - return ArrayPrototypeJoin(array, separator); - } - toLocaleString(...opts) { - assertFloat16Array(this); - const float16bitsArray = getFloat16BitsArray(this); - const array = copyToArray(float16bitsArray); - return ArrayPrototypeToLocaleString(array, ...safeIfNeeded(opts)); - } - get [SymbolToStringTag]() { - if (isFloat16Array(this)) { - return ("Float16Array"); - } - } - } - ObjectDefineProperty(Float16Array, "BYTES_PER_ELEMENT", { - value: BYTES_PER_ELEMENT, - }); - ObjectDefineProperty(Float16Array, brand, {}); - ReflectSetPrototypeOf(Float16Array, TypedArray); - const Float16ArrayPrototype = Float16Array.prototype; - ObjectDefineProperty(Float16ArrayPrototype, "BYTES_PER_ELEMENT", { - value: BYTES_PER_ELEMENT, - }); - ObjectDefineProperty(Float16ArrayPrototype, SymbolIterator, { - value: Float16ArrayPrototype.values, - writable: true, - configurable: true, - }); - ReflectSetPrototypeOf(Float16ArrayPrototype, TypedArrayPrototype); - - function isTypedArray(target) { - return isNativeTypedArray(target) || isFloat16Array(target); - } - - function getFloat16(dataView, byteOffset, ...opts) { - return convertToNumber( - DataViewPrototypeGetUint16(dataView, byteOffset, ...safeIfNeeded(opts)) - ); - } - function setFloat16(dataView, byteOffset, value, ...opts) { - return DataViewPrototypeSetUint16( - dataView, - byteOffset, - roundToFloat16Bits(value), - ...safeIfNeeded(opts) - ); - } - - function hfround(num) { - if (typeof num === "bigint") { - throw NativeTypeError(CANNOT_CONVERT_A_BIGINT_VALUE_TO_A_NUMBER); - } - num = NativeNumber(num); - if (!NumberIsFinite(num) || num === 0) { - return num; - } - const x16 = roundToFloat16Bits(num); - return convertToNumber(x16); - } - - exports.Float16Array = Float16Array; - exports.getFloat16 = getFloat16; - exports.hfround = hfround; - exports.isFloat16Array = isFloat16Array; - exports.isTypedArray = isTypedArray; - exports.setFloat16 = setFloat16; - - Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); - - return exports; - -})({}); diff --git a/v3/js/libs/jquery-3.3.1.min.js b/v3/js/libs/jquery-3.3.1.min.js deleted file mode 100644 index 4d9b3a2..0000000 --- a/v3/js/libs/jquery-3.3.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" - - - - - - - - - - - - - - - - - - - - - - diff --git a/v3/test/util/colorLUT.json b/v3/test/util/colorLUT.json deleted file mode 100644 index 6da374a..0000000 --- a/v3/test/util/colorLUT.json +++ /dev/null @@ -1 +0,0 @@ -{"0": "rgb(0,0,0)", "1": "rgb(255,255,255)", "2": "rgb(205,62,78)"} \ No newline at end of file diff --git a/v3/test/util/labels.json b/v3/test/util/labels.json deleted file mode 100644 index 4885a94..0000000 --- a/v3/test/util/labels.json +++ /dev/null @@ -1 +0,0 @@ -{"0": "background", "1": "White Matter", "2": "Grey Matter"}